Loading Documentation/devicetree/bindings/soc/qcom/dcc.txt +15 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,14 @@ Optional properties: "atb" : To send captured data over ATB to a trace sink "sram" : To save captured data in dcc internal SRAM. - qcom,curr-link-list: int, To specify the link list to use for the default list. - qcom,link-list: The values to be programmed into the default link list. The enum values for DCC operations is defined in dt-bindings/soc/qcom,dcc_v2.h The following gives basic structure to be used for each operation: <DCC_operation addr val apb_bus> val is to be interpreted based on what operation is to be performed. Example: dcc: dcc@4b3000 { Loading @@ -47,6 +55,13 @@ Example: clocks = <&clock_gcc clk_gcc_dcc_ahb_clk>; clock-names = "dcc_clk"; qcom,curr-link-list = <2>; qcom,link-list = <DCC_READ 0x1740300 6 0>, <DCC_READ 0x1620500 4 0>, <DCC_READ 0x7840000 1 0>, <DCC_READ 0x7841010 12 0>, <DCC_READ 0x7842000 16 0>, <DCC_READ 0x7842500 2 0>; qcom,save-reg; }; drivers/soc/qcom/dcc_v2.c +115 −37 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ #include <linux/uaccess.h> #include <soc/qcom/memory_dump.h> #include <soc/qcom/scm.h> #include <dt-bindings/soc/qcom,dcc_v2.h> #define TIMEOUT_US (100) Loading Loading @@ -1088,14 +1089,30 @@ static ssize_t dcc_store_interrupt_disable(struct device *dev, static DEVICE_ATTR(interrupt_disable, 0644, dcc_show_interrupt_disable, dcc_store_interrupt_disable); static int dcc_add_loop(struct dcc_drvdata *drvdata, unsigned long loop_cnt) { struct dcc_config_entry *entry; entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; entry->loop_cnt = min_t(uint32_t, loop_cnt, MAX_LOOP_CNT); entry->index = drvdata->nr_config[drvdata->curr_list]++; entry->desc_type = DCC_LOOP_TYPE; INIT_LIST_HEAD(&entry->list); list_add_tail(&entry->list, &drvdata->cfg_head[drvdata->curr_list]); return 0; } static ssize_t dcc_store_loop(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int ret = size; int ret; unsigned long loop_cnt; struct dcc_drvdata *drvdata = dev_get_drvdata(dev); struct dcc_config_entry *entry; mutex_lock(&drvdata->mutex); Loading @@ -1110,18 +1127,12 @@ static ssize_t dcc_store_loop(struct device *dev, goto err; } entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL); if (!entry) { ret = -ENOMEM; ret = dcc_add_loop(drvdata, loop_cnt); if (ret) goto err; } entry->loop_cnt = min_t(uint32_t, loop_cnt, MAX_LOOP_CNT); entry->index = drvdata->nr_config[drvdata->curr_list]++; entry->desc_type = DCC_LOOP_TYPE; INIT_LIST_HEAD(&entry->list); list_add_tail(&entry->list, &drvdata->cfg_head[drvdata->curr_list]); mutex_unlock(&drvdata->mutex); return size; err: mutex_unlock(&drvdata->mutex); return ret; Loading Loading @@ -1171,16 +1182,37 @@ static ssize_t dcc_rd_mod_wr(struct device *dev, } static DEVICE_ATTR(rd_mod_wr, 0200, NULL, dcc_rd_mod_wr); static int dcc_add_write(struct dcc_drvdata *drvdata, unsigned int addr, unsigned int write_val, int apb_bus) { struct dcc_config_entry *entry; entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; 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; entry->apb_bus = apb_bus; INIT_LIST_HEAD(&entry->list); list_add_tail(&entry->list, &drvdata->cfg_head[drvdata->curr_list]); return 0; } static ssize_t dcc_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int ret = size; int ret; int nval; unsigned int addr, write_val; int apb_bus; int apb_bus = 0; struct dcc_drvdata *drvdata = dev_get_drvdata(dev); struct dcc_config_entry *entry; mutex_lock(&drvdata->mutex); Loading @@ -1197,23 +1229,15 @@ static ssize_t dcc_write(struct device *dev, goto err; } entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL); if (!entry) { ret = -ENOMEM; goto err; } if (nval == 3 && apb_bus != 0) apb_bus = 1; if (nval == 3) entry->apb_bus = true; ret = dcc_add_write(drvdata, addr, write_val, apb_bus); if (ret) 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]); mutex_unlock(&drvdata->mutex); return size; err: mutex_unlock(&drvdata->mutex); return ret; Loading Loading @@ -1418,6 +1442,64 @@ static void dcc_sram_dev_exit(struct dcc_drvdata *drvdata) dcc_sram_dev_deregister(drvdata); } static void dcc_configure_list(struct dcc_drvdata *drvdata, struct device_node *np) { int ret, i; const __be32 *prop; uint32_t len, entry, val1, val2, apb_bus; uint32_t curr_link_list; ret = of_property_read_u32(np, "qcom,curr-link-list", &curr_link_list); if (ret) return; if (curr_link_list >= DCC_MAX_LINK_LIST) { dev_err(drvdata->dev, "List configuration failed"); return; } drvdata->curr_list = curr_link_list; prop = of_get_property(np, "qcom,link-list", &len); if (prop) { len /= sizeof(__be32); i = 0; while (i < len) { entry = be32_to_cpu(prop[i++]); val1 = be32_to_cpu(prop[i++]); val2 = be32_to_cpu(prop[i++]); apb_bus = be32_to_cpu(prop[i++]); switch (entry) { case DCC_READ: ret = dcc_config_add(drvdata, val1, val2, apb_bus); break; case DCC_WRITE: ret = dcc_add_write(drvdata, val1, val2, apb_bus); break; case DCC_LOOP: ret = dcc_add_loop(drvdata, val1); break; default: ret = -EINVAL; } if (ret) { dev_err(drvdata->dev, "DCC init time config failed err:%d\n", ret); break; } } if (!ret) dcc_enable(drvdata); } } static int dcc_probe(struct platform_device *pdev) { int ret, i; Loading Loading @@ -1492,6 +1574,8 @@ static int dcc_probe(struct platform_device *pdev) if (ret) goto err; dcc_configure_list(drvdata, pdev->dev.of_node); return 0; err: return ret; Loading Loading @@ -1527,13 +1611,7 @@ static int __init dcc_init(void) { return platform_driver_register(&dcc_driver); } module_init(dcc_init); static void __exit dcc_exit(void) { platform_driver_unregister(&dcc_driver); } module_exit(dcc_exit); pure_initcall(dcc_init); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("MSM data capture and compare engine"); include/dt-bindings/soc/qcom,dcc_v2.h 0 → 100644 +20 −0 Original line number Diff line number Diff line /* Copyright (c) 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef __DT_BINDINGS_QCOM_DCC_V2_H #define __DT_BINDINGS_QCOM_DCC_V2_H #define DCC_READ 0 #define DCC_WRITE 1 #define DCC_LOOP 2 #endif Loading
Documentation/devicetree/bindings/soc/qcom/dcc.txt +15 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,14 @@ Optional properties: "atb" : To send captured data over ATB to a trace sink "sram" : To save captured data in dcc internal SRAM. - qcom,curr-link-list: int, To specify the link list to use for the default list. - qcom,link-list: The values to be programmed into the default link list. The enum values for DCC operations is defined in dt-bindings/soc/qcom,dcc_v2.h The following gives basic structure to be used for each operation: <DCC_operation addr val apb_bus> val is to be interpreted based on what operation is to be performed. Example: dcc: dcc@4b3000 { Loading @@ -47,6 +55,13 @@ Example: clocks = <&clock_gcc clk_gcc_dcc_ahb_clk>; clock-names = "dcc_clk"; qcom,curr-link-list = <2>; qcom,link-list = <DCC_READ 0x1740300 6 0>, <DCC_READ 0x1620500 4 0>, <DCC_READ 0x7840000 1 0>, <DCC_READ 0x7841010 12 0>, <DCC_READ 0x7842000 16 0>, <DCC_READ 0x7842500 2 0>; qcom,save-reg; };
drivers/soc/qcom/dcc_v2.c +115 −37 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ #include <linux/uaccess.h> #include <soc/qcom/memory_dump.h> #include <soc/qcom/scm.h> #include <dt-bindings/soc/qcom,dcc_v2.h> #define TIMEOUT_US (100) Loading Loading @@ -1088,14 +1089,30 @@ static ssize_t dcc_store_interrupt_disable(struct device *dev, static DEVICE_ATTR(interrupt_disable, 0644, dcc_show_interrupt_disable, dcc_store_interrupt_disable); static int dcc_add_loop(struct dcc_drvdata *drvdata, unsigned long loop_cnt) { struct dcc_config_entry *entry; entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; entry->loop_cnt = min_t(uint32_t, loop_cnt, MAX_LOOP_CNT); entry->index = drvdata->nr_config[drvdata->curr_list]++; entry->desc_type = DCC_LOOP_TYPE; INIT_LIST_HEAD(&entry->list); list_add_tail(&entry->list, &drvdata->cfg_head[drvdata->curr_list]); return 0; } static ssize_t dcc_store_loop(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int ret = size; int ret; unsigned long loop_cnt; struct dcc_drvdata *drvdata = dev_get_drvdata(dev); struct dcc_config_entry *entry; mutex_lock(&drvdata->mutex); Loading @@ -1110,18 +1127,12 @@ static ssize_t dcc_store_loop(struct device *dev, goto err; } entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL); if (!entry) { ret = -ENOMEM; ret = dcc_add_loop(drvdata, loop_cnt); if (ret) goto err; } entry->loop_cnt = min_t(uint32_t, loop_cnt, MAX_LOOP_CNT); entry->index = drvdata->nr_config[drvdata->curr_list]++; entry->desc_type = DCC_LOOP_TYPE; INIT_LIST_HEAD(&entry->list); list_add_tail(&entry->list, &drvdata->cfg_head[drvdata->curr_list]); mutex_unlock(&drvdata->mutex); return size; err: mutex_unlock(&drvdata->mutex); return ret; Loading Loading @@ -1171,16 +1182,37 @@ static ssize_t dcc_rd_mod_wr(struct device *dev, } static DEVICE_ATTR(rd_mod_wr, 0200, NULL, dcc_rd_mod_wr); static int dcc_add_write(struct dcc_drvdata *drvdata, unsigned int addr, unsigned int write_val, int apb_bus) { struct dcc_config_entry *entry; entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; 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; entry->apb_bus = apb_bus; INIT_LIST_HEAD(&entry->list); list_add_tail(&entry->list, &drvdata->cfg_head[drvdata->curr_list]); return 0; } static ssize_t dcc_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int ret = size; int ret; int nval; unsigned int addr, write_val; int apb_bus; int apb_bus = 0; struct dcc_drvdata *drvdata = dev_get_drvdata(dev); struct dcc_config_entry *entry; mutex_lock(&drvdata->mutex); Loading @@ -1197,23 +1229,15 @@ static ssize_t dcc_write(struct device *dev, goto err; } entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL); if (!entry) { ret = -ENOMEM; goto err; } if (nval == 3 && apb_bus != 0) apb_bus = 1; if (nval == 3) entry->apb_bus = true; ret = dcc_add_write(drvdata, addr, write_val, apb_bus); if (ret) 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]); mutex_unlock(&drvdata->mutex); return size; err: mutex_unlock(&drvdata->mutex); return ret; Loading Loading @@ -1418,6 +1442,64 @@ static void dcc_sram_dev_exit(struct dcc_drvdata *drvdata) dcc_sram_dev_deregister(drvdata); } static void dcc_configure_list(struct dcc_drvdata *drvdata, struct device_node *np) { int ret, i; const __be32 *prop; uint32_t len, entry, val1, val2, apb_bus; uint32_t curr_link_list; ret = of_property_read_u32(np, "qcom,curr-link-list", &curr_link_list); if (ret) return; if (curr_link_list >= DCC_MAX_LINK_LIST) { dev_err(drvdata->dev, "List configuration failed"); return; } drvdata->curr_list = curr_link_list; prop = of_get_property(np, "qcom,link-list", &len); if (prop) { len /= sizeof(__be32); i = 0; while (i < len) { entry = be32_to_cpu(prop[i++]); val1 = be32_to_cpu(prop[i++]); val2 = be32_to_cpu(prop[i++]); apb_bus = be32_to_cpu(prop[i++]); switch (entry) { case DCC_READ: ret = dcc_config_add(drvdata, val1, val2, apb_bus); break; case DCC_WRITE: ret = dcc_add_write(drvdata, val1, val2, apb_bus); break; case DCC_LOOP: ret = dcc_add_loop(drvdata, val1); break; default: ret = -EINVAL; } if (ret) { dev_err(drvdata->dev, "DCC init time config failed err:%d\n", ret); break; } } if (!ret) dcc_enable(drvdata); } } static int dcc_probe(struct platform_device *pdev) { int ret, i; Loading Loading @@ -1492,6 +1574,8 @@ static int dcc_probe(struct platform_device *pdev) if (ret) goto err; dcc_configure_list(drvdata, pdev->dev.of_node); return 0; err: return ret; Loading Loading @@ -1527,13 +1611,7 @@ static int __init dcc_init(void) { return platform_driver_register(&dcc_driver); } module_init(dcc_init); static void __exit dcc_exit(void) { platform_driver_unregister(&dcc_driver); } module_exit(dcc_exit); pure_initcall(dcc_init); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("MSM data capture and compare engine");
include/dt-bindings/soc/qcom,dcc_v2.h 0 → 100644 +20 −0 Original line number Diff line number Diff line /* Copyright (c) 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef __DT_BINDINGS_QCOM_DCC_V2_H #define __DT_BINDINGS_QCOM_DCC_V2_H #define DCC_READ 0 #define DCC_WRITE 1 #define DCC_LOOP 2 #endif