Loading drivers/hwtracing/coresight/Kconfig +8 −0 Original line number Diff line number Diff line Loading @@ -155,6 +155,14 @@ config CORESIGHT_TPDM_DEFAULT_ENABLE If unsure, say 'N' here to avoid potential power and performance penalty. config CORESIGHT_QPDI bool "CoreSight PMIC debug interface support" help This driver provides support for controlling the PMIC debug interface feature. When enabled via sysfs it allows disagnostic access to the PMIC. Similarly this debug feature can be disabled via sysfs which prevents debug dongle detection. config CORESIGHT_HWEVENT bool "CoreSight Hardware Event driver" depends on CORESIGHT_STM Loading drivers/hwtracing/coresight/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ obj-$(CONFIG_CORESIGHT_TPDA) += coresight-tpda.o obj-$(CONFIG_CORESIGHT_TPDM) += coresight-tpdm.o obj-$(CONFIG_CORESIGHT_EVENT) += coresight-event.o obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o obj-$(CONFIG_CORESIGHT_QPDI) += coresight-qpdi.o obj-$(CONFIG_CORESIGHT_CSR) += coresight-csr.o obj-$(CONFIG_CORESIGHT_HWEVENT) += coresight-hwevent.o obj-$(CONFIG_CORESIGHT_DUMMY) += coresight-dummy.o Loading drivers/hwtracing/coresight/coresight-funnel.c +2 −1 Original line number Diff line number Diff line /* Copyright (c) 2011-2012, 2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2011-2012, 2017, 2020, The Linux Foundation. All rights reserved. * * Description: CoreSight Funnel driver * Loading Loading @@ -253,6 +253,7 @@ static int funnel_probe(struct amba_device *adev, const struct amba_id *id) if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); dev_info(drvdata->dev, "FUNNEL initialized\n"); return 0; } Loading drivers/hwtracing/coresight/coresight-qpdi.c 0 → 100644 +407 −0 Original line number Diff line number Diff line /* Copyright (c) 2014-2016, 2020, 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. */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/device.h> #include <linux/platform_device.h> #include <linux/io.h> #include <linux/err.h> #include <linux/slab.h> #include <linux/mutex.h> #include <linux/coresight.h> #include <linux/of.h> #include <linux/gpio.h> #include <linux/of_gpio.h> #include <linux/regulator/consumer.h> #include "coresight-priv.h" #define qpdi_writel(drvdata, val, off) __raw_writel((val), drvdata->base + off) #define qpdi_readl(drvdata, off) __raw_readl(drvdata->base + off) #define QPDI_DISABLE_CFG (0x0) static int boot_enable; module_param_named( boot_enable, boot_enable, int, 0444 ); struct qpdi_drvdata { void __iomem *base; struct device *dev; struct coresight_device *csdev; struct mutex mutex; struct regulator *reg; unsigned int reg_low; unsigned int reg_high; unsigned int reg_lpm; unsigned int reg_hpm; struct regulator *reg_io; unsigned int reg_low_io; unsigned int reg_high_io; unsigned int reg_lpm_io; unsigned int reg_hpm_io; int pmic_gpio_vote; bool skip_ldo; bool enable; }; static int qpdi_reg_set_optimum_mode(struct regulator *reg, unsigned int reg_hpm) { if (regulator_count_voltages(reg) <= 0) return 0; return regulator_set_load(reg, reg_hpm); } static int qpdi_reg_set_voltage(struct regulator *reg, unsigned int reg_low, unsigned int reg_high) { if (regulator_count_voltages(reg) <= 0) return 0; return regulator_set_voltage(reg, reg_low, reg_high); } static int __qpdi_enable(struct qpdi_drvdata *drvdata) { int ret; if (!drvdata->reg || !drvdata->reg_io) return -EINVAL; ret = qpdi_reg_set_optimum_mode(drvdata->reg, drvdata->reg_hpm); if (ret < 0) return ret; ret = qpdi_reg_set_voltage(drvdata->reg, drvdata->reg_low, drvdata->reg_high); if (ret) goto err0; ret = regulator_enable(drvdata->reg); if (ret) goto err1; ret = qpdi_reg_set_optimum_mode(drvdata->reg_io, drvdata->reg_hpm_io); if (ret < 0) goto err2; ret = qpdi_reg_set_voltage(drvdata->reg_io, drvdata->reg_low_io, drvdata->reg_high_io); if (ret) goto err3; ret = regulator_enable(drvdata->reg_io); if (ret) goto err4; return 0; err4: qpdi_reg_set_voltage(drvdata->reg_io, 0, drvdata->reg_high_io); err3: qpdi_reg_set_optimum_mode(drvdata->reg_io, 0); err2: regulator_disable(drvdata->reg); err1: qpdi_reg_set_voltage(drvdata->reg, 0, drvdata->reg_high); err0: qpdi_reg_set_optimum_mode(drvdata->reg, 0); return ret; } static int qpdi_enable(struct qpdi_drvdata *drvdata) { int ret; mutex_lock(&drvdata->mutex); if (drvdata->enable) goto out; if (!drvdata->skip_ldo) { ret = __qpdi_enable(drvdata); if (ret) goto err; } qpdi_writel(drvdata, 0x2, QPDI_DISABLE_CFG); drvdata->enable = true; dev_info(drvdata->dev, "qpdi enabled\n"); out: mutex_unlock(&drvdata->mutex); return 0; err: mutex_unlock(&drvdata->mutex); return ret; } static void __qpdi_disable(struct qpdi_drvdata *drvdata) { regulator_disable(drvdata->reg); qpdi_reg_set_voltage(drvdata->reg, 0, drvdata->reg_high); qpdi_reg_set_optimum_mode(drvdata->reg, 0); regulator_disable(drvdata->reg_io); qpdi_reg_set_voltage(drvdata->reg_io, 0, drvdata->reg_high_io); qpdi_reg_set_optimum_mode(drvdata->reg_io, 0); } static void qpdi_disable(struct qpdi_drvdata *drvdata) { mutex_lock(&drvdata->mutex); if (!drvdata->enable) { mutex_unlock(&drvdata->mutex); return; } qpdi_writel(drvdata, 0x3, QPDI_DISABLE_CFG); if (!drvdata->skip_ldo) __qpdi_disable(drvdata); drvdata->enable = false; mutex_unlock(&drvdata->mutex); dev_info(drvdata->dev, "qpdi disabled\n"); } static ssize_t qpdi_show_enable(struct device *dev, struct device_attribute *attr, char *buf) { struct qpdi_drvdata *drvdata = dev_get_drvdata(dev->parent); unsigned long val = drvdata->enable; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } static ssize_t qpdi_store_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct qpdi_drvdata *drvdata = dev_get_drvdata(dev->parent); unsigned long val; int ret = 0; if (kstrtoul(buf, 16, &val)) return -EINVAL; if (val) ret = qpdi_enable(drvdata); else qpdi_disable(drvdata); if (ret) return ret; return size; } static DEVICE_ATTR(enable, 0644, qpdi_show_enable, qpdi_store_enable); static struct attribute *qpdi_attrs[] = { &dev_attr_enable.attr, NULL, }; static struct attribute_group qpdi_attr_grp = { .attrs = qpdi_attrs, }; static const struct attribute_group *qpdi_attr_grps[] = { &qpdi_attr_grp, NULL, }; static int qpdi_parse_of_data(struct platform_device *pdev, struct qpdi_drvdata *drvdata) { struct device_node *node = pdev->dev.of_node; struct device_node *reg_node = NULL; struct device *dev = &pdev->dev; const __be32 *prop; int len, ret; drvdata->skip_ldo = of_property_read_bool(node, "qcom,skip-ldo"); if (drvdata->skip_ldo) return 0; drvdata->pmic_gpio_vote = of_get_named_gpio(pdev->dev.of_node, "qcom,pmic-carddetect-gpio", 0); if (drvdata->pmic_gpio_vote < 0) dev_info(dev, "QPDI hotplug card detection is not supported\n"); else { ret = gpio_request(drvdata->pmic_gpio_vote, "qpdi_gpio_hp"); if (ret) { dev_err(dev, "failed to allocate the GPIO\n"); return ret; } ret = gpio_direction_input(drvdata->pmic_gpio_vote); if (ret) { dev_err(dev, "failed to set the gpio to input\n"); gpio_free(drvdata->pmic_gpio_vote); return ret; } drvdata->skip_ldo = 1; return 0; } reg_node = of_parse_phandle(node, "vdd-supply", 0); if (reg_node) { drvdata->reg = devm_regulator_get(dev, "vdd"); if (IS_ERR(drvdata->reg)) return PTR_ERR(drvdata->reg); prop = of_get_property(node, "qcom,vdd-voltage-level", &len); if (!prop || (len != (2 * sizeof(__be32)))) { dev_err(dev, "sdc voltage levels not specified\n"); } else { drvdata->reg_low = be32_to_cpup(&prop[0]); drvdata->reg_high = be32_to_cpup(&prop[1]); } prop = of_get_property(node, "qcom,vdd-current-level", &len); if (!prop || (len != (2 * sizeof(__be32)))) { dev_err(dev, "sdc current levels not specified\n"); } else { drvdata->reg_lpm = be32_to_cpup(&prop[0]); drvdata->reg_hpm = be32_to_cpup(&prop[1]); } of_node_put(reg_node); } else { dev_err(dev, "sdc voltage supply not specified or available\n"); } reg_node = of_parse_phandle(node, "vdd-io-supply", 0); if (reg_node) { drvdata->reg_io = devm_regulator_get(dev, "vdd-io"); if (IS_ERR(drvdata->reg_io)) return PTR_ERR(drvdata->reg_io); prop = of_get_property(node, "qcom,vdd-io-voltage-level", &len); if (!prop || (len != (2 * sizeof(__be32)))) { dev_err(dev, "sdc io voltage levels not specified\n"); } else { drvdata->reg_low_io = be32_to_cpup(&prop[0]); drvdata->reg_high_io = be32_to_cpup(&prop[1]); } prop = of_get_property(node, "qcom,vdd-io-current-level", &len); if (!prop || (len != (2 * sizeof(__be32)))) { dev_err(dev, "sdc io current levels not specified\n"); } else { drvdata->reg_lpm_io = be32_to_cpup(&prop[0]); drvdata->reg_hpm_io = be32_to_cpup(&prop[1]); } of_node_put(reg_node); } else { dev_err(dev, "sdc io voltage supply not specified or available\n"); } return 0; } static int qpdi_probe(struct platform_device *pdev) { int ret; struct device *dev = &pdev->dev; struct coresight_platform_data *pdata; struct qpdi_drvdata *drvdata; struct resource *res; struct coresight_desc *desc; dev_info(dev, "CoreSight QPDI start\n"); pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node); if (IS_ERR(pdata)) return PTR_ERR(pdata); pdev->dev.platform_data = pdata; drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; drvdata->dev = &pdev->dev; platform_set_drvdata(pdev, drvdata); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qpdi-base"); if (!res) return -ENODEV; drvdata->base = devm_ioremap(dev, res->start, resource_size(res)); if (!drvdata->base) return -ENOMEM; mutex_init(&drvdata->mutex); ret = qpdi_parse_of_data(pdev, drvdata); if (ret) return ret; desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); if (!desc) return -ENOMEM; desc->type = CORESIGHT_DEV_TYPE_NONE; desc->pdata = pdev->dev.platform_data; desc->dev = &pdev->dev; desc->groups = qpdi_attr_grps; drvdata->csdev = coresight_register(desc); if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); if (boot_enable) qpdi_enable(drvdata); dev_info(dev, "CoreSight QPDI driver initialized\n"); return 0; } static int qpdi_remove(struct platform_device *pdev) { struct qpdi_drvdata *drvdata = platform_get_drvdata(pdev); if (drvdata->pmic_gpio_vote > -1) gpio_free(drvdata->pmic_gpio_vote); coresight_unregister(drvdata->csdev); return 0; } static const struct of_device_id qpdi_match[] = { {.compatible = "qcom,coresight-qpdi"}, {} }; static struct platform_driver qpdi_driver = { .probe = qpdi_probe, .remove = qpdi_remove, .driver = { .name = "coresight-qpdi", .owner = THIS_MODULE, .of_match_table = qpdi_match, }, }; static int __init qpdi_init(void) { return platform_driver_register(&qpdi_driver); } module_init(qpdi_init); static void __exit qpdi_exit(void) { platform_driver_unregister(&qpdi_driver); } module_exit(qpdi_exit); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("CoreSight QPDI driver"); Loading
drivers/hwtracing/coresight/Kconfig +8 −0 Original line number Diff line number Diff line Loading @@ -155,6 +155,14 @@ config CORESIGHT_TPDM_DEFAULT_ENABLE If unsure, say 'N' here to avoid potential power and performance penalty. config CORESIGHT_QPDI bool "CoreSight PMIC debug interface support" help This driver provides support for controlling the PMIC debug interface feature. When enabled via sysfs it allows disagnostic access to the PMIC. Similarly this debug feature can be disabled via sysfs which prevents debug dongle detection. config CORESIGHT_HWEVENT bool "CoreSight Hardware Event driver" depends on CORESIGHT_STM Loading
drivers/hwtracing/coresight/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ obj-$(CONFIG_CORESIGHT_TPDA) += coresight-tpda.o obj-$(CONFIG_CORESIGHT_TPDM) += coresight-tpdm.o obj-$(CONFIG_CORESIGHT_EVENT) += coresight-event.o obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o obj-$(CONFIG_CORESIGHT_QPDI) += coresight-qpdi.o obj-$(CONFIG_CORESIGHT_CSR) += coresight-csr.o obj-$(CONFIG_CORESIGHT_HWEVENT) += coresight-hwevent.o obj-$(CONFIG_CORESIGHT_DUMMY) += coresight-dummy.o Loading
drivers/hwtracing/coresight/coresight-funnel.c +2 −1 Original line number Diff line number Diff line /* Copyright (c) 2011-2012, 2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2011-2012, 2017, 2020, The Linux Foundation. All rights reserved. * * Description: CoreSight Funnel driver * Loading Loading @@ -253,6 +253,7 @@ static int funnel_probe(struct amba_device *adev, const struct amba_id *id) if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); dev_info(drvdata->dev, "FUNNEL initialized\n"); return 0; } Loading
drivers/hwtracing/coresight/coresight-qpdi.c 0 → 100644 +407 −0 Original line number Diff line number Diff line /* Copyright (c) 2014-2016, 2020, 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. */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/device.h> #include <linux/platform_device.h> #include <linux/io.h> #include <linux/err.h> #include <linux/slab.h> #include <linux/mutex.h> #include <linux/coresight.h> #include <linux/of.h> #include <linux/gpio.h> #include <linux/of_gpio.h> #include <linux/regulator/consumer.h> #include "coresight-priv.h" #define qpdi_writel(drvdata, val, off) __raw_writel((val), drvdata->base + off) #define qpdi_readl(drvdata, off) __raw_readl(drvdata->base + off) #define QPDI_DISABLE_CFG (0x0) static int boot_enable; module_param_named( boot_enable, boot_enable, int, 0444 ); struct qpdi_drvdata { void __iomem *base; struct device *dev; struct coresight_device *csdev; struct mutex mutex; struct regulator *reg; unsigned int reg_low; unsigned int reg_high; unsigned int reg_lpm; unsigned int reg_hpm; struct regulator *reg_io; unsigned int reg_low_io; unsigned int reg_high_io; unsigned int reg_lpm_io; unsigned int reg_hpm_io; int pmic_gpio_vote; bool skip_ldo; bool enable; }; static int qpdi_reg_set_optimum_mode(struct regulator *reg, unsigned int reg_hpm) { if (regulator_count_voltages(reg) <= 0) return 0; return regulator_set_load(reg, reg_hpm); } static int qpdi_reg_set_voltage(struct regulator *reg, unsigned int reg_low, unsigned int reg_high) { if (regulator_count_voltages(reg) <= 0) return 0; return regulator_set_voltage(reg, reg_low, reg_high); } static int __qpdi_enable(struct qpdi_drvdata *drvdata) { int ret; if (!drvdata->reg || !drvdata->reg_io) return -EINVAL; ret = qpdi_reg_set_optimum_mode(drvdata->reg, drvdata->reg_hpm); if (ret < 0) return ret; ret = qpdi_reg_set_voltage(drvdata->reg, drvdata->reg_low, drvdata->reg_high); if (ret) goto err0; ret = regulator_enable(drvdata->reg); if (ret) goto err1; ret = qpdi_reg_set_optimum_mode(drvdata->reg_io, drvdata->reg_hpm_io); if (ret < 0) goto err2; ret = qpdi_reg_set_voltage(drvdata->reg_io, drvdata->reg_low_io, drvdata->reg_high_io); if (ret) goto err3; ret = regulator_enable(drvdata->reg_io); if (ret) goto err4; return 0; err4: qpdi_reg_set_voltage(drvdata->reg_io, 0, drvdata->reg_high_io); err3: qpdi_reg_set_optimum_mode(drvdata->reg_io, 0); err2: regulator_disable(drvdata->reg); err1: qpdi_reg_set_voltage(drvdata->reg, 0, drvdata->reg_high); err0: qpdi_reg_set_optimum_mode(drvdata->reg, 0); return ret; } static int qpdi_enable(struct qpdi_drvdata *drvdata) { int ret; mutex_lock(&drvdata->mutex); if (drvdata->enable) goto out; if (!drvdata->skip_ldo) { ret = __qpdi_enable(drvdata); if (ret) goto err; } qpdi_writel(drvdata, 0x2, QPDI_DISABLE_CFG); drvdata->enable = true; dev_info(drvdata->dev, "qpdi enabled\n"); out: mutex_unlock(&drvdata->mutex); return 0; err: mutex_unlock(&drvdata->mutex); return ret; } static void __qpdi_disable(struct qpdi_drvdata *drvdata) { regulator_disable(drvdata->reg); qpdi_reg_set_voltage(drvdata->reg, 0, drvdata->reg_high); qpdi_reg_set_optimum_mode(drvdata->reg, 0); regulator_disable(drvdata->reg_io); qpdi_reg_set_voltage(drvdata->reg_io, 0, drvdata->reg_high_io); qpdi_reg_set_optimum_mode(drvdata->reg_io, 0); } static void qpdi_disable(struct qpdi_drvdata *drvdata) { mutex_lock(&drvdata->mutex); if (!drvdata->enable) { mutex_unlock(&drvdata->mutex); return; } qpdi_writel(drvdata, 0x3, QPDI_DISABLE_CFG); if (!drvdata->skip_ldo) __qpdi_disable(drvdata); drvdata->enable = false; mutex_unlock(&drvdata->mutex); dev_info(drvdata->dev, "qpdi disabled\n"); } static ssize_t qpdi_show_enable(struct device *dev, struct device_attribute *attr, char *buf) { struct qpdi_drvdata *drvdata = dev_get_drvdata(dev->parent); unsigned long val = drvdata->enable; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } static ssize_t qpdi_store_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct qpdi_drvdata *drvdata = dev_get_drvdata(dev->parent); unsigned long val; int ret = 0; if (kstrtoul(buf, 16, &val)) return -EINVAL; if (val) ret = qpdi_enable(drvdata); else qpdi_disable(drvdata); if (ret) return ret; return size; } static DEVICE_ATTR(enable, 0644, qpdi_show_enable, qpdi_store_enable); static struct attribute *qpdi_attrs[] = { &dev_attr_enable.attr, NULL, }; static struct attribute_group qpdi_attr_grp = { .attrs = qpdi_attrs, }; static const struct attribute_group *qpdi_attr_grps[] = { &qpdi_attr_grp, NULL, }; static int qpdi_parse_of_data(struct platform_device *pdev, struct qpdi_drvdata *drvdata) { struct device_node *node = pdev->dev.of_node; struct device_node *reg_node = NULL; struct device *dev = &pdev->dev; const __be32 *prop; int len, ret; drvdata->skip_ldo = of_property_read_bool(node, "qcom,skip-ldo"); if (drvdata->skip_ldo) return 0; drvdata->pmic_gpio_vote = of_get_named_gpio(pdev->dev.of_node, "qcom,pmic-carddetect-gpio", 0); if (drvdata->pmic_gpio_vote < 0) dev_info(dev, "QPDI hotplug card detection is not supported\n"); else { ret = gpio_request(drvdata->pmic_gpio_vote, "qpdi_gpio_hp"); if (ret) { dev_err(dev, "failed to allocate the GPIO\n"); return ret; } ret = gpio_direction_input(drvdata->pmic_gpio_vote); if (ret) { dev_err(dev, "failed to set the gpio to input\n"); gpio_free(drvdata->pmic_gpio_vote); return ret; } drvdata->skip_ldo = 1; return 0; } reg_node = of_parse_phandle(node, "vdd-supply", 0); if (reg_node) { drvdata->reg = devm_regulator_get(dev, "vdd"); if (IS_ERR(drvdata->reg)) return PTR_ERR(drvdata->reg); prop = of_get_property(node, "qcom,vdd-voltage-level", &len); if (!prop || (len != (2 * sizeof(__be32)))) { dev_err(dev, "sdc voltage levels not specified\n"); } else { drvdata->reg_low = be32_to_cpup(&prop[0]); drvdata->reg_high = be32_to_cpup(&prop[1]); } prop = of_get_property(node, "qcom,vdd-current-level", &len); if (!prop || (len != (2 * sizeof(__be32)))) { dev_err(dev, "sdc current levels not specified\n"); } else { drvdata->reg_lpm = be32_to_cpup(&prop[0]); drvdata->reg_hpm = be32_to_cpup(&prop[1]); } of_node_put(reg_node); } else { dev_err(dev, "sdc voltage supply not specified or available\n"); } reg_node = of_parse_phandle(node, "vdd-io-supply", 0); if (reg_node) { drvdata->reg_io = devm_regulator_get(dev, "vdd-io"); if (IS_ERR(drvdata->reg_io)) return PTR_ERR(drvdata->reg_io); prop = of_get_property(node, "qcom,vdd-io-voltage-level", &len); if (!prop || (len != (2 * sizeof(__be32)))) { dev_err(dev, "sdc io voltage levels not specified\n"); } else { drvdata->reg_low_io = be32_to_cpup(&prop[0]); drvdata->reg_high_io = be32_to_cpup(&prop[1]); } prop = of_get_property(node, "qcom,vdd-io-current-level", &len); if (!prop || (len != (2 * sizeof(__be32)))) { dev_err(dev, "sdc io current levels not specified\n"); } else { drvdata->reg_lpm_io = be32_to_cpup(&prop[0]); drvdata->reg_hpm_io = be32_to_cpup(&prop[1]); } of_node_put(reg_node); } else { dev_err(dev, "sdc io voltage supply not specified or available\n"); } return 0; } static int qpdi_probe(struct platform_device *pdev) { int ret; struct device *dev = &pdev->dev; struct coresight_platform_data *pdata; struct qpdi_drvdata *drvdata; struct resource *res; struct coresight_desc *desc; dev_info(dev, "CoreSight QPDI start\n"); pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node); if (IS_ERR(pdata)) return PTR_ERR(pdata); pdev->dev.platform_data = pdata; drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; drvdata->dev = &pdev->dev; platform_set_drvdata(pdev, drvdata); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qpdi-base"); if (!res) return -ENODEV; drvdata->base = devm_ioremap(dev, res->start, resource_size(res)); if (!drvdata->base) return -ENOMEM; mutex_init(&drvdata->mutex); ret = qpdi_parse_of_data(pdev, drvdata); if (ret) return ret; desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); if (!desc) return -ENOMEM; desc->type = CORESIGHT_DEV_TYPE_NONE; desc->pdata = pdev->dev.platform_data; desc->dev = &pdev->dev; desc->groups = qpdi_attr_grps; drvdata->csdev = coresight_register(desc); if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); if (boot_enable) qpdi_enable(drvdata); dev_info(dev, "CoreSight QPDI driver initialized\n"); return 0; } static int qpdi_remove(struct platform_device *pdev) { struct qpdi_drvdata *drvdata = platform_get_drvdata(pdev); if (drvdata->pmic_gpio_vote > -1) gpio_free(drvdata->pmic_gpio_vote); coresight_unregister(drvdata->csdev); return 0; } static const struct of_device_id qpdi_match[] = { {.compatible = "qcom,coresight-qpdi"}, {} }; static struct platform_driver qpdi_driver = { .probe = qpdi_probe, .remove = qpdi_remove, .driver = { .name = "coresight-qpdi", .owner = THIS_MODULE, .of_match_table = qpdi_match, }, }; static int __init qpdi_init(void) { return platform_driver_register(&qpdi_driver); } module_init(qpdi_init); static void __exit qpdi_exit(void) { platform_driver_unregister(&qpdi_driver); } module_exit(qpdi_exit); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("CoreSight QPDI driver");