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

Commit 33073621 authored by Satyajit Desai's avatar Satyajit Desai
Browse files

soc: qcom: Add write support for DCCV2



Add register/memory write support for DCCV2. This will enable DCC
to issue write commands using the configuration script.

Change-Id: I7963a3dc3bf315833a980325a20610340a055efa
Signed-off-by: default avatarSatyajit Desai <sadesai@codeaurora.org>
parent d6686534
Loading
Loading
Loading
Loading
+200 −84
Original line number Diff line number Diff line
@@ -74,6 +74,9 @@
#define DCC_RD_MOD_WR_DESCRIPTOR	(BIT(31))
#define DCC_LINK_DESCRIPTOR		(BIT(31) | BIT(30))

#define DCC_READ_IND			0x00
#define DCC_WRITE_IND			(BIT(28))

#define DCC_MAX_LINK_LIST		5
#define DCC_INVALID_LINK_LIST		0xFF

@@ -92,6 +95,13 @@ enum dcc_data_sink {
	DCC_DATA_SINK_ATB
};

enum dcc_descriptor_type {
	DCC_ADDR_TYPE,
	DCC_LOOP_TYPE,
	DCC_READ_WRITE_TYPE,
	DCC_WRITE_TYPE
};

static const char * const str_dcc_data_sink[] = {
	[DCC_DATA_SINK_SRAM]		= "sram",
	[DCC_DATA_SINK_ATB]		= "atb",
@@ -108,9 +118,9 @@ struct dcc_config_entry {
	uint32_t			len;
	uint32_t			index;
	uint32_t			loop_cnt;
	uint32_t		rd_mod_wr;
	uint32_t			write_val;
	uint32_t			mask;
	bool			rd_wr_entry;
	enum dcc_descriptor_type	desc_type;
	struct list_head		list;
};

@@ -189,7 +199,7 @@ static int dcc_sw_trigger(struct dcc_drvdata *drvdata)
	mutex_lock(&drvdata->mutex);

	if (!dcc_ready(drvdata)) {
		dev_err(drvdata->dev, "DCC is not ready!\n");
		dev_err(drvdata->dev, "DCC is not ready\n");
		ret = -EBUSY;
		goto err;
	}
@@ -224,7 +234,7 @@ static int __dcc_ll_cfg(struct dcc_drvdata *drvdata, int curr_list)
	uint32_t loop_off = 0;
	uint32_t link;
	uint32_t pos, total_len = 0, loop_len = 0;
	uint32_t loop, loop_cnt;
	uint32_t loop, loop_cnt = 0;
	bool loop_start = false;
	struct dcc_config_entry *entry;

@@ -233,7 +243,9 @@ static int __dcc_ll_cfg(struct dcc_drvdata *drvdata, int curr_list)
	link = 0;

	list_for_each_entry(entry, &drvdata->cfg_head[curr_list], list) {
		if (entry->rd_wr_entry) {
		switch (entry->desc_type) {
		case DCC_READ_WRITE_TYPE:
		{
			if (link) {
				/* write new offset = 1 to continue
				 * processing the list
@@ -255,12 +267,14 @@ static int __dcc_ll_cfg(struct dcc_drvdata *drvdata, int curr_list)
			dcc_sram_writel(drvdata, entry->mask, sram_offset);
				sram_offset += 4;

			dcc_sram_writel(drvdata, entry->rd_mod_wr, sram_offset);
			dcc_sram_writel(drvdata, entry->write_val, sram_offset);
				sram_offset += 4;
			continue;
			addr = 0;
			break;
		}

		if (entry->loop_cnt) {
		case DCC_LOOP_TYPE:
		{
			/* Check if we need to write link of prev entry */
			if (link) {
				dcc_sram_writel(drvdata, link, sram_offset);
@@ -292,23 +306,66 @@ static int __dcc_ll_cfg(struct dcc_drvdata *drvdata, int curr_list)
			prev_off = 0;
			prev_addr = addr;

			continue;
			break;
		}

		case DCC_WRITE_TYPE:
		{
			if (link) {
				/* write new offset = 1 to continue
				 * processing the list
				 */
				link |= ((0x1 << 8) & BM(8, 14));
				dcc_sram_writel(drvdata, link, sram_offset);
				sram_offset += 4;
				/* Reset link and prev_off */
				addr = 0x00;
				prev_off = 0;
				prev_addr = addr;
			}

			off = entry->offset/4;
			/* write new offset-length pair to correct position */
			link |= ((off & BM(0, 7)) | BIT(15) |
				 ((entry->len << 8) & BM(8, 14)));
			link |= DCC_LINK_DESCRIPTOR;

			/* Address type */
			addr = (entry->base >> 4) & BM(0, 27);
		addr |= DCC_ADDR_DESCRIPTOR;
			addr |= DCC_ADDR_DESCRIPTOR | DCC_WRITE_IND;

			dcc_sram_writel(drvdata, addr, sram_offset);
				sram_offset += 4;

			dcc_sram_writel(drvdata, link, sram_offset);
				sram_offset += 4;

			dcc_sram_writel(drvdata, entry->write_val, sram_offset);
				sram_offset += 4;
			addr = 0x00;
			link = 0;
			break;
		}

		default:
		{
			/* Address type */
			addr = (entry->base >> 4) & BM(0, 27);
			addr |= DCC_ADDR_DESCRIPTOR | DCC_READ_IND;

			off = entry->offset/4;
			total_len += entry->len * 4;

			if (!prev_addr || prev_addr != addr || prev_off > off) {
			/* Check if we need to write link of prev entry */
				/* Check if we need to write prev link entry */
				if (link) {
				dcc_sram_writel(drvdata, link, sram_offset);
					dcc_sram_writel(drvdata,
							link, sram_offset);
					sram_offset += 4;
				}
			dev_err(drvdata->dev,
				"DCC: sram address.%d\n", sram_offset);
				dev_dbg(drvdata->dev,
					"DCC: sram address 0x%x\n",
					sram_offset);

				/* Write address */
				dcc_sram_writel(drvdata, addr, sram_offset);
@@ -319,9 +376,10 @@ static int __dcc_ll_cfg(struct dcc_drvdata *drvdata, int curr_list)
				prev_off = 0;
			}

		if ((off - prev_off) > 0xFF || entry->len > MAX_DCC_LEN) {
			if ((off - prev_off) > 0xFF ||
			    entry->len > MAX_DCC_LEN) {
				dev_err(drvdata->dev,
				"DCC: Progamming error! Base: 0x%x, offset 0x%x.\n",
					"DCC: Progamming error Base: 0x%x, offset 0x%x\n",
					entry->base, entry->offset);
				ret = -EINVAL;
				goto err;
@@ -330,7 +388,8 @@ static int __dcc_ll_cfg(struct dcc_drvdata *drvdata, int curr_list)
			if (link) {
				/*
				 * link already has one offset-length so new
			 * offset-length needs to be placed at bits [29:15]
				 * offset-length needs to be placed at
				 * bits [29:15]
				 */
				pos = 15;

@@ -338,8 +397,8 @@ static int __dcc_ll_cfg(struct dcc_drvdata *drvdata, int curr_list)
				link &= BM(0, 14);
			} else {
				/*
			 * link is empty, so new offset-length needs to be
			 * placed at bits [15:0]
				 * link is empty, so new offset-length needs
				 * to be placed at bits [15:0]
				 */
				pos = 0;
				link = 1 << 15;
@@ -360,6 +419,8 @@ static int __dcc_ll_cfg(struct dcc_drvdata *drvdata, int curr_list)
			prev_off  = off;
			prev_addr = addr;
			}
		}
	}

	if (link) {
		dcc_sram_writel(drvdata, link, sram_offset);
@@ -368,7 +429,7 @@ static int __dcc_ll_cfg(struct dcc_drvdata *drvdata, int curr_list)

	if (loop_start) {
		dev_err(drvdata->dev,
			"DCC: Progamming error! Loop unterminated.\n");
			"DCC: Progamming error: Loop unterminated\n");
		ret = -EINVAL;
		goto err;
	}
@@ -457,7 +518,7 @@ static void __dcc_first_crc(struct dcc_drvdata *drvdata)
	 */
	for (i = 0; i < 2; i++) {
		if (!dcc_ready(drvdata))
			dev_err(drvdata->dev, "DCC is not ready!\n");
			dev_err(drvdata->dev, "DCC is not ready\n");

		dcc_writel(drvdata, 1,
			   DCC_LL_SW_TRIGGER(drvdata->curr_list));
@@ -476,13 +537,13 @@ static int dcc_valid_list(struct dcc_drvdata *drvdata, int curr_list)
		return -EINVAL;

	if (drvdata->enable[curr_list]) {
		dev_err(drvdata->dev, "DCC is already enabled!\n");
		dev_err(drvdata->dev, "DCC is already enabled\n");
		return -EINVAL;
	}

	lock_reg = dcc_readl(drvdata, DCC_LL_LOCK(curr_list));
	if (lock_reg & 0x1) {
		dev_err(drvdata->dev, "DCC is already enabled!\n");
		dev_err(drvdata->dev, "DCC is already enabled\n");
		return -EINVAL;
	}

@@ -565,7 +626,7 @@ static void dcc_disable(struct dcc_drvdata *drvdata)
	mutex_lock(&drvdata->mutex);

	if (!dcc_ready(drvdata))
		dev_err(drvdata->dev, "DCC is not ready! Disabling DCC...\n");
		dev_err(drvdata->dev, "DCC is not ready Disabling DCC...\n");

	for (curr_list = 0; curr_list < DCC_MAX_LINK_LIST; curr_list++) {
		if (!drvdata->enable[curr_list])
@@ -600,7 +661,7 @@ static ssize_t dcc_curr_list(struct device *dev,
	mutex_lock(&drvdata->mutex);
	lock_reg = dcc_readl(drvdata, DCC_LL_LOCK(val));
	if (lock_reg & 0x1) {
		dev_err(drvdata->dev, "DCC linked list is already configured!\n");
		dev_err(drvdata->dev, "DCC linked list is already configured\n");
		mutex_unlock(&drvdata->mutex);
		return -EINVAL;
	}
@@ -783,25 +844,34 @@ static ssize_t dcc_show_config(struct device *dev,
	mutex_lock(&drvdata->mutex);
	list_for_each_entry(entry,
			    &drvdata->cfg_head[drvdata->curr_list], list) {
		if (entry->rd_wr_entry)
		switch (entry->desc_type) {
		case DCC_READ_WRITE_TYPE:
			len = snprintf(local_buf, 64,
				       "Index: 0x%x, mask: 0x%x, val: 0x%x\n",
				       entry->index, entry->mask,
				       entry->rd_mod_wr);
		else if (entry->loop_cnt)
				       entry->write_val);
			break;
		case DCC_LOOP_TYPE:
			len = snprintf(local_buf, 64, "Index: 0x%x, Loop: %d\n",
				       entry->index, entry->loop_cnt);
		else
			break;
		case DCC_WRITE_TYPE:
			len = snprintf(local_buf, 64,
				       "Index: 0x%x, Base: 0x%x, Offset: 0x%x, len: 0x%x\n",
				       "Write Index: 0x%x, Base: 0x%x, Offset: 0x%x, len: 0x%x\n",
				       entry->index, entry->base,
				       entry->offset, entry->len);
			break;
		default:
			len = snprintf(local_buf, 64,
				       "Read Index: 0x%x, Base: 0x%x, Offset: 0x%x, len: 0x%x\n",
				       entry->index, entry->base,
				       entry->offset, entry->len);
		}

		if ((count + len) > PAGE_SIZE) {
			dev_err(dev, "DCC: Couldn't write complete config!\n");
			dev_err(dev, "DCC: Couldn't write complete config\n");
			break;
		}

		strlcat(buf, local_buf, PAGE_SIZE);
		count += len;
	}
@@ -821,7 +891,7 @@ static int dcc_config_add(struct dcc_drvdata *drvdata, unsigned int addr,
	mutex_lock(&drvdata->mutex);

	if (!len) {
		dev_err(drvdata->dev, "DCC: Invalid length!\n");
		dev_err(drvdata->dev, "DCC: Invalid length\n");
		ret = -EINVAL;
		goto err;
	}
@@ -883,6 +953,7 @@ static int dcc_config_add(struct dcc_drvdata *drvdata, unsigned int addr,
		entry->offset = offset;
		entry->len = min_t(uint32_t, len, MAX_DCC_LEN);
		entry->index = drvdata->nr_config[drvdata->curr_list]++;
		entry->desc_type = DCC_ADDR_TYPE;
		INIT_LIST_HEAD(&entry->list);
		list_add_tail(&entry->list,
			      &drvdata->cfg_head[drvdata->curr_list]);
@@ -1087,7 +1158,7 @@ static ssize_t dcc_rd_mod_wr(struct device *dev,
	}

	if (list_empty(&drvdata->cfg_head[drvdata->curr_list])) {
		dev_err(drvdata->dev, "DCC: No read address programmed!\n");
		dev_err(drvdata->dev, "DCC: No read address programmed\n");
		ret = -EPERM;
		goto err;
	}
@@ -1098,9 +1169,9 @@ static ssize_t dcc_rd_mod_wr(struct device *dev,
		goto err;
	}

	entry->rd_wr_entry = true;
	entry->desc_type = DCC_READ_WRITE_TYPE;
	entry->mask = mask;
	entry->rd_mod_wr = val;
	entry->write_val = val;
	entry->index = drvdata->nr_config[drvdata->curr_list]++;
	INIT_LIST_HEAD(&entry->list);
	list_add_tail(&entry->list, &drvdata->cfg_head[drvdata->curr_list]);
@@ -1110,6 +1181,50 @@ static ssize_t dcc_rd_mod_wr(struct device *dev,
}
static DEVICE_ATTR(rd_mod_wr, 0200, NULL, dcc_rd_mod_wr);

static ssize_t dcc_write(struct device *dev,
				    struct device_attribute *attr,
				    const char *buf, size_t size)
{
	int ret = size;
	int nval;
	unsigned int addr, write_val;
	struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
	struct dcc_config_entry *entry;

	mutex_lock(&drvdata->mutex);

	nval = sscanf(buf, "%x %x", &addr, &write_val);

	if (drvdata->curr_list >= DCC_MAX_LINK_LIST) {
		dev_err(dev, "Select link list to program using curr_list\n");
		return -EINVAL;
	}

	if (nval <= 1 || nval > 2) {
		ret = -EINVAL;
		goto err;
	}

	entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL);
	if (!entry) {
		ret = -ENOMEM;
		goto err;
	}

	entry->desc_type = DCC_WRITE_TYPE;
	entry->base = addr & BM(4, 31);
	entry->offset = addr - entry->base;
	entry->write_val = write_val;
	entry->index = drvdata->nr_config[drvdata->curr_list]++;
	entry->len = 1;
	INIT_LIST_HEAD(&entry->list);
	list_add_tail(&entry->list, &drvdata->cfg_head[drvdata->curr_list]);
err:
	mutex_unlock(&drvdata->mutex);
	return ret;
}
static DEVICE_ATTR(config_write, 0200, NULL, dcc_write);

static const struct device_attribute *dcc_attrs[] = {
	&dev_attr_func_type,
	&dev_attr_data_sink,
@@ -1123,6 +1238,7 @@ static const struct device_attribute *dcc_attrs[] = {
	&dev_attr_loop,
	&dev_attr_rd_mod_wr,
	&dev_attr_curr_list,
	&dev_attr_config_write,
	NULL,
};

@@ -1134,7 +1250,7 @@ static int dcc_create_files(struct device *dev,
	for (i = 0; attrs[i] != NULL; i++) {
		ret = device_create_file(dev, attrs[i]);
		if (ret) {
			dev_err(dev, "DCC: Couldn't create sysfs attribute: %s!\n",
			dev_err(dev, "DCC: Couldn't create sysfs attribute: %s\n",
				attrs[i]->attr.name);
			break;
		}
@@ -1173,7 +1289,7 @@ static ssize_t dcc_sram_read(struct file *file, char __user *data,

	if (copy_to_user(data, buf, len)) {
		dev_err(drvdata->dev,
			"DCC: Couldn't copy all data to user!\n");
			"DCC: Couldn't copy all data to user\n");
		kfree(buf);
		return -EFAULT;
	}
@@ -1372,7 +1488,7 @@ static int dcc_probe(struct platform_device *pdev)
			}

		if (i == ARRAY_SIZE(str_dcc_data_sink)) {
			dev_err(dev, "Unknown sink type for DCC! Using '%s' as data sink\n",
			dev_err(dev, "Unknown sink type for DCC Using '%s' as data sink\n",
				str_dcc_data_sink[drvdata->data_sink]);
		}
	}