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

Commit 2d62e9e2 authored by Subbaraman Narayanamurthy's avatar Subbaraman Narayanamurthy
Browse files

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



Currently FG SRAM is accessed by Interleaved Memory Access mode
(IMA). GEN3 FG has support for Direct Memory Access (DMA) to
access the SRAM. With DMA, SRAM can be directly accessed via the
SPMI peripheral registers once the configuration is done and DMA
is granted. This improves the latency in accessing SRAM with IMA.

Change-Id: I6208e75b23a07e983c4aee103454076ac66b9806
Signed-off-by: default avatarSubbaraman Narayanamurthy <subbaram@codeaurora.org>
parent 70be28c9
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",