Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 37b11709 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "power: qpnp-fg-gen3: add DMA support for accessing FG SRAM"

parents c1f033ee 2d62e9e2
Loading
Loading
Loading
Loading
+21 −1
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@
#define MAX_LINE_LENGTH			(ADDR_LEN + (ITEMS_PER_LINE *	\
					CHARS_PER_ITEM) + 1)		\

#define NUM_PARTITIONS			3
#define FG_SRAM_ADDRESS_MAX		255
#define FG_SRAM_LEN			504
#define PROFILE_LEN			224
@@ -192,6 +193,18 @@ struct fg_sram_param {
		int val);
};

struct fg_dma_address {
	/* Starting word address of the partition */
	u16 partition_start;
	/* Last word address of the partition */
	u16 partition_end;
	/*
	 * Byte offset in the FG_DMA peripheral that maps to the partition_start
	 * in SRAM
	 */
	u16 spmi_addr_base;
};

enum fg_alg_flag_id {
	ALG_FLAG_SOC_LT_OTG_MIN = 0,
	ALG_FLAG_SOC_LT_RECHARGE,
@@ -360,12 +373,12 @@ struct fg_chip {
	struct power_supply	*parallel_psy;
	struct iio_channel	*batt_id_chan;
	struct iio_channel	*die_temp_chan;
	struct fg_memif		*sram;
	struct fg_irq_info	*irqs;
	struct votable		*awake_votable;
	struct votable		*delta_bsoc_irq_en_votable;
	struct votable		*batt_miss_irq_en_votable;
	struct fg_sram_param	*sp;
	struct fg_dma_address	*addr_map;
	struct fg_alg_flag	*alg_flags;
	int			*debug_mask;
	char			batt_profile[PROFILE_LEN];
@@ -409,8 +422,10 @@ struct fg_chip {
	bool			esr_flt_cold_temp_en;
	bool			slope_limit_en;
	bool			use_ima_single_mode;
	bool			use_dma;
	struct completion	soc_update;
	struct completion	soc_ready;
	struct completion	mem_grant;
	struct delayed_work	profile_load_work;
	struct work_struct	status_change_work;
	struct work_struct	cycle_count_work;
@@ -459,10 +474,15 @@ extern int fg_interleaved_mem_read(struct fg_chip *chip, u16 address,
			u8 offset, u8 *val, int len);
extern int fg_interleaved_mem_write(struct fg_chip *chip, u16 address,
			u8 offset, u8 *val, int len, bool atomic_access);
extern int fg_direct_mem_read(struct fg_chip *chip, u16 address,
			u8 offset, u8 *val, int len);
extern int fg_direct_mem_write(struct fg_chip *chip, u16 address,
			u8 offset, u8 *val, int len, bool atomic_access);
extern int fg_read(struct fg_chip *chip, int addr, u8 *val, int len);
extern int fg_write(struct fg_chip *chip, int addr, u8 *val, int len);
extern int fg_masked_write(struct fg_chip *chip, int addr, u8 mask, u8 val);
extern int fg_ima_init(struct fg_chip *chip);
extern int fg_dma_init(struct fg_chip *chip);
extern int fg_clear_ima_errors_if_any(struct fg_chip *chip, bool check_hw_sts);
extern int fg_clear_dma_errors_if_any(struct fg_chip *chip);
extern int fg_debugfs_create(struct fg_chip *chip);
+307 −0
Original line number Diff line number Diff line
@@ -746,6 +746,257 @@ int fg_interleaved_mem_write(struct fg_chip *chip, u16 address, u8 offset,
	return rc;
}

#define MEM_GRANT_WAIT_MS	200
static int fg_direct_mem_request(struct fg_chip *chip, bool request)
{
	int rc, ret;
	u8 val, mask;
	bool tried_again = false;

	if (request)
		reinit_completion(&chip->mem_grant);

	mask = MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT;
	val = request ? MEM_ACCESS_REQ_BIT : 0;
	rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip), mask, val);
	if (rc < 0) {
		pr_err("failed to configure mem_if_mem_intf_cfg rc=%d\n", rc);
		return rc;
	}

	mask = MEM_ARB_LO_LATENCY_EN_BIT | MEM_ARB_REQ_BIT;
	val = request ? mask : 0;
	rc = fg_masked_write(chip, MEM_IF_MEM_ARB_CFG(chip), mask, val);
	if (rc < 0) {
		pr_err("failed to configure mem_if_mem_arb_cfg rc:%d\n", rc);
		return rc;
	}

	if (request)
		pr_debug("requesting access\n");
	else
		pr_debug("releasing access\n");

	if (!request)
		return 0;

wait:
	ret = wait_for_completion_interruptible_timeout(
		&chip->mem_grant, msecs_to_jiffies(MEM_GRANT_WAIT_MS));
	/* If we were interrupted wait again one more time. */
	if (ret <= 0) {
		if ((ret == -ERESTARTSYS || ret == 0) && !tried_again) {
			pr_debug("trying again, ret=%d\n", ret);
			tried_again = true;
			goto wait;
		} else {
			pr_err("wait for mem_grant timed out ret=%d\n",
				ret);
		}
	}

	if (ret <= 0) {
		val = 0;
		mask = MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT;
		rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip), mask,
					val);
		if (rc < 0) {
			pr_err("failed to configure mem_if_mem_intf_cfg rc=%d\n",
				rc);
			return rc;
		}

		mask = MEM_ARB_LO_LATENCY_EN_BIT | MEM_ARB_REQ_BIT;
		rc = fg_masked_write(chip, MEM_IF_MEM_ARB_CFG(chip), mask,
					val);
		if (rc < 0) {
			pr_err("failed to configure mem_if_mem_arb_cfg rc:%d\n",
				rc);
			return rc;
		}

		return -ETIMEDOUT;
	}

	return rc;
}

static int fg_get_dma_address(struct fg_chip *chip, u16 sram_addr, u8 offset,
				u16 *addr)
{
	int i;
	u16 start_sram_addr, end_sram_addr;

	for (i = 0; i < NUM_PARTITIONS; i++) {
		start_sram_addr = chip->addr_map[i].partition_start;
		end_sram_addr = chip->addr_map[i].partition_end;
		if (sram_addr >= start_sram_addr &&
			sram_addr <= end_sram_addr) {
			*addr = chip->addr_map[i].spmi_addr_base + offset +
					(sram_addr - start_sram_addr) *
						BYTES_PER_SRAM_WORD;
			return 0;
		}
	}

	pr_err("Couldn't find address for %d from address map\n", sram_addr);
	return -ENXIO;
}

static int fg_get_partition_count(struct fg_chip *chip, u16 sram_addr, int len,
				int *count)
{
	int i, num = 0;
	u16 end_addr, last_addr = 0;

	end_addr = sram_addr + len / BYTES_PER_SRAM_WORD;
	if (!(len % BYTES_PER_SRAM_WORD))
		end_addr -= 1;

	if (sram_addr == end_addr) {
		*count = 1;
		return 0;
	}

	for (i = 0; i < NUM_PARTITIONS; i++) {
		pr_debug("address: %d last_addr: %d\n", sram_addr, last_addr);
		if (sram_addr >= chip->addr_map[i].partition_start
			&& sram_addr <= chip->addr_map[i].partition_end
			&& last_addr < end_addr) {
			num++;
			last_addr = chip->addr_map[i].partition_end;
			sram_addr = chip->addr_map[i+1].partition_start;
		}
	}

	if (num > 0) {
		*count = num;
		return 0;
	}

	pr_err("Couldn't find number of partitions for address %d\n",
		sram_addr);
	return -ENXIO;
}

static int fg_get_partition_avail_bytes(struct fg_chip *chip, u16 sram_addr,
					int len, int *rem_len)
{
	int i, part_len = 0, temp;
	u16 end_addr;

	for (i = 0; i < NUM_PARTITIONS; i++) {
		if (sram_addr >= chip->addr_map[i].partition_start
			&& sram_addr <= chip->addr_map[i].partition_end) {
			part_len = (chip->addr_map[i].partition_end -
					chip->addr_map[i].partition_start + 1);
			part_len *= BYTES_PER_SRAM_WORD;
			end_addr = chip->addr_map[i].partition_end;
			break;
		}
	}

	if (part_len <= 0) {
		pr_err("Bad address? total_len=%d\n", part_len);
		return -ENXIO;
	}

	temp = (end_addr - sram_addr + 1) * BYTES_PER_SRAM_WORD;
	if (temp > part_len || !temp) {
		pr_err("Bad length=%d\n", temp);
		return -ENXIO;
	}

	*rem_len = temp;
	pr_debug("address %d len %d rem_len %d\n", sram_addr, len, *rem_len);
	return 0;
}

static int __fg_direct_mem_rw(struct fg_chip *chip, u16 sram_addr, u8 offset,
				u8 *val, int len, bool access)
{
	int rc, ret, num_partitions, num_bytes = 0;
	u16 addr;
	u8 *ptr = val;
	char *temp_str;

	if (offset > 3) {
		pr_err("offset too large %d\n", offset);
		return -EINVAL;
	}

	rc = fg_get_partition_count(chip, sram_addr, len, &num_partitions);
	if (rc < 0)
		return rc;

	pr_debug("number of partitions: %d\n", num_partitions);

	rc = fg_direct_mem_request(chip, true);
	if (rc < 0) {
		pr_err("Error in requesting direct_mem access rc=%d\n", rc);
		return rc;
	}

	while (num_partitions-- && len) {
		rc = fg_get_dma_address(chip, sram_addr, offset, &addr);
		if (rc < 0) {
			pr_err("Incorrect address %d/offset %d\n", sram_addr,
				offset);
			break;
		}

		rc = fg_get_partition_avail_bytes(chip, sram_addr + offset, len,
						&num_bytes);
		if (rc < 0)
			break;

		if (num_bytes > len)
			num_bytes = len;

		pr_debug("reading from address: [%d %d] dma_address = %x\n",
			sram_addr, offset, addr);

		if (access == FG_READ) {
			rc = fg_read(chip, addr, ptr, num_bytes);
			temp_str = "read";
		} else {
			rc = fg_write(chip, addr, ptr, num_bytes);
			temp_str = "write";
		}

		if (rc < 0) {
			pr_err("Error in %sing address %d rc=%d\n", temp_str,
				sram_addr, rc);
			break;
		}

		ptr += num_bytes;
		len -= num_bytes;
		sram_addr += (num_bytes / BYTES_PER_SRAM_WORD);
		offset = 0;
	}

	ret = fg_direct_mem_request(chip, false);
	if (ret < 0) {
		pr_err("Error in releasing direct_mem access rc=%d\n", rc);
		return ret;
	}

	return rc;
}

int fg_direct_mem_read(struct fg_chip *chip, u16 sram_addr, u8 offset,
				u8 *val, int len)
{
	return __fg_direct_mem_rw(chip, sram_addr, offset, val, len, FG_READ);
}

int fg_direct_mem_write(struct fg_chip *chip, u16 sram_addr, u8 offset,
				u8 *val, int len, bool atomic_access)
{
	return __fg_direct_mem_rw(chip, sram_addr, offset, val, len, FG_WRITE);
}

int fg_ima_init(struct fg_chip *chip)
{
	int rc;
@@ -778,3 +1029,59 @@ int fg_ima_init(struct fg_chip *chip)

	return 0;
}

/*
 * This SRAM partition to DMA address partition mapping remains identical for
 * PMICs that use GEN3 FG.
 */
static struct fg_dma_address fg_gen3_addr_map[NUM_PARTITIONS] = {
	/* system partition */
	{
		.partition_start = 0,
		.partition_end = 23,
		.spmi_addr_base = FG_DMA0_BASE + SRAM_ADDR_OFFSET,
	},
	/* battery profile partition */
	{
		.partition_start = 24,
		.partition_end = 79,
		.spmi_addr_base = FG_DMA1_BASE + SRAM_ADDR_OFFSET,
	},
	/* scratch pad partition */
	{
		.partition_start = 80,
		.partition_end =  125,
		.spmi_addr_base = FG_DMA2_BASE + SRAM_ADDR_OFFSET,
	},
};
int fg_dma_init(struct fg_chip *chip)
{
	int rc;

	chip->addr_map = fg_gen3_addr_map;

	/* Clear DMA errors if any before clearing IMA errors */
	rc = fg_clear_dma_errors_if_any(chip);
	if (rc < 0) {
		pr_err("Error in checking DMA errors rc:%d\n", rc);
		return rc;
	}

	/* Configure the DMA peripheral addressing to partition */
	rc = fg_masked_write(chip, MEM_IF_DMA_CTL(chip), ADDR_KIND_BIT,
				ADDR_KIND_BIT);
	if (rc < 0) {
		pr_err("failed to configure DMA_CTL rc:%d\n", rc);
		return rc;
	}

	/* Release the DMA initially so that request can happen */
	rc = fg_direct_mem_request(chip, false);
	if (rc < 0) {
		pr_err("Error in releasing direct_mem access rc=%d\n",
			rc);
		return rc;
	}

	return 0;
}
+15 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@
#define BATT_SOC_LOW_PWR_STS(chip)		(chip->batt_soc_base + 0x56)

/* BATT_SOC_INT_RT_STS */
#define SOC_READY_BIT				BIT(1)
#define MSOC_EMPTY_BIT				BIT(5)

/* BATT_SOC_EN_CTL */
@@ -266,6 +267,7 @@

/* FG_MEM_IF register and bit definitions */
#define MEM_IF_INT_RT_STS(chip)			((chip->mem_if_base) + 0x10)
#define MEM_IF_MEM_ARB_CFG(chip)		((chip->mem_if_base) + 0x40)
#define MEM_IF_MEM_INTF_CFG(chip)		((chip->mem_if_base) + 0x50)
#define MEM_IF_IMA_CTL(chip)			((chip->mem_if_base) + 0x51)
#define MEM_IF_IMA_CFG(chip)			((chip->mem_if_base) + 0x52)
@@ -286,6 +288,11 @@

/* MEM_IF_INT_RT_STS */
#define MEM_XCP_BIT				BIT(1)
#define MEM_GNT_BIT				BIT(2)

/* MEM_IF_MEM_ARB_CFG */
#define MEM_ARB_LO_LATENCY_EN_BIT		BIT(1)
#define MEM_ARB_REQ_BIT				BIT(0)

/* MEM_IF_MEM_INTF_CFG */
#define MEM_ACCESS_REQ_BIT			BIT(7)
@@ -325,5 +332,13 @@
#define DMA_READ_ERROR_BIT			BIT(2)

/* MEM_IF_DMA_CTL */
#define ADDR_KIND_BIT				BIT(1)
#define DMA_CLEAR_LOG_BIT			BIT(0)

/* FG_DMAx */
#define FG_DMA0_BASE				0x4800
#define FG_DMA1_BASE				0x4900
#define FG_DMA2_BASE				0x4A00
#define FG_DMA3_BASE				0x4B00
#define SRAM_ADDR_OFFSET			0x20
#endif
+14 −5
Original line number Diff line number Diff line
@@ -255,8 +255,6 @@ int fg_sram_write(struct fg_chip *chip, u16 address, u8 offset,
		reinit_completion(&chip->soc_update);
		enable_irq(chip->irqs[SOC_UPDATE_IRQ].irq);
		atomic_access = true;
	} else {
		flags = FG_IMA_DEFAULT;
	}
wait:
	/*
@@ -282,11 +280,17 @@ int fg_sram_write(struct fg_chip *chip, u16 address, u8 offset,
		}
	}

	if (chip->use_dma)
		rc = fg_direct_mem_write(chip, address, offset, val, len,
				false);
	else
		rc = fg_interleaved_mem_write(chip, address, offset, val, len,
				atomic_access);

	if (rc < 0)
		pr_err("Error in writing SRAM address 0x%x[%d], rc=%d\n",
			address, offset, rc);

out:
	if (atomic_access)
		disable_irq_nosync(chip->irqs[SOC_UPDATE_IRQ].irq);
@@ -313,9 +317,14 @@ int fg_sram_read(struct fg_chip *chip, u16 address, u8 offset,

	if (!(flags & FG_IMA_NO_WLOCK))
		vote(chip->awake_votable, SRAM_READ, true, 0);

	mutex_lock(&chip->sram_rw_lock);

	if (chip->use_dma)
		rc = fg_direct_mem_read(chip, address, offset, val, len);
	else
		rc = fg_interleaved_mem_read(chip, address, offset, val, len);

	if (rc < 0)
		pr_err("Error in reading SRAM address 0x%x[%d], rc=%d\n",
			address, offset, rc);
+47 −21
Original line number Diff line number Diff line
@@ -533,7 +533,7 @@ static int fg_get_sram_prop(struct fg_chip *chip, enum fg_sram_param_id id,
	rc = fg_sram_read(chip, chip->sp[id].addr_word, chip->sp[id].addr_byte,
		buf, chip->sp[id].len, FG_IMA_DEFAULT);
	if (rc < 0) {
		pr_err("Error reading address 0x%04x[%d] rc=%d\n",
		pr_err("Error reading address %d[%d] rc=%d\n",
			chip->sp[id].addr_word, chip->sp[id].addr_byte, rc);
		return rc;
	}
@@ -3503,6 +3503,9 @@ static int fg_hw_init(struct fg_chip *chip)

static int fg_memif_init(struct fg_chip *chip)
{
	if (chip->use_dma)
		return fg_dma_init(chip);

	return fg_ima_init(chip);
}

@@ -3542,6 +3545,26 @@ static int fg_adjust_timebase(struct fg_chip *chip)

/* INTERRUPT HANDLERS STAY HERE */

static irqreturn_t fg_dma_grant_irq_handler(int irq, void *data)
{
	struct fg_chip *chip = data;
	u8 status;
	int rc;

	rc = fg_read(chip, MEM_IF_INT_RT_STS(chip), &status, 1);
	if (rc < 0) {
		pr_err("failed to read addr=0x%04x, rc=%d\n",
			MEM_IF_INT_RT_STS(chip), rc);
		return IRQ_HANDLED;
	}

	fg_dbg(chip, FG_IRQ, "irq %d triggered, status:%d\n", irq, status);
	if (status & MEM_GNT_BIT)
		complete_all(&chip->mem_grant);

	return IRQ_HANDLED;
}

static irqreturn_t fg_mem_xcp_irq_handler(int irq, void *data)
{
	struct fg_chip *chip = data;
@@ -3822,7 +3845,8 @@ static struct fg_irq_info fg_irqs[FG_IRQ_MAX] = {
	/* MEM_IF irqs */
	[DMA_GRANT_IRQ] = {
		.name		= "dma-grant",
		.handler	= fg_dummy_irq_handler,
		.handler	= fg_dma_grant_irq_handler,
		.wakeable	= true,
	},
	[MEM_XCP_IRQ] = {
		.name		= "mem-xcp",
@@ -4046,6 +4070,7 @@ static int fg_parse_dt(struct fg_chip *chip)

	switch (chip->pmic_rev_id->pmic_subtype) {
	case PMI8998_SUBTYPE:
		chip->use_dma = true;
		if (chip->pmic_rev_id->rev4 < PMI8998_V2P0_REV4) {
			chip->sp = pmi8998_v1_sram_params;
			chip->alg_flags = pmi8998_v1_alg_flags;
@@ -4466,6 +4491,7 @@ static int fg_gen3_probe(struct platform_device *pdev)
	mutex_init(&chip->charge_full_lock);
	init_completion(&chip->soc_update);
	init_completion(&chip->soc_ready);
	init_completion(&chip->mem_grant);
	INIT_DELAYED_WORK(&chip->profile_load_work, profile_load_work);
	INIT_WORK(&chip->status_change_work, status_change_work);
	INIT_WORK(&chip->cycle_count_work, cycle_count_work);
@@ -4479,6 +4505,25 @@ static int fg_gen3_probe(struct platform_device *pdev)
		goto exit;
	}

	platform_set_drvdata(pdev, chip);

	rc = fg_register_interrupts(chip);
	if (rc < 0) {
		dev_err(chip->dev, "Error in registering interrupts, rc:%d\n",
			rc);
		goto exit;
	}

	/* Keep SOC_UPDATE irq disabled until we require it */
	if (fg_irqs[SOC_UPDATE_IRQ].irq)
		disable_irq_nosync(fg_irqs[SOC_UPDATE_IRQ].irq);

	/* Keep BSOC_DELTA_IRQ disabled until we require it */
	vote(chip->delta_bsoc_irq_en_votable, DELTA_BSOC_IRQ_VOTER, false, 0);

	/* Keep BATT_MISSING_IRQ disabled until we require it */
	vote(chip->batt_miss_irq_en_votable, BATT_MISS_IRQ_VOTER, false, 0);

	rc = fg_hw_init(chip);
	if (rc < 0) {
		dev_err(chip->dev, "Error in initializing FG hardware, rc:%d\n",
@@ -4486,8 +4531,6 @@ static int fg_gen3_probe(struct platform_device *pdev)
		goto exit;
	}

	platform_set_drvdata(pdev, chip);

	/* Register the power supply */
	fg_psy_cfg.drv_data = chip;
	fg_psy_cfg.of_node = NULL;
@@ -4508,23 +4551,6 @@ static int fg_gen3_probe(struct platform_device *pdev)
		goto exit;
	}

	rc = fg_register_interrupts(chip);
	if (rc < 0) {
		dev_err(chip->dev, "Error in registering interrupts, rc:%d\n",
			rc);
		goto exit;
	}

	/* Keep SOC_UPDATE_IRQ disabled until we require it */
	if (fg_irqs[SOC_UPDATE_IRQ].irq)
		disable_irq_nosync(fg_irqs[SOC_UPDATE_IRQ].irq);

	/* Keep BSOC_DELTA_IRQ disabled until we require it */
	vote(chip->delta_bsoc_irq_en_votable, DELTA_BSOC_IRQ_VOTER, false, 0);

	/* Keep BATT_MISSING_IRQ disabled until we require it */
	vote(chip->batt_miss_irq_en_votable, BATT_MISS_IRQ_VOTER, false, 0);

	rc = fg_debugfs_create(chip);
	if (rc < 0) {
		dev_err(chip->dev, "Error in creating debugfs entries, rc:%d\n",