Added the sysfs variables to expose the user space functionalities
like DCC enable,disable,configure addresses and software triggers.
Also added the necessary methods along with the same.

Signed-off-by: Souradeep Chowdhury <schow...@codeaurora.org>
---
 drivers/soc/qcom/dcc.c | 519 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 519 insertions(+)

diff --git a/drivers/soc/qcom/dcc.c b/drivers/soc/qcom/dcc.c
index d67452b..02eee96 100644
--- a/drivers/soc/qcom/dcc.c
+++ b/drivers/soc/qcom/dcc.c
@@ -185,6 +185,82 @@ static int dcc_sram_writel(struct dcc_drvdata *drvdata,
        return 0;
 }
 
+static bool dcc_ready(struct dcc_drvdata *drvdata)
+{
+       u32 val;
+
+       /* poll until DCC ready */
+       if (!readl_poll_timeout((drvdata->base + DCC_STATUS), val,
+                               (BMVAL(val, 0, 1) == 0), 1, TIMEOUT_US))
+               return true;
+
+       return false;
+}
+
+static int dcc_read_status(struct dcc_drvdata *drvdata)
+{
+       int curr_list;
+       u32 bus_status;
+       u32 ll_cfg = 0;
+       u32 tmp_ll_cfg = 0;
+
+       for (curr_list = 0; curr_list < drvdata->nr_link_list; curr_list++) {
+               if (!drvdata->enable[curr_list])
+                       continue;
+
+               bus_status = dcc_readl(drvdata, 
DCC_LL_BUS_ACCESS_STATUS(curr_list));
+
+               if (bus_status) {
+                       dev_err(drvdata->dev,
+                               "Read access error for list %d err: 0x%x.\n",
+                               curr_list, bus_status);
+
+                       ll_cfg = dcc_readl(drvdata, DCC_LL_CFG(curr_list));
+                       tmp_ll_cfg = ll_cfg & ~BIT(9);
+                       dcc_writel(drvdata, tmp_ll_cfg, DCC_LL_CFG(curr_list));
+                       dcc_writel(drvdata, 0x3,
+                               DCC_LL_BUS_ACCESS_STATUS(curr_list));
+                       dcc_writel(drvdata, ll_cfg, DCC_LL_CFG(curr_list));
+                       return -ENODATA;
+               }
+       }
+
+       return 0;
+}
+
+static int dcc_sw_trigger(struct dcc_drvdata *drvdata)
+{
+       int ret = 0;
+       int curr_list;
+       u32 ll_cfg = 0;
+       u32 tmp_ll_cfg = 0;
+
+       mutex_lock(&drvdata->mutex);
+
+       for (curr_list = 0; curr_list < drvdata->nr_link_list; curr_list++) {
+               if (!drvdata->enable[curr_list])
+                       continue;
+               ll_cfg = dcc_readl(drvdata, DCC_LL_CFG(curr_list));
+               tmp_ll_cfg = ll_cfg & ~BIT(9);
+               dcc_writel(drvdata, tmp_ll_cfg, DCC_LL_CFG(curr_list));
+               dcc_writel(drvdata, 1, DCC_LL_SW_TRIGGER(curr_list));
+               dcc_writel(drvdata, ll_cfg, DCC_LL_CFG(curr_list));
+       }
+
+       if (!dcc_ready(drvdata)) {
+               dev_err(drvdata->dev,
+                       "DCC is busy after receiving sw tigger.\n");
+               ret = -EBUSY;
+               goto err;
+       }
+
+       ret = dcc_read_status(drvdata);
+
+err:
+       mutex_unlock(&drvdata->mutex);
+       return ret;
+}
+
 static int _dcc_ll_cfg_read_write(struct dcc_drvdata *drvdata,
 struct dcc_config_entry *entry, struct dcc_cfg_attr *cfg)
 {
@@ -628,6 +704,211 @@ static int dcc_enable(struct dcc_drvdata *drvdata)
        return ret;
 }
 
+static void dcc_disable(struct dcc_drvdata *drvdata)
+{
+       int curr_list;
+
+       mutex_lock(&drvdata->mutex);
+
+       if (!dcc_ready(drvdata))
+               dev_err(drvdata->dev, "DCC is not ready Disabling DCC...\n");
+
+       for (curr_list = 0; curr_list < drvdata->nr_link_list; curr_list++) {
+               if (!drvdata->enable[curr_list])
+                       continue;
+               dcc_writel(drvdata, 0, DCC_LL_CFG(curr_list));
+               dcc_writel(drvdata, 0, DCC_LL_BASE(curr_list));
+               dcc_writel(drvdata, 0, DCC_FD_BASE(curr_list));
+               dcc_writel(drvdata, 0, DCC_LL_LOCK(curr_list));
+               drvdata->enable[curr_list] = false;
+       }
+       memset_io(drvdata->ram_base, 0, drvdata->ram_size);
+       drvdata->ram_cfg = 0;
+       drvdata->ram_start = 0;
+       mutex_unlock(&drvdata->mutex);
+}
+
+static ssize_t curr_list_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       int ret;
+       struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
+
+       mutex_lock(&drvdata->mutex);
+       if (drvdata->curr_list == DCC_INVALID_LINK_LIST) {
+               dev_err(dev, "curr_list is not set.\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       ret = scnprintf(buf, PAGE_SIZE, "%d\n", drvdata->curr_list);
+err:
+       mutex_unlock(&drvdata->mutex);
+       return ret;
+}
+
+static ssize_t curr_list_store(struct device *dev,
+                                               struct device_attribute *attr,
+                                               const char *buf, size_t size)
+{
+       struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
+       unsigned long val;
+       u32 lock_reg;
+       bool dcc_enable = false;
+
+       if (kstrtoul(buf, 16, &val))
+               return -EINVAL;
+
+       if (val >= drvdata->nr_link_list)
+               return -EINVAL;
+
+       mutex_lock(&drvdata->mutex);
+
+       dcc_enable = is_dcc_enabled(drvdata);
+       if (drvdata->curr_list != DCC_INVALID_LINK_LIST && dcc_enable) {
+               dev_err(drvdata->dev, "DCC is enabled, please disable it 
first.\n");
+               mutex_unlock(&drvdata->mutex);
+               return -EINVAL;
+       }
+
+       lock_reg = dcc_readl(drvdata, DCC_LL_LOCK(val));
+       if (lock_reg & 0x1) {
+               dev_err(drvdata->dev, "DCC linked list is already 
configured\n");
+               mutex_unlock(&drvdata->mutex);
+               return -EINVAL;
+       }
+       drvdata->curr_list = val;
+       mutex_unlock(&drvdata->mutex);
+
+       return size;
+}
+
+static DEVICE_ATTR_RW(curr_list);
+
+
+static ssize_t trigger_store(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t size)
+{
+       int ret = 0;
+       unsigned long val;
+       struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
+
+       if (kstrtoul(buf, 16, &val))
+               return -EINVAL;
+       if (val != 1)
+               return -EINVAL;
+
+       ret = dcc_sw_trigger(drvdata);
+       if (!ret)
+               ret = size;
+
+       return ret;
+}
+static DEVICE_ATTR_WO(trigger);
+
+static ssize_t enable_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       int ret;
+       bool dcc_enable = false;
+       struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
+
+       mutex_lock(&drvdata->mutex);
+       if (drvdata->curr_list >= drvdata->nr_link_list) {
+               dev_err(dev, "Select link list to program using curr_list\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       dcc_enable = is_dcc_enabled(drvdata);
+
+       ret = scnprintf(buf, PAGE_SIZE, "%u\n",
+                               (unsigned int)dcc_enable);
+err:
+       mutex_unlock(&drvdata->mutex);
+       return ret;
+}
+
+static ssize_t enable_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t size)
+{
+       int ret = 0;
+       unsigned long val;
+       struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
+
+       if (kstrtoul(buf, 16, &val))
+               return -EINVAL;
+
+       if (val)
+               ret = dcc_enable(drvdata);
+       else
+               dcc_disable(drvdata);
+
+       if (!ret)
+               ret = size;
+
+       return ret;
+
+}
+
+static DEVICE_ATTR_RW(enable);
+
+static ssize_t config_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
+       struct dcc_config_entry *entry;
+       char local_buf[64];
+       int len = 0, count = 0;
+
+       buf[0] = '\0';
+
+       mutex_lock(&drvdata->mutex);
+       if (drvdata->curr_list >= drvdata->nr_link_list) {
+               dev_err(dev, "Select link list to program using curr_list\n");
+               count = -EINVAL;
+               goto err;
+       }
+
+       list_for_each_entry(entry,
+       &drvdata->cfg_head[drvdata->curr_list], list) {
+               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->write_val);
+                       break;
+               case DCC_LOOP_TYPE:
+                       len = snprintf(local_buf, 64, "Index: 0x%x, Loop: %d\n",
+                               entry->index, entry->loop_cnt);
+                       break;
+               case DCC_WRITE_TYPE:
+                       len = snprintf(local_buf, 64,
+                               "Write Index: 0x%x, Base: 0x%x, Offset: 0x%x, 
len: 0x%x APB: %d\n",
+                               entry->index, entry->base, entry->offset, 
entry->len,
+                               entry->apb_bus);
+                       break;
+               default:
+                       len = snprintf(local_buf, 64,
+                               "Read Index: 0x%x, Base: 0x%x, Offset: 0x%x, 
len: 0x%x APB: %d\n",
+                               entry->index, entry->base, entry->offset,
+                               entry->len, entry->apb_bus);
+               }
+
+               if ((count + len) > PAGE_SIZE) {
+                       dev_err(dev, "DCC: Couldn't write complete config\n");
+                       break;
+               }
+               strlcat(buf, local_buf, PAGE_SIZE);
+               count += len;
+       }
+
+err:
+       mutex_unlock(&drvdata->mutex);
+       return count;
+}
+
 static int dcc_config_add(struct dcc_drvdata *drvdata, unsigned int addr,
                                unsigned int len, int apb_bus)
 {
@@ -709,6 +990,38 @@ static int dcc_config_add(struct dcc_drvdata *drvdata, 
unsigned int addr,
        return ret;
 }
 
+static ssize_t config_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t size)
+{
+       int ret, len, apb_bus;
+       unsigned int base;
+       struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
+       int nval;
+
+       nval = sscanf(buf, "%x %i %d", &base, &len, &apb_bus);
+       if (nval <= 0 || nval > 3)
+               return -EINVAL;
+
+       if (nval == 1) {
+               len = 1;
+               apb_bus = 0;
+       } else if (nval == 2) {
+               apb_bus = 0;
+       } else {
+               apb_bus = 1;
+       }
+
+       ret = dcc_config_add(drvdata, base, len, apb_bus);
+       if (ret)
+               return ret;
+
+       return size;
+
+}
+
+static DEVICE_ATTR_RW(config);
+
 static void dcc_config_reset(struct dcc_drvdata *drvdata)
 {
        struct dcc_config_entry *entry, *temp;
@@ -730,6 +1043,80 @@ static void dcc_config_reset(struct dcc_drvdata *drvdata)
        mutex_unlock(&drvdata->mutex);
 }
 
+static ssize_t config_reset_store(struct device *dev,
+       struct device_attribute *attr,
+       const char *buf, size_t size)
+{
+       unsigned long val;
+       struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
+
+       if (kstrtoul(buf, 16, &val))
+               return -EINVAL;
+
+       if (val)
+               dcc_config_reset(drvdata);
+
+       return size;
+}
+
+static DEVICE_ATTR_WO(config_reset);
+
+static ssize_t ready_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       int ret;
+       struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
+
+       mutex_lock(&drvdata->mutex);
+
+       if (drvdata->curr_list >= drvdata->nr_link_list) {
+               dev_err(dev, "Select link list to program using curr_list\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       if (!drvdata->enable[drvdata->curr_list]) {
+               ret = -EINVAL;
+               goto err;
+       }
+
+       ret = scnprintf(buf, PAGE_SIZE, "%u\n",
+                       (unsigned int)BVAL(dcc_readl(drvdata, DCC_STATUS), 1));
+err:
+       mutex_unlock(&drvdata->mutex);
+       return ret;
+}
+
+static DEVICE_ATTR_RO(ready);
+
+static ssize_t interrupt_disable_show(struct device *dev,
+                                               struct device_attribute *attr,
+                                               char *buf)
+{
+       struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n",
+                               (unsigned int)drvdata->interrupt_disable);
+}
+
+static ssize_t interrupt_disable_store(struct device *dev,
+       struct device_attribute *attr,
+       const char *buf, size_t size)
+{
+       unsigned long val;
+       struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
+
+       if (kstrtoul(buf, 16, &val))
+               return -EINVAL;
+
+       mutex_lock(&drvdata->mutex);
+       drvdata->interrupt_disable = (val ? 1:0);
+       mutex_unlock(&drvdata->mutex);
+       return size;
+}
+
+static DEVICE_ATTR_RW(interrupt_disable);
+
 static int dcc_add_loop(struct dcc_drvdata *drvdata, unsigned long loop_cnt)
 {
        struct dcc_config_entry *entry;
@@ -747,6 +1134,40 @@ static int dcc_add_loop(struct dcc_drvdata *drvdata, 
unsigned long loop_cnt)
        return 0;
 }
 
+static ssize_t loop_store(struct device *dev,
+       struct device_attribute *attr,
+       const char *buf, size_t size)
+{
+       int ret;
+       unsigned long loop_cnt;
+       struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
+
+       mutex_lock(&drvdata->mutex);
+
+       if (kstrtoul(buf, 16, &loop_cnt)) {
+               ret = -EINVAL;
+               goto err;
+       }
+
+       if (drvdata->curr_list >= drvdata->nr_link_list) {
+               dev_err(dev, "Select link list to program using curr_list\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       ret = dcc_add_loop(drvdata, loop_cnt);
+       if (ret)
+               goto err;
+
+       mutex_unlock(&drvdata->mutex);
+       return size;
+err:
+       mutex_unlock(&drvdata->mutex);
+       return ret;
+}
+
+static DEVICE_ATTR_WO(loop);
+
 static int dcc_rd_mod_wr_add(struct dcc_drvdata *drvdata, unsigned int mask,
                                unsigned int val)
 {
@@ -784,6 +1205,30 @@ static int dcc_rd_mod_wr_add(struct dcc_drvdata *drvdata, 
unsigned int mask,
        return ret;
 }
 
+static ssize_t rd_mod_wr_store(struct device *dev,
+       struct device_attribute *attr,
+       const char *buf, size_t size)
+{
+       int ret;
+       int nval;
+       unsigned int mask, val;
+       struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
+
+       nval = sscanf(buf, "%x %x", &mask, &val);
+
+       if (nval <= 1 || nval > 2)
+               return -EINVAL;
+
+       ret = dcc_rd_mod_wr_add(drvdata, mask, val);
+       if (ret)
+               return ret;
+
+       return size;
+
+}
+
+static DEVICE_ATTR_WO(rd_mod_wr);
+
 static int dcc_add_write(struct dcc_drvdata *drvdata, unsigned int addr,
                                unsigned int write_val, int apb_bus)
 {
@@ -806,6 +1251,77 @@ static int dcc_add_write(struct dcc_drvdata *drvdata, 
unsigned int addr,
        return 0;
 }
 
+static ssize_t config_write_store(struct device *dev,
+                                               struct device_attribute *attr,
+                                               const char *buf, size_t size)
+{
+       int ret;
+       int nval;
+       unsigned int addr, write_val;
+       int apb_bus = 0;
+       struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
+
+       mutex_lock(&drvdata->mutex);
+
+       nval = sscanf(buf, "%x %x %d", &addr, &write_val, &apb_bus);
+
+       if (nval <= 1 || nval > 3) {
+               ret = -EINVAL;
+               goto err;
+       }
+
+       if (drvdata->curr_list >= drvdata->nr_link_list) {
+               dev_err(dev, "Select link list to program using curr_list\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       if (nval == 3 && apb_bus != 0)
+               apb_bus = 1;
+
+       ret = dcc_add_write(drvdata, addr, write_val, apb_bus);
+       if (ret)
+               goto err;
+
+       mutex_unlock(&drvdata->mutex);
+       return size;
+err:
+       mutex_unlock(&drvdata->mutex);
+       return ret;
+}
+
+static DEVICE_ATTR_WO(config_write);
+
+static const struct device_attribute *dcc_attrs[] = {
+       &dev_attr_trigger,
+       &dev_attr_enable,
+       &dev_attr_config,
+       &dev_attr_config_reset,
+       &dev_attr_ready,
+       &dev_attr_interrupt_disable,
+       &dev_attr_loop,
+       &dev_attr_rd_mod_wr,
+       &dev_attr_curr_list,
+       &dev_attr_config_write,
+       NULL,
+};
+
+static int dcc_create_files(struct device *dev,
+                                       const struct device_attribute **attrs)
+{
+       int ret = 0, i;
+
+       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",
+                               attrs[i]->attr.name);
+                       break;
+               }
+       }
+       return ret;
+}
+
 static int dcc_sram_open(struct inode *inode, struct file *file)
 {
        struct dcc_drvdata *drvdata = container_of(inode->i_cdev,
@@ -1018,6 +1534,9 @@ static int dcc_probe(struct platform_device *pdev)
        ret = dcc_sram_dev_init(drvdata);
        if (ret)
                goto err;
+       ret = dcc_create_files(dev, dcc_attrs);
+       if (ret)
+               goto err;
 
        return ret;
 err:
-- 
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation

Reply via email to