From e5f71d60ff167d0caa491659d65551a55ea6b406 Mon Sep 17 00:00:00 2001 From: Pavel Begunkov Date: Wed, 8 Sep 2021 20:10:38 +0100 Subject: /dev/mem: nowait zero/null ops Make read_iter_zero() to honor IOCB_NOWAIT, so /dev/zero can be advertised as FMODE_NOWAIT. It's useful for io_uring, which needs it to apply certain optimisations when doing I/O against the device. Set FMODE_NOWAIT for /dev/null as well, it never waits and therefore trivially meets the criteria. Reviewed-by: Jens Axboe Signed-off-by: Pavel Begunkov Link: https://lore.kernel.org/r/f11090f97ddc2b2ce49ea1211258658ddfbc5563.1631127867.git.asml.silence@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/char/mem.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 1c596b5cdb27..cc296f0823bd 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -495,6 +495,10 @@ static ssize_t read_iter_zero(struct kiocb *iocb, struct iov_iter *iter) written += n; if (signal_pending(current)) return written ? written : -ERESTARTSYS; + if (!need_resched()) + continue; + if (iocb->ki_flags & IOCB_NOWAIT) + return written ? written : -EAGAIN; cond_resched(); } return written; @@ -696,11 +700,11 @@ static const struct memdev { #ifdef CONFIG_DEVMEM [DEVMEM_MINOR] = { "mem", 0, &mem_fops, FMODE_UNSIGNED_OFFSET }, #endif - [3] = { "null", 0666, &null_fops, 0 }, + [3] = { "null", 0666, &null_fops, FMODE_NOWAIT }, #ifdef CONFIG_DEVPORT [4] = { "port", 0, &port_fops, 0 }, #endif - [5] = { "zero", 0666, &zero_fops, 0 }, + [5] = { "zero", 0666, &zero_fops, FMODE_NOWAIT }, [7] = { "full", 0666, &full_fops, 0 }, [8] = { "random", 0666, &random_fops, 0 }, [9] = { "urandom", 0666, &urandom_fops, 0 }, -- cgit v1.2.3-70-g09d2 From 83c510568ec52f8eb00abf45bf35fbae9b97390e Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Tue, 31 Aug 2021 22:16:30 +0200 Subject: misc: rtsx: Remove usage of the deprecated "pci-dma-compat.h" API In [1], Christoph Hellwig has proposed to remove the wrappers in include/linux/pci-dma-compat.h. Some reasons why this API should be removed have been given by Julia Lawall in [2]. Finally, Arnd Bergmann reminded that the documentation was updated 11 years ago to only describe the modern linux/dma-mapping.h interfaces and mark the old bus-specific ones as no longer recommended, see commit 216bf58f4092 ("Documentation: convert PCI-DMA-mapping.txt to use the generic DMA API"). A coccinelle script has been used to perform the needed transformation Only relevant parts are given below. @@ expression e1, e2; @@ - pci_set_dma_mask(e1, e2) + dma_set_mask(&e1->dev, e2) [1]: https://lore.kernel.org/kernel-janitors/20200421081257.GA131897@infradead.org/ [2]: https://lore.kernel.org/kernel-janitors/alpine.DEB.2.22.394.2007120902170.2424@hadrien/ Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/95752079d0e2bb1613f0f3a53f13f642f5c72572.1630440769.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cardreader/rtsx_pcr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c index baf83594a01d..8c72eb590f79 100644 --- a/drivers/misc/cardreader/rtsx_pcr.c +++ b/drivers/misc/cardreader/rtsx_pcr.c @@ -1536,7 +1536,7 @@ static int rtsx_pci_probe(struct pci_dev *pcidev, pci_name(pcidev), (int)pcidev->vendor, (int)pcidev->device, (int)pcidev->revision); - ret = pci_set_dma_mask(pcidev, DMA_BIT_MASK(32)); + ret = dma_set_mask(&pcidev->dev, DMA_BIT_MASK(32)); if (ret < 0) return ret; -- cgit v1.2.3-70-g09d2 From a34993a2791c507d6941582f37c9776257662523 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 3 Sep 2021 11:28:33 +0200 Subject: misc: hisi_hikey_usb: change the DT schema As there's no upstream DT bindings for this driver, let's update its DT schema, while it is not too late. While here, add error messages, in order to help discovering problems during probing time. Signed-off-by: Mauro Carvalho Chehab Link: https://lore.kernel.org/r/746237a6bdbb84d4271a77994c82bccf524680c7.1630659949.git.mchehab+huawei@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/hisi_hikey_usb.c | 119 ++++++++++++++++++++++-------------------- 1 file changed, 63 insertions(+), 56 deletions(-) diff --git a/drivers/misc/hisi_hikey_usb.c b/drivers/misc/hisi_hikey_usb.c index 989d7d129469..2165ec35a343 100644 --- a/drivers/misc/hisi_hikey_usb.c +++ b/drivers/misc/hisi_hikey_usb.c @@ -34,7 +34,6 @@ struct hisi_hikey_usb { struct device *dev; struct gpio_desc *otg_switch; struct gpio_desc *typec_vbus; - struct gpio_desc *hub_vbus; struct gpio_desc *reset; struct regulator *regulator; @@ -54,9 +53,6 @@ static void hub_power_ctrl(struct hisi_hikey_usb *hisi_hikey_usb, int value) { int ret, status; - if (hisi_hikey_usb->hub_vbus) - gpiod_set_value_cansleep(hisi_hikey_usb->hub_vbus, value); - if (!hisi_hikey_usb->regulator) return; @@ -147,75 +143,50 @@ static int hub_usb_role_switch_set(struct usb_role_switch *sw, enum usb_role rol return 0; } -static int hisi_hikey_usb_parse_kirin970(struct platform_device *pdev, +static int hisi_hikey_usb_of_role_switch(struct platform_device *pdev, struct hisi_hikey_usb *hisi_hikey_usb) -{ - struct regulator *regulator; - - regulator = devm_regulator_get(&pdev->dev, "hub-vdd"); - if (IS_ERR(regulator)) { - if (PTR_ERR(regulator) == -EPROBE_DEFER) { - dev_info(&pdev->dev, - "waiting for hub-vdd-supply to be probed\n"); - return PTR_ERR(regulator); - } - dev_err(&pdev->dev, - "get hub-vdd-supply failed with error %ld\n", - PTR_ERR(regulator)); - return PTR_ERR(regulator); - } - hisi_hikey_usb->regulator = regulator; - - hisi_hikey_usb->reset = devm_gpiod_get(&pdev->dev, "hub_reset_en_gpio", - GPIOD_OUT_HIGH); - return PTR_ERR_OR_ZERO(hisi_hikey_usb->reset); -} - -static int hisi_hikey_usb_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct hisi_hikey_usb *hisi_hikey_usb; struct usb_role_switch_desc hub_role_switch = {NULL}; - int ret; - hisi_hikey_usb = devm_kzalloc(dev, sizeof(*hisi_hikey_usb), GFP_KERNEL); - if (!hisi_hikey_usb) - return -ENOMEM; - - hisi_hikey_usb->dev = &pdev->dev; + if (!device_property_read_bool(dev, "usb-role-switch")) + return 0; hisi_hikey_usb->otg_switch = devm_gpiod_get(dev, "otg-switch", GPIOD_OUT_HIGH); - if (IS_ERR(hisi_hikey_usb->otg_switch)) + if (IS_ERR(hisi_hikey_usb->otg_switch)) { + dev_err(dev, "get otg-switch failed with error %ld\n", + PTR_ERR(hisi_hikey_usb->otg_switch)); return PTR_ERR(hisi_hikey_usb->otg_switch); + } hisi_hikey_usb->typec_vbus = devm_gpiod_get(dev, "typec-vbus", GPIOD_OUT_LOW); - if (IS_ERR(hisi_hikey_usb->typec_vbus)) + if (IS_ERR(hisi_hikey_usb->typec_vbus)) { + dev_err(dev, "get typec-vbus failed with error %ld\n", + PTR_ERR(hisi_hikey_usb->typec_vbus)); return PTR_ERR(hisi_hikey_usb->typec_vbus); + } - /* Parse Kirin 970-specific OF data */ - if (of_device_is_compatible(pdev->dev.of_node, - "hisilicon,kirin970_hikey_usbhub")) { - ret = hisi_hikey_usb_parse_kirin970(pdev, hisi_hikey_usb); - if (ret) - return ret; - } else { - /* hub-vdd33-en is optional */ - hisi_hikey_usb->hub_vbus = devm_gpiod_get_optional(dev, "hub-vdd33-en", - GPIOD_OUT_HIGH); - if (IS_ERR(hisi_hikey_usb->hub_vbus)) - return PTR_ERR(hisi_hikey_usb->hub_vbus); + hisi_hikey_usb->reset = devm_gpiod_get_optional(dev, + "hub-reset-en", + GPIOD_OUT_HIGH); + if (IS_ERR(hisi_hikey_usb->reset)) { + dev_err(dev, "get hub-reset-en failed with error %ld\n", + PTR_ERR(hisi_hikey_usb->reset)); + return PTR_ERR(hisi_hikey_usb->reset); } hisi_hikey_usb->dev_role_sw = usb_role_switch_get(dev); if (!hisi_hikey_usb->dev_role_sw) return -EPROBE_DEFER; - if (IS_ERR(hisi_hikey_usb->dev_role_sw)) + if (IS_ERR(hisi_hikey_usb->dev_role_sw)) { + dev_err(dev, "get device role switch failed with error %ld\n", + PTR_ERR(hisi_hikey_usb->dev_role_sw)); return PTR_ERR(hisi_hikey_usb->dev_role_sw); + } INIT_WORK(&hisi_hikey_usb->work, relay_set_role_switch); - mutex_init(&hisi_hikey_usb->lock); hub_role_switch.fwnode = dev_fwnode(dev); hub_role_switch.set = hub_usb_role_switch_set; @@ -225,10 +196,44 @@ static int hisi_hikey_usb_probe(struct platform_device *pdev) &hub_role_switch); if (IS_ERR(hisi_hikey_usb->hub_role_sw)) { + dev_err(dev, + "failed to register hub role with error %ld\n", + PTR_ERR(hisi_hikey_usb->hub_role_sw)); usb_role_switch_put(hisi_hikey_usb->dev_role_sw); return PTR_ERR(hisi_hikey_usb->hub_role_sw); } + return 0; +} + +static int hisi_hikey_usb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct hisi_hikey_usb *hisi_hikey_usb; + int ret; + + hisi_hikey_usb = devm_kzalloc(dev, sizeof(*hisi_hikey_usb), GFP_KERNEL); + if (!hisi_hikey_usb) + return -ENOMEM; + + hisi_hikey_usb->dev = &pdev->dev; + mutex_init(&hisi_hikey_usb->lock); + + hisi_hikey_usb->regulator = devm_regulator_get(dev, "hub-vdd"); + if (IS_ERR(hisi_hikey_usb->regulator)) { + if (PTR_ERR(hisi_hikey_usb->regulator) == -EPROBE_DEFER) { + dev_info(dev, "waiting for hub-vdd-supply\n"); + return PTR_ERR(hisi_hikey_usb->regulator); + } + dev_err(dev, "get hub-vdd-supply failed with error %ld\n", + PTR_ERR(hisi_hikey_usb->regulator)); + return PTR_ERR(hisi_hikey_usb->regulator); + } + + ret = hisi_hikey_usb_of_role_switch(pdev, hisi_hikey_usb); + if (ret) + return ret; + platform_set_drvdata(pdev, hisi_hikey_usb); return 0; @@ -238,18 +243,20 @@ static int hisi_hikey_usb_remove(struct platform_device *pdev) { struct hisi_hikey_usb *hisi_hikey_usb = platform_get_drvdata(pdev); - if (hisi_hikey_usb->hub_role_sw) + if (hisi_hikey_usb->hub_role_sw) { usb_role_switch_unregister(hisi_hikey_usb->hub_role_sw); - if (hisi_hikey_usb->dev_role_sw) - usb_role_switch_put(hisi_hikey_usb->dev_role_sw); + if (hisi_hikey_usb->dev_role_sw) + usb_role_switch_put(hisi_hikey_usb->dev_role_sw); + } else { + hub_power_ctrl(hisi_hikey_usb, HUB_VBUS_POWER_OFF); + } return 0; } static const struct of_device_id id_table_hisi_hikey_usb[] = { - { .compatible = "hisilicon,gpio_hubv1" }, - { .compatible = "hisilicon,kirin970_hikey_usbhub" }, + { .compatible = "hisilicon,usbhub" }, {} }; MODULE_DEVICE_TABLE(of, id_table_hisi_hikey_usb); -- cgit v1.2.3-70-g09d2 From 33a430419456991480cde9d8889e5a27f6049df4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 29 Aug 2021 15:43:52 +0300 Subject: pvpanic: Keep single style across modules We have different style on where we place module_*() and MODULE_*() macros. Inherit the style from the original module (now pvpanic-mmio.c). Reviewed-by: Mihai Carabas Link: https://lore.kernel.org/r/20210829124354.81653-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pvpanic/pvpanic-pci.c | 14 ++++++-------- drivers/misc/pvpanic/pvpanic.c | 3 +-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/misc/pvpanic/pvpanic-pci.c b/drivers/misc/pvpanic/pvpanic-pci.c index 741116b3d995..7d1220f4c95b 100644 --- a/drivers/misc/pvpanic/pvpanic-pci.c +++ b/drivers/misc/pvpanic/pvpanic-pci.c @@ -22,11 +22,6 @@ MODULE_AUTHOR("Mihai Carabas "); MODULE_DESCRIPTION("pvpanic device driver "); MODULE_LICENSE("GPL"); -static const struct pci_device_id pvpanic_pci_id_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_PVPANIC)}, - {} -}; - static ssize_t capability_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -99,6 +94,12 @@ static int pvpanic_pci_probe(struct pci_dev *pdev, return devm_pvpanic_probe(&pdev->dev, pi); } +static const struct pci_device_id pvpanic_pci_id_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_PVPANIC)}, + {} +}; +MODULE_DEVICE_TABLE(pci, pvpanic_pci_id_tbl); + static struct pci_driver pvpanic_pci_driver = { .name = "pvpanic-pci", .id_table = pvpanic_pci_id_tbl, @@ -107,7 +108,4 @@ static struct pci_driver pvpanic_pci_driver = { .dev_groups = pvpanic_pci_dev_groups, }, }; - -MODULE_DEVICE_TABLE(pci, pvpanic_pci_id_tbl); - module_pci_driver(pvpanic_pci_driver); diff --git a/drivers/misc/pvpanic/pvpanic.c b/drivers/misc/pvpanic/pvpanic.c index bb7aa6368538..f073c2efc3ae 100644 --- a/drivers/misc/pvpanic/pvpanic.c +++ b/drivers/misc/pvpanic/pvpanic.c @@ -101,6 +101,7 @@ static int pvpanic_init(void) return 0; } +module_init(pvpanic_init); static void pvpanic_exit(void) { @@ -108,6 +109,4 @@ static void pvpanic_exit(void) &pvpanic_panic_nb); } - -module_init(pvpanic_init); module_exit(pvpanic_exit); -- cgit v1.2.3-70-g09d2 From cc5b392d0f94f27743583140d819fa35a46899db Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 29 Aug 2021 15:43:53 +0300 Subject: pvpanic: Fix typos in the comments Fix a few spelling typos in the comments. Reviewed-by: Mihai Carabas Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210829124354.81653-2-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pvpanic/pvpanic-mmio.c | 2 +- drivers/misc/pvpanic/pvpanic.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/pvpanic/pvpanic-mmio.c b/drivers/misc/pvpanic/pvpanic-mmio.c index be4016084979..61dbff5f0065 100644 --- a/drivers/misc/pvpanic/pvpanic-mmio.c +++ b/drivers/misc/pvpanic/pvpanic-mmio.c @@ -100,7 +100,7 @@ static int pvpanic_mmio_probe(struct platform_device *pdev) pi->base = base; pi->capability = PVPANIC_PANICKED | PVPANIC_CRASH_LOADED; - /* initlize capability by RDPT */ + /* initialize capability by RDPT */ pi->capability &= ioread8(base); pi->events = pi->capability; diff --git a/drivers/misc/pvpanic/pvpanic.c b/drivers/misc/pvpanic/pvpanic.c index f073c2efc3ae..6ffc8c2e5b04 100644 --- a/drivers/misc/pvpanic/pvpanic.c +++ b/drivers/misc/pvpanic/pvpanic.c @@ -58,7 +58,7 @@ pvpanic_panic_notify(struct notifier_block *nb, unsigned long code, static struct notifier_block pvpanic_panic_nb = { .notifier_call = pvpanic_panic_notify, - .priority = 1, /* let this called before broken drm_fb_helper */ + .priority = 1, /* let this called before broken drm_fb_helper() */ }; static void pvpanic_remove(void *param) -- cgit v1.2.3-70-g09d2 From 84b0f12a953c4feff9994b1c4583ed18b441f482 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 29 Aug 2021 15:43:54 +0300 Subject: pvpanic: Indentation fixes here and there 1) replace double spaces with single; 2) relax line width limitation a bit. Reviewed-by: Mihai Carabas Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210829124354.81653-3-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pvpanic/pvpanic-mmio.c | 7 +++---- drivers/misc/pvpanic/pvpanic-pci.c | 12 +++++------- drivers/misc/pvpanic/pvpanic.c | 11 ++++------- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/drivers/misc/pvpanic/pvpanic-mmio.c b/drivers/misc/pvpanic/pvpanic-mmio.c index 61dbff5f0065..eb97167c03fb 100644 --- a/drivers/misc/pvpanic/pvpanic-mmio.c +++ b/drivers/misc/pvpanic/pvpanic-mmio.c @@ -24,8 +24,7 @@ MODULE_AUTHOR("Hu Tao "); MODULE_DESCRIPTION("pvpanic-mmio device driver"); MODULE_LICENSE("GPL"); -static ssize_t capability_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t capability_show(struct device *dev, struct device_attribute *attr, char *buf) { struct pvpanic_instance *pi = dev_get_drvdata(dev); @@ -33,14 +32,14 @@ static ssize_t capability_show(struct device *dev, } static DEVICE_ATTR_RO(capability); -static ssize_t events_show(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t events_show(struct device *dev, struct device_attribute *attr, char *buf) { struct pvpanic_instance *pi = dev_get_drvdata(dev); return sysfs_emit(buf, "%x\n", pi->events); } -static ssize_t events_store(struct device *dev, struct device_attribute *attr, +static ssize_t events_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct pvpanic_instance *pi = dev_get_drvdata(dev); diff --git a/drivers/misc/pvpanic/pvpanic-pci.c b/drivers/misc/pvpanic/pvpanic-pci.c index 7d1220f4c95b..07eddb5ea30f 100644 --- a/drivers/misc/pvpanic/pvpanic-pci.c +++ b/drivers/misc/pvpanic/pvpanic-pci.c @@ -19,11 +19,10 @@ #define PCI_DEVICE_ID_REDHAT_PVPANIC 0x0011 MODULE_AUTHOR("Mihai Carabas "); -MODULE_DESCRIPTION("pvpanic device driver "); +MODULE_DESCRIPTION("pvpanic device driver"); MODULE_LICENSE("GPL"); -static ssize_t capability_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t capability_show(struct device *dev, struct device_attribute *attr, char *buf) { struct pvpanic_instance *pi = dev_get_drvdata(dev); @@ -31,14 +30,14 @@ static ssize_t capability_show(struct device *dev, } static DEVICE_ATTR_RO(capability); -static ssize_t events_show(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t events_show(struct device *dev, struct device_attribute *attr, char *buf) { struct pvpanic_instance *pi = dev_get_drvdata(dev); return sysfs_emit(buf, "%x\n", pi->events); } -static ssize_t events_store(struct device *dev, struct device_attribute *attr, +static ssize_t events_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct pvpanic_instance *pi = dev_get_drvdata(dev); @@ -65,8 +64,7 @@ static struct attribute *pvpanic_pci_dev_attrs[] = { }; ATTRIBUTE_GROUPS(pvpanic_pci_dev); -static int pvpanic_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int pvpanic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct pvpanic_instance *pi; void __iomem *base; diff --git a/drivers/misc/pvpanic/pvpanic.c b/drivers/misc/pvpanic/pvpanic.c index 6ffc8c2e5b04..4b8f1c7d726d 100644 --- a/drivers/misc/pvpanic/pvpanic.c +++ b/drivers/misc/pvpanic/pvpanic.c @@ -23,7 +23,7 @@ #include "pvpanic.h" MODULE_AUTHOR("Mihai Carabas "); -MODULE_DESCRIPTION("pvpanic device driver "); +MODULE_DESCRIPTION("pvpanic device driver"); MODULE_LICENSE("GPL"); static struct list_head pvpanic_list; @@ -43,8 +43,7 @@ pvpanic_send_event(unsigned int event) } static int -pvpanic_panic_notify(struct notifier_block *nb, unsigned long code, - void *unused) +pvpanic_panic_notify(struct notifier_block *nb, unsigned long code, void *unused) { unsigned int event = PVPANIC_PANICKED; @@ -96,8 +95,7 @@ static int pvpanic_init(void) INIT_LIST_HEAD(&pvpanic_list); spin_lock_init(&pvpanic_lock); - atomic_notifier_chain_register(&panic_notifier_list, - &pvpanic_panic_nb); + atomic_notifier_chain_register(&panic_notifier_list, &pvpanic_panic_nb); return 0; } @@ -105,8 +103,7 @@ module_init(pvpanic_init); static void pvpanic_exit(void) { - atomic_notifier_chain_unregister(&panic_notifier_list, - &pvpanic_panic_nb); + atomic_notifier_chain_unregister(&panic_notifier_list, &pvpanic_panic_nb); } module_exit(pvpanic_exit); -- cgit v1.2.3-70-g09d2 From f7e55f05301e71af557c45224817438670225aa7 Mon Sep 17 00:00:00 2001 From: Andra Paraschiv Date: Fri, 27 Aug 2021 18:49:24 +0300 Subject: nitro_enclaves: Enable Arm64 support Update the kernel config to enable the Nitro Enclaves kernel driver for Arm64 support. Reviewed-by: George-Aurelian Popescu Acked-by: Stefano Garzarella Signed-off-by: Andra Paraschiv Link: https://lore.kernel.org/r/20210827154930.40608-2-andraprs@amazon.com Signed-off-by: Greg Kroah-Hartman --- drivers/virt/nitro_enclaves/Kconfig | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/virt/nitro_enclaves/Kconfig b/drivers/virt/nitro_enclaves/Kconfig index 8c9387a232df..f53740b941c0 100644 --- a/drivers/virt/nitro_enclaves/Kconfig +++ b/drivers/virt/nitro_enclaves/Kconfig @@ -1,17 +1,13 @@ # SPDX-License-Identifier: GPL-2.0 # -# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright 2020-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. # Amazon Nitro Enclaves (NE) support. # Nitro is a hypervisor that has been developed by Amazon. -# TODO: Add dependency for ARM64 once NE is supported on Arm platforms. For now, -# the NE kernel driver can be built for aarch64 arch. -# depends on (ARM64 || X86) && HOTPLUG_CPU && PCI && SMP - config NITRO_ENCLAVES tristate "Nitro Enclaves Support" - depends on X86 && HOTPLUG_CPU && PCI && SMP + depends on (ARM64 || X86) && HOTPLUG_CPU && PCI && SMP help This driver consists of support for enclave lifetime management for Nitro Enclaves (NE). -- cgit v1.2.3-70-g09d2 From cfa3c18cd528f48fd1b4b584f330df8f00b8a97f Mon Sep 17 00:00:00 2001 From: Andra Paraschiv Date: Fri, 27 Aug 2021 18:49:25 +0300 Subject: nitro_enclaves: Update documentation for Arm64 support Add references for hugepages and booting steps for Arm64. Include info about the current supported architectures for the NE kernel driver. Reviewed-by: George-Aurelian Popescu Acked-by: Stefano Garzarella Signed-off-by: Andra Paraschiv Link: https://lore.kernel.org/r/20210827154930.40608-3-andraprs@amazon.com Signed-off-by: Greg Kroah-Hartman --- Documentation/virt/ne_overview.rst | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/Documentation/virt/ne_overview.rst b/Documentation/virt/ne_overview.rst index 39b0c8fe2654..74c2f5919c88 100644 --- a/Documentation/virt/ne_overview.rst +++ b/Documentation/virt/ne_overview.rst @@ -14,12 +14,15 @@ instances [1]. For example, an application that processes sensitive data and runs in a VM, can be separated from other applications running in the same VM. This application then runs in a separate VM than the primary VM, namely an enclave. +It runs alongside the VM that spawned it. This setup matches low latency +applications needs. -An enclave runs alongside the VM that spawned it. This setup matches low latency -applications needs. The resources that are allocated for the enclave, such as -memory and CPUs, are carved out of the primary VM. Each enclave is mapped to a -process running in the primary VM, that communicates with the NE driver via an -ioctl interface. +The current supported architectures for the NE kernel driver, available in the +upstream Linux kernel, are x86 and ARM64. + +The resources that are allocated for the enclave, such as memory and CPUs, are +carved out of the primary VM. Each enclave is mapped to a process running in the +primary VM, that communicates with the NE kernel driver via an ioctl interface. In this sense, there are two components: @@ -43,8 +46,8 @@ for the enclave VM. An enclave does not have persistent storage attached. The memory regions carved out of the primary VM and given to an enclave need to be aligned 2 MiB / 1 GiB physically contiguous memory regions (or multiple of this size e.g. 8 MiB). The memory can be allocated e.g. by using hugetlbfs from -user space [2][3]. The memory size for an enclave needs to be at least 64 MiB. -The enclave memory and CPUs need to be from the same NUMA node. +user space [2][3][7]. The memory size for an enclave needs to be at least +64 MiB. The enclave memory and CPUs need to be from the same NUMA node. An enclave runs on dedicated cores. CPU 0 and its CPU siblings need to remain available for the primary VM. A CPU pool has to be set for NE purposes by an @@ -61,7 +64,7 @@ device is placed in memory below the typical 4 GiB. The application that runs in the enclave needs to be packaged in an enclave image together with the OS ( e.g. kernel, ramdisk, init ) that will run in the enclave VM. The enclave VM has its own kernel and follows the standard Linux -boot protocol [6]. +boot protocol [6][8]. The kernel bzImage, the kernel command line, the ramdisk(s) are part of the Enclave Image Format (EIF); plus an EIF header including metadata such as magic @@ -93,3 +96,5 @@ enclave process can exit. [4] https://www.kernel.org/doc/html/latest/admin-guide/kernel-parameters.html [5] https://man7.org/linux/man-pages/man7/vsock.7.html [6] https://www.kernel.org/doc/html/latest/x86/boot.html +[7] https://www.kernel.org/doc/html/latest/arm64/hugetlbpage.html +[8] https://www.kernel.org/doc/html/latest/arm64/booting.html -- cgit v1.2.3-70-g09d2 From e16a30a419c89d71e68f4da348b48918944fd49c Mon Sep 17 00:00:00 2001 From: Andra Paraschiv Date: Fri, 27 Aug 2021 18:49:26 +0300 Subject: nitro_enclaves: Add fix for the kernel-doc report Fix the reported issue from the kernel-doc script, to have a comment per identifier. Reviewed-by: George-Aurelian Popescu Signed-off-by: Andra Paraschiv Link: https://lore.kernel.org/r/20210827154930.40608-4-andraprs@amazon.com Signed-off-by: Greg Kroah-Hartman --- drivers/virt/nitro_enclaves/ne_pci_dev.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/virt/nitro_enclaves/ne_pci_dev.h b/drivers/virt/nitro_enclaves/ne_pci_dev.h index 8bfbc6607818..6e9f28971a4e 100644 --- a/drivers/virt/nitro_enclaves/ne_pci_dev.h +++ b/drivers/virt/nitro_enclaves/ne_pci_dev.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright 2020-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. */ #ifndef _NE_PCI_DEV_H_ @@ -84,9 +84,13 @@ */ /** - * NE_SEND_DATA_SIZE / NE_RECV_DATA_SIZE - 240 bytes for send / recv buffer. + * NE_SEND_DATA_SIZE - Size of the send buffer, in bytes. */ #define NE_SEND_DATA_SIZE (240) + +/** + * NE_RECV_DATA_SIZE - Size of the receive buffer, in bytes. + */ #define NE_RECV_DATA_SIZE (240) /** -- cgit v1.2.3-70-g09d2 From e3cba4d2454c2e4034147ca53b75c6592ded7c5a Mon Sep 17 00:00:00 2001 From: Andra Paraschiv Date: Fri, 27 Aug 2021 18:49:27 +0300 Subject: nitro_enclaves: Update copyright statement to include 2021 Update the copyright statement to include 2021, as a change has been made over this year. Check commit d874742f6a73 ("nitro_enclaves: Set Bus Master for the NE PCI device") for the codebase update from this file (ne_pci_dev.c). Reviewed-by: George-Aurelian Popescu Signed-off-by: Andra Paraschiv Link: https://lore.kernel.org/r/20210827154930.40608-5-andraprs@amazon.com Signed-off-by: Greg Kroah-Hartman --- drivers/virt/nitro_enclaves/ne_pci_dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/virt/nitro_enclaves/ne_pci_dev.c b/drivers/virt/nitro_enclaves/ne_pci_dev.c index 143207e9b969..40b49ec8e30b 100644 --- a/drivers/virt/nitro_enclaves/ne_pci_dev.c +++ b/drivers/virt/nitro_enclaves/ne_pci_dev.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright 2020-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. */ /** -- cgit v1.2.3-70-g09d2 From 02bba596de19c0b951b2843ebcd7a9da6e0938d4 Mon Sep 17 00:00:00 2001 From: Andra Paraschiv Date: Fri, 27 Aug 2021 18:49:28 +0300 Subject: nitro_enclaves: Add fixes for checkpatch match open parenthesis reports Update the codebase formatting to fix the reports from the checkpatch script, to match the open parenthesis. Reviewed-by: George-Aurelian Popescu Signed-off-by: Andra Paraschiv Link: https://lore.kernel.org/r/20210827154930.40608-6-andraprs@amazon.com Signed-off-by: Greg Kroah-Hartman --- drivers/virt/nitro_enclaves/ne_misc_dev.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/virt/nitro_enclaves/ne_misc_dev.c b/drivers/virt/nitro_enclaves/ne_misc_dev.c index e21e1e86ad15..8939612ee0e0 100644 --- a/drivers/virt/nitro_enclaves/ne_misc_dev.c +++ b/drivers/virt/nitro_enclaves/ne_misc_dev.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright 2020-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. */ /** @@ -284,8 +284,8 @@ static int ne_setup_cpu_pool(const char *ne_cpu_list) ne_cpu_pool.nr_parent_vm_cores = nr_cpu_ids / ne_cpu_pool.nr_threads_per_core; ne_cpu_pool.avail_threads_per_core = kcalloc(ne_cpu_pool.nr_parent_vm_cores, - sizeof(*ne_cpu_pool.avail_threads_per_core), - GFP_KERNEL); + sizeof(*ne_cpu_pool.avail_threads_per_core), + GFP_KERNEL); if (!ne_cpu_pool.avail_threads_per_core) { rc = -ENOMEM; @@ -735,7 +735,7 @@ static int ne_add_vcpu_ioctl(struct ne_enclave *ne_enclave, u32 vcpu_id) * * Negative return value on failure. */ static int ne_sanity_check_user_mem_region(struct ne_enclave *ne_enclave, - struct ne_user_memory_region mem_region) + struct ne_user_memory_region mem_region) { struct ne_mem_region *ne_mem_region = NULL; @@ -771,7 +771,7 @@ static int ne_sanity_check_user_mem_region(struct ne_enclave *ne_enclave, u64 userspace_addr = ne_mem_region->userspace_addr; if ((userspace_addr <= mem_region.userspace_addr && - mem_region.userspace_addr < (userspace_addr + memory_size)) || + mem_region.userspace_addr < (userspace_addr + memory_size)) || (mem_region.userspace_addr <= userspace_addr && (mem_region.userspace_addr + mem_region.memory_size) > userspace_addr)) { dev_err_ratelimited(ne_misc_dev.this_device, @@ -836,7 +836,7 @@ static int ne_sanity_check_user_mem_region_page(struct ne_enclave *ne_enclave, * * Negative return value on failure. */ static int ne_set_user_memory_region_ioctl(struct ne_enclave *ne_enclave, - struct ne_user_memory_region mem_region) + struct ne_user_memory_region mem_region) { long gup_rc = 0; unsigned long i = 0; @@ -1014,7 +1014,7 @@ free_mem_region: * * Negative return value on failure. */ static int ne_start_enclave_ioctl(struct ne_enclave *ne_enclave, - struct ne_enclave_start_info *enclave_start_info) + struct ne_enclave_start_info *enclave_start_info) { struct ne_pci_dev_cmd_reply cmd_reply = {}; unsigned int cpu = 0; @@ -1574,7 +1574,8 @@ static int ne_create_vm_ioctl(struct ne_pci_dev *ne_pci_dev, u64 __user *slot_ui mutex_unlock(&ne_cpu_pool.mutex); ne_enclave->threads_per_core = kcalloc(ne_enclave->nr_parent_vm_cores, - sizeof(*ne_enclave->threads_per_core), GFP_KERNEL); + sizeof(*ne_enclave->threads_per_core), + GFP_KERNEL); if (!ne_enclave->threads_per_core) { rc = -ENOMEM; -- cgit v1.2.3-70-g09d2 From 059ebe4fe332c5d1c25124166527cdf9fe43a3ce Mon Sep 17 00:00:00 2001 From: Andra Paraschiv Date: Fri, 27 Aug 2021 18:49:29 +0300 Subject: nitro_enclaves: Add fixes for checkpatch spell check reports Fix the typos in the words spelling as per the checkpatch script reports. Reviewed-by: George-Aurelian Popescu Signed-off-by: Andra Paraschiv Link: https://lore.kernel.org/r/20210827154930.40608-7-andraprs@amazon.com Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/nitro_enclaves.h | 10 +++++----- samples/nitro_enclaves/ne_ioctl_sample.c | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/uapi/linux/nitro_enclaves.h b/include/uapi/linux/nitro_enclaves.h index b945073fe544..e808f5ba124d 100644 --- a/include/uapi/linux/nitro_enclaves.h +++ b/include/uapi/linux/nitro_enclaves.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright 2020-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. */ #ifndef _UAPI_LINUX_NITRO_ENCLAVES_H_ @@ -60,7 +60,7 @@ * * Context: Process context. * Return: - * * 0 - Logic succesfully completed. + * * 0 - Logic successfully completed. * * -1 - There was a failure in the ioctl logic. * On failure, errno is set to: * * EFAULT - copy_from_user() / copy_to_user() failure. @@ -95,7 +95,7 @@ * * Context: Process context. * Return: - * * 0 - Logic succesfully completed. + * * 0 - Logic successfully completed. * * -1 - There was a failure in the ioctl logic. * On failure, errno is set to: * * EFAULT - copy_from_user() / copy_to_user() failure. @@ -118,7 +118,7 @@ * * Context: Process context. * Return: - * * 0 - Logic succesfully completed. + * * 0 - Logic successfully completed. * * -1 - There was a failure in the ioctl logic. * On failure, errno is set to: * * EFAULT - copy_from_user() failure. @@ -161,7 +161,7 @@ * * Context: Process context. * Return: - * * 0 - Logic succesfully completed. + * * 0 - Logic successfully completed. * * -1 - There was a failure in the ioctl logic. * On failure, errno is set to: * * EFAULT - copy_from_user() / copy_to_user() failure. diff --git a/samples/nitro_enclaves/ne_ioctl_sample.c b/samples/nitro_enclaves/ne_ioctl_sample.c index 480b763142b3..6a60990b2e20 100644 --- a/samples/nitro_enclaves/ne_ioctl_sample.c +++ b/samples/nitro_enclaves/ne_ioctl_sample.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright 2020-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. */ /** @@ -638,7 +638,7 @@ static int ne_start_enclave(int enclave_fd, struct ne_enclave_start_info *encla } /** - * ne_start_enclave_check_booted() - Start the enclave and wait for a hearbeat + * ne_start_enclave_check_booted() - Start the enclave and wait for a heartbeat * from it, on a newly created vsock channel, * to check it has booted. * @enclave_fd : The file descriptor associated with the enclave. -- cgit v1.2.3-70-g09d2 From da1c396a81b87541cf7a7c2c6e128b722461a1c8 Mon Sep 17 00:00:00 2001 From: Andra Paraschiv Date: Fri, 27 Aug 2021 18:49:30 +0300 Subject: nitro_enclaves: Add fixes for checkpatch blank line reports Remove blank lines that are not necessary, fixing the checkpatch script reports. While at it, add a blank line after the switch default block, similar to the other parts of the codebase. Reviewed-by: George-Aurelian Popescu Signed-off-by: Andra Paraschiv Link: https://lore.kernel.org/r/20210827154930.40608-8-andraprs@amazon.com Signed-off-by: Greg Kroah-Hartman --- samples/nitro_enclaves/ne_ioctl_sample.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/samples/nitro_enclaves/ne_ioctl_sample.c b/samples/nitro_enclaves/ne_ioctl_sample.c index 6a60990b2e20..765b131c7319 100644 --- a/samples/nitro_enclaves/ne_ioctl_sample.c +++ b/samples/nitro_enclaves/ne_ioctl_sample.c @@ -185,7 +185,6 @@ static int ne_create_vm(int ne_dev_fd, unsigned long *slot_uid, int *enclave_fd) return 0; } - /** * ne_poll_enclave_fd() - Thread function for polling the enclave fd. * @data: Argument provided for the polling function. @@ -560,8 +559,8 @@ static int ne_add_vcpu(int enclave_fd, unsigned int *vcpu_id) default: printf("Error in add vcpu [%m]\n"); - } + return rc; } -- cgit v1.2.3-70-g09d2 From b46f7d3309fdd1695df7576a5329077d6b94d2ad Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 27 Aug 2021 19:17:33 +0200 Subject: char: xillybus: Remove usage of the deprecated 'pci-dma-compat.h' API In [1], Christoph Hellwig has proposed to remove the wrappers in include/linux/pci-dma-compat.h. Some reasons why this API should be removed have been given by Julia Lawall in [2]. A coccinelle script has been used to perform the needed transformation Only relevant part are given below. 'xilly_pci_direction()' has been hand modified to simplify it slightly. It has been compile tested. @@ @@ - PCI_DMA_BIDIRECTIONAL + DMA_BIDIRECTIONAL @@ @@ - PCI_DMA_TODEVICE + DMA_TO_DEVICE @@ @@ - PCI_DMA_FROMDEVICE + DMA_FROM_DEVICE @@ expression e1, e2; @@ - pci_set_dma_mask(e1, e2) + dma_set_mask(&e1->dev, e2) [1]: https://lore.kernel.org/kernel-janitors/20200421081257.GA131897@infradead.org/ [2]: https://lore.kernel.org/kernel-janitors/alpine.DEB.2.22.394.2007120902170.2424@hadrien/ Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/e25aa2a804972c5d4f06c4c4e0511e11ff97a425.1630083668.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman --- drivers/char/xillybus/xillybus_pcie.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/char/xillybus/xillybus_pcie.c b/drivers/char/xillybus/xillybus_pcie.c index bdf1c366b4fc..be25bfdb0d9a 100644 --- a/drivers/char/xillybus/xillybus_pcie.c +++ b/drivers/char/xillybus/xillybus_pcie.c @@ -36,11 +36,10 @@ static int xilly_pci_direction(int direction) { switch (direction) { case DMA_TO_DEVICE: - return PCI_DMA_TODEVICE; case DMA_FROM_DEVICE: - return PCI_DMA_FROMDEVICE; + return direction; default: - return PCI_DMA_BIDIRECTIONAL; + return DMA_BIDIRECTIONAL; } } @@ -185,9 +184,9 @@ static int xilly_probe(struct pci_dev *pdev, * So go for the 64-bit mask only when failing is the other option. */ - if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { + if (!dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) { endpoint->dma_using_dac = 0; - } else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) { + } else if (!dma_set_mask(&pdev->dev, DMA_BIT_MASK(64))) { endpoint->dma_using_dac = 1; } else { dev_err(endpoint->dev, "Failed to set DMA mask. Aborting.\n"); -- cgit v1.2.3-70-g09d2 From 0b1eff5152b3646c9e33b7e0e5dd6f0f2006b06c Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 27 Aug 2021 19:17:39 +0200 Subject: char: xillybus: Remove usage of 'pci_unmap_single()' 'struct xilly_mapping' includes a 'void *device' field which holds, depending of the context, a 'struct device *' or a 'struct pci_dev *'. This field is then used with 'pci_umap_single()' in 'xillybus_pcie.c' and with 'dma_umap_single()' in 'xillybus_of.c'. In order to remove usage of the deprecated 'pci_unmap_single()' API, turn the 'void *device' field from 'struct xilly_mapping', into an explicit 'struct device *device' and use 'dma_umap_single()' everywhere. In order to update 'xillybus_pcie.c', use the 'dev' field instead of the 'pdev' field from the 'struct xilly_endpoint'. Both fields are initialized by 'xillybus_init_endpoint()' and in 'xillybus_pcie.c', we have: xillybus_init_endpoint(pdev, &pdev->dev, &pci_hw); ^ ^ xilly_endpoint.pdev = ___| |___ = xilly_endpoint.dev So the modification from pci_ to dma_ function is straightforward. While at it, remove a comment that is wrong, because in the case above, both 'dev' and 'pdev' are not NULL. Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/baa3f6c7f009d9c231ae320bf1d568268bfef089.1630083668.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman --- drivers/char/xillybus/xillybus.h | 6 +----- drivers/char/xillybus/xillybus_pcie.c | 6 +++--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/char/xillybus/xillybus.h b/drivers/char/xillybus/xillybus.h index c63ffc56637c..7c71bdef7ccb 100644 --- a/drivers/char/xillybus/xillybus.h +++ b/drivers/char/xillybus/xillybus.h @@ -87,10 +87,6 @@ struct xilly_channel { }; struct xilly_endpoint { - /* - * One of pdev and dev is always NULL, and the other is a valid - * pointer, depending on the type of device - */ struct pci_dev *pdev; struct device *dev; struct xilly_endpoint_hardware *ephw; @@ -131,7 +127,7 @@ struct xilly_endpoint_hardware { }; struct xilly_mapping { - void *device; + struct device *device; dma_addr_t dma_addr; size_t size; int direction; diff --git a/drivers/char/xillybus/xillybus_pcie.c b/drivers/char/xillybus/xillybus_pcie.c index be25bfdb0d9a..8360427e4226 100644 --- a/drivers/char/xillybus/xillybus_pcie.c +++ b/drivers/char/xillybus/xillybus_pcie.c @@ -69,8 +69,8 @@ static void xilly_pci_unmap(void *ptr) { struct xilly_mapping *data = ptr; - pci_unmap_single(data->device, data->dma_addr, - data->size, data->direction); + dma_unmap_single(data->device, data->dma_addr, data->size, + data->direction); kfree(ptr); } @@ -105,7 +105,7 @@ static int xilly_map_single_pci(struct xilly_endpoint *ep, return -ENODEV; } - this->device = ep->pdev; + this->device = ep->dev; this->dma_addr = addr; this->size = size; this->direction = pci_direction; -- cgit v1.2.3-70-g09d2 From 3e053c44eff5ec5364916a2c63b701d14f3099bf Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 27 Aug 2021 19:17:47 +0200 Subject: char: xillybus: Remove usage of remaining deprecated pci_ API 'struct xilly_endpoint' has a 'dev' field which is a 'struct device *' and a 'pdev' field which is 'struct pci_dev *'. Both fields are initialized by 'xillybus_init_endpoint()' and in 'xillybus_pcie.c', we have: xillybus_init_endpoint(pdev, &pdev->dev, &pci_hw); ^ ^ xilly_endpoint.pdev = ___| |___ = xilly_endpoint.dev So the modification from pci_ to dma_ function is straightforward. Update all remaining deprecated pci_ function calls to equivalent dma_ API function. Switching from 'ep->pdev' to 'ep->dev' makes the transformation straightforward. Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/19d67ac0208a609aef1e28278b3f2477aa714029.1630083668.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman --- drivers/char/xillybus/xillybus.h | 1 - drivers/char/xillybus/xillybus_core.c | 2 +- drivers/char/xillybus/xillybus_pcie.c | 16 ++++++---------- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/char/xillybus/xillybus.h b/drivers/char/xillybus/xillybus.h index 7c71bdef7ccb..55d47cb13a7b 100644 --- a/drivers/char/xillybus/xillybus.h +++ b/drivers/char/xillybus/xillybus.h @@ -87,7 +87,6 @@ struct xilly_channel { }; struct xilly_endpoint { - struct pci_dev *pdev; struct device *dev; struct xilly_endpoint_hardware *ephw; diff --git a/drivers/char/xillybus/xillybus_core.c b/drivers/char/xillybus/xillybus_core.c index 931d0bf4cec6..0ced9ec6977f 100644 --- a/drivers/char/xillybus/xillybus_core.c +++ b/drivers/char/xillybus/xillybus_core.c @@ -1783,7 +1783,7 @@ struct xilly_endpoint *xillybus_init_endpoint(struct pci_dev *pdev, if (!endpoint) return NULL; - endpoint->pdev = pdev; + (void)pdev; // silence a compiler warning, will be removed endpoint->dev = dev; endpoint->ephw = ephw; endpoint->msg_counter = 0x0b; diff --git a/drivers/char/xillybus/xillybus_pcie.c b/drivers/char/xillybus/xillybus_pcie.c index 8360427e4226..f4be61349ca6 100644 --- a/drivers/char/xillybus/xillybus_pcie.c +++ b/drivers/char/xillybus/xillybus_pcie.c @@ -48,10 +48,8 @@ static void xilly_dma_sync_single_for_cpu_pci(struct xilly_endpoint *ep, size_t size, int direction) { - pci_dma_sync_single_for_cpu(ep->pdev, - dma_handle, - size, - xilly_pci_direction(direction)); + dma_sync_single_for_cpu(ep->dev, dma_handle, size, + xilly_pci_direction(direction)); } static void xilly_dma_sync_single_for_device_pci(struct xilly_endpoint *ep, @@ -59,10 +57,8 @@ static void xilly_dma_sync_single_for_device_pci(struct xilly_endpoint *ep, size_t size, int direction) { - pci_dma_sync_single_for_device(ep->pdev, - dma_handle, - size, - xilly_pci_direction(direction)); + dma_sync_single_for_device(ep->dev, dma_handle, size, + xilly_pci_direction(direction)); } static void xilly_pci_unmap(void *ptr) @@ -98,9 +94,9 @@ static int xilly_map_single_pci(struct xilly_endpoint *ep, pci_direction = xilly_pci_direction(direction); - addr = pci_map_single(ep->pdev, ptr, size, pci_direction); + addr = dma_map_single(ep->dev, ptr, size, pci_direction); - if (pci_dma_mapping_error(ep->pdev, addr)) { + if (dma_mapping_error(ep->dev, addr)) { kfree(this); return -ENODEV; } -- cgit v1.2.3-70-g09d2 From ff8d123f0b0ee774089f70d8e266ecd1371e84e2 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 27 Aug 2021 19:17:53 +0200 Subject: char: xillybus: Simplify 'xillybus_init_endpoint()' Ths first argument of 'xillybus_init_endpoint()' is now useless. Remove it. Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/ba687c1eff5dc8f21422323f57164d06f25d4169.1630083668.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman --- drivers/char/xillybus/xillybus.h | 3 +-- drivers/char/xillybus/xillybus_core.c | 4 +--- drivers/char/xillybus/xillybus_of.c | 2 +- drivers/char/xillybus/xillybus_pcie.c | 2 +- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/char/xillybus/xillybus.h b/drivers/char/xillybus/xillybus.h index 55d47cb13a7b..afce5bb4d127 100644 --- a/drivers/char/xillybus/xillybus.h +++ b/drivers/char/xillybus/xillybus.h @@ -134,8 +134,7 @@ struct xilly_mapping { irqreturn_t xillybus_isr(int irq, void *data); -struct xilly_endpoint *xillybus_init_endpoint(struct pci_dev *pdev, - struct device *dev, +struct xilly_endpoint *xillybus_init_endpoint(struct device *dev, struct xilly_endpoint_hardware *ephw); diff --git a/drivers/char/xillybus/xillybus_core.c b/drivers/char/xillybus/xillybus_core.c index 0ced9ec6977f..02f30140c2d5 100644 --- a/drivers/char/xillybus/xillybus_core.c +++ b/drivers/char/xillybus/xillybus_core.c @@ -1772,8 +1772,7 @@ static const struct file_operations xillybus_fops = { .poll = xillybus_poll, }; -struct xilly_endpoint *xillybus_init_endpoint(struct pci_dev *pdev, - struct device *dev, +struct xilly_endpoint *xillybus_init_endpoint(struct device *dev, struct xilly_endpoint_hardware *ephw) { @@ -1783,7 +1782,6 @@ struct xilly_endpoint *xillybus_init_endpoint(struct pci_dev *pdev, if (!endpoint) return NULL; - (void)pdev; // silence a compiler warning, will be removed endpoint->dev = dev; endpoint->ephw = ephw; endpoint->msg_counter = 0x0b; diff --git a/drivers/char/xillybus/xillybus_of.c b/drivers/char/xillybus/xillybus_of.c index 1a20b286fd1d..4e6e0c19d8c8 100644 --- a/drivers/char/xillybus/xillybus_of.c +++ b/drivers/char/xillybus/xillybus_of.c @@ -120,7 +120,7 @@ static int xilly_drv_probe(struct platform_device *op) if (of_property_read_bool(dev->of_node, "dma-coherent")) ephw = &of_hw_coherent; - endpoint = xillybus_init_endpoint(NULL, dev, ephw); + endpoint = xillybus_init_endpoint(dev, ephw); if (!endpoint) return -ENOMEM; diff --git a/drivers/char/xillybus/xillybus_pcie.c b/drivers/char/xillybus/xillybus_pcie.c index f4be61349ca6..a6ef4ce90649 100644 --- a/drivers/char/xillybus/xillybus_pcie.c +++ b/drivers/char/xillybus/xillybus_pcie.c @@ -124,7 +124,7 @@ static int xilly_probe(struct pci_dev *pdev, struct xilly_endpoint *endpoint; int rc; - endpoint = xillybus_init_endpoint(pdev, &pdev->dev, &pci_hw); + endpoint = xillybus_init_endpoint(&pdev->dev, &pci_hw); if (!endpoint) return -ENOMEM; -- cgit v1.2.3-70-g09d2 From aee1bbf66ba0343828fd8e62e59480314914b75e Mon Sep 17 00:00:00 2001 From: Len Baker Date: Sun, 5 Sep 2021 17:37:07 +0200 Subject: tifm: Prefer struct_size over open coded arithmetic As noted in the "Deprecated Interfaces, Language Features, Attributes, and Conventions" documentation [1], size calculations (especially multiplication) should not be performed in memory allocator (or similar) function arguments due to the risk of them overflowing. This could lead to values wrapping around and a smaller allocation being made than the caller was expecting. Using those allocations could lead to linear overflows of heap memory and other misbehaviors. So, use the struct_size() helper to do the arithmetic instead of the argument "size + size * count" in the kzalloc() function. [1] https://www.kernel.org/doc/html/v5.14/process/deprecated.html#open-coded-arithmetic-in-allocator-arguments Signed-off-by: Len Baker Link: https://lore.kernel.org/r/20210905153707.9638-1-len.baker@gmx.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/tifm_core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c index 52656fc87e99..2bb46b1f4be3 100644 --- a/drivers/misc/tifm_core.c +++ b/drivers/misc/tifm_core.c @@ -176,8 +176,7 @@ struct tifm_adapter *tifm_alloc_adapter(unsigned int num_sockets, { struct tifm_adapter *fm; - fm = kzalloc(sizeof(struct tifm_adapter) - + sizeof(struct tifm_dev*) * num_sockets, GFP_KERNEL); + fm = kzalloc(struct_size(fm, sockets, num_sockets), GFP_KERNEL); if (fm) { fm->dev.class = &tifm_adapter_class; fm->dev.parent = dev; -- cgit v1.2.3-70-g09d2 From d06246ebd7738bbfc581b887bc24a102450a323f Mon Sep 17 00:00:00 2001 From: "Philip K. Gisslow" Date: Sun, 5 Sep 2021 09:31:32 +0200 Subject: scripts/tags.sh: Fix obsolete parameter for ctags Distros such as Fedora and Arch are using the maintained universal-ctags implementation. This version has replaced the obsolete --extra flag with --extras. Signed-off-by: Philip K. Gisslow Link: https://lore.kernel.org/r/20210905073133.21910-1-ripxorip@gmail.com Signed-off-by: Greg Kroah-Hartman --- scripts/tags.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/tags.sh b/scripts/tags.sh index db8ba411860a..b24bfaec6290 100755 --- a/scripts/tags.sh +++ b/scripts/tags.sh @@ -247,6 +247,10 @@ setup_regex() exuberant() { + CTAGS_EXTRA="extra" + if $1 --version 2>&1 | grep -iq universal; then + CTAGS_EXTRA="extras" + fi setup_regex exuberant asm c all_target_sources | xargs $1 -a \ -I __initdata,__exitdata,__initconst,__ro_after_init \ @@ -261,7 +265,7 @@ exuberant() -I EXPORT_SYMBOL,EXPORT_SYMBOL_GPL,ACPI_EXPORT_SYMBOL \ -I DEFINE_TRACE,EXPORT_TRACEPOINT_SYMBOL,EXPORT_TRACEPOINT_SYMBOL_GPL \ -I static,const \ - --extra=+fq --c-kinds=+px --fields=+iaS --langmap=c:+.h \ + --$CTAGS_EXTRA=+fq --c-kinds=+px --fields=+iaS --langmap=c:+.h \ "${regex[@]}" setup_regex exuberant kconfig -- cgit v1.2.3-70-g09d2 From bf43a71a0a7f396434f6460b46e33eb00752f78d Mon Sep 17 00:00:00 2001 From: Puranjay Mohan Date: Wed, 11 Aug 2021 13:00:26 +0530 Subject: dt-bindings: iio: accel: Add DT binding doc for ADXL355 Add devicetree binding document for ADXL355, a 3-Axis MEMS Accelerometer. Signed-off-by: Puranjay Mohan Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20210811073027.124619-2-puranjay12@gmail.com Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/accel/adi,adxl355.yaml | 88 ++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/accel/adi,adxl355.yaml diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl355.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl355.yaml new file mode 100644 index 000000000000..ba54d6998f2e --- /dev/null +++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl355.yaml @@ -0,0 +1,88 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/accel/adi,adxl355.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices ADXL355 3-Axis, Low noise MEMS Accelerometer + +maintainers: + - Puranjay Mohan + +description: | + Analog Devices ADXL355 3-Axis, Low noise MEMS Accelerometer that supports + both I2C & SPI interfaces + https://www.analog.com/en/products/adxl355.html + +properties: + compatible: + enum: + - adi,adxl355 + + reg: + maxItems: 1 + + interrupts: + minItems: 1 + maxItems: 3 + description: | + Type for DRDY should be IRQ_TYPE_EDGE_RISING. + Three configurable interrupt lines exist. + + interrupt-names: + description: Specify which interrupt line is in use. + items: + enum: + - INT1 + - INT2 + - DRDY + minItems: 1 + maxItems: 3 + + vdd-supply: + description: Regulator that provides power to the sensor + + vddio-supply: + description: Regulator that provides power to the bus + + spi-max-frequency: true + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + /* Example for a I2C device node */ + accelerometer@1d { + compatible = "adi,adxl355"; + reg = <0x1d>; + interrupt-parent = <&gpio>; + interrupts = <25 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "DRDY"; + }; + }; + - | + #include + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + + accelerometer@0 { + compatible = "adi,adxl355"; + reg = <0>; + spi-max-frequency = <1000000>; + interrupt-parent = <&gpio>; + interrupts = <25 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "DRDY"; + }; + }; -- cgit v1.2.3-70-g09d2 From 12ed27863ea3148239ec368e16c1a0f937e4d9bd Mon Sep 17 00:00:00 2001 From: Puranjay Mohan Date: Wed, 11 Aug 2021 13:00:27 +0530 Subject: iio: accel: Add driver support for ADXL355 ADXL355 is a 3-axis MEMS Accelerometer. It offers low noise density, low 0g offset drift, low power with selectable measurement ranges. It also features programmable high-pass and low-pass filters. Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/adxl354_adxl355.pdf Reviewed-by: Alexandru Ardelean Reviewed-by: Andy Shevchenko Signed-off-by: Puranjay Mohan Link: https://lore.kernel.org/r/20210811073027.124619-3-puranjay12@gmail.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 10 + drivers/iio/accel/Kconfig | 29 ++ drivers/iio/accel/Makefile | 3 + drivers/iio/accel/adxl355.h | 21 ++ drivers/iio/accel/adxl355_core.c | 612 +++++++++++++++++++++++++++++++++++++++ drivers/iio/accel/adxl355_i2c.c | 62 ++++ drivers/iio/accel/adxl355_spi.c | 65 +++++ 7 files changed, 802 insertions(+) create mode 100644 drivers/iio/accel/adxl355.h create mode 100644 drivers/iio/accel/adxl355_core.c create mode 100644 drivers/iio/accel/adxl355_i2c.c create mode 100644 drivers/iio/accel/adxl355_spi.c diff --git a/MAINTAINERS b/MAINTAINERS index eeb4c70b3d5b..e7e2626b83dd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -598,6 +598,16 @@ W: http://ez.analog.com/community/linux-device-drivers F: Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml F: drivers/input/misc/adxl34x.c +ADXL355 THREE-AXIS DIGITAL ACCELEROMETER DRIVER +M: Puranjay Mohan +L: linux-iio@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/iio/accel/adi,adxl355.yaml +F: drivers/iio/accel/adxl355.h +F: drivers/iio/accel/adxl355_core.c +F: drivers/iio/accel/adxl355_i2c.c +F: drivers/iio/accel/adxl355_spi.c + ADXL372 THREE-AXIS DIGITAL ACCELEROMETER DRIVER M: Michael Hennerich S: Supported diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index a0e9061f6d6b..05a3504119a8 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -61,6 +61,35 @@ config ADXL345_SPI will be called adxl345_spi and you will also get adxl345_core for the core module. +config ADXL355 + tristate + +config ADXL355_I2C + tristate "Analog Devices ADXL355 3-Axis Digital Accelerometer I2C Driver" + depends on I2C + select ADXL355 + select REGMAP_I2C + help + Say Y here if you want to build i2c support for the Analog Devices + ADXL355 3-axis digital accelerometer. + + To compile this driver as a module, choose M here: the module + will be called adxl355_i2c and you will also get adxl355_core + for the core module. + +config ADXL355_SPI + tristate "Analog Devices ADXL355 3-Axis Digital Accelerometer SPI Driver" + depends on SPI + select ADXL355 + select REGMAP_SPI + help + Say Y here if you want to build spi support for the Analog Devices + ADXL355 3-axis digital accelerometer. + + To compile this driver as a module, choose M here: the module + will be called adxl355_spi and you will also get adxl355_core + for the core module. + config ADXL372 tristate select IIO_BUFFER diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 89280e823bcd..7f4d97bf41f9 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -9,6 +9,9 @@ obj-$(CONFIG_ADIS16209) += adis16209.o obj-$(CONFIG_ADXL345) += adxl345_core.o obj-$(CONFIG_ADXL345_I2C) += adxl345_i2c.o obj-$(CONFIG_ADXL345_SPI) += adxl345_spi.o +obj-$(CONFIG_ADXL355) += adxl355_core.o +obj-$(CONFIG_ADXL355_I2C) += adxl355_i2c.o +obj-$(CONFIG_ADXL355_SPI) += adxl355_spi.o obj-$(CONFIG_ADXL372) += adxl372.o obj-$(CONFIG_ADXL372_I2C) += adxl372_i2c.o obj-$(CONFIG_ADXL372_SPI) += adxl372_spi.o diff --git a/drivers/iio/accel/adxl355.h b/drivers/iio/accel/adxl355.h new file mode 100644 index 000000000000..6dd49b13e4fd --- /dev/null +++ b/drivers/iio/accel/adxl355.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ADXL355 3-Axis Digital Accelerometer + * + * Copyright (c) 2021 Puranjay Mohan + */ + +#ifndef _ADXL355_H_ +#define _ADXL355_H_ + +#include + +struct device; + +extern const struct regmap_access_table adxl355_readable_regs_tbl; +extern const struct regmap_access_table adxl355_writeable_regs_tbl; + +int adxl355_core_probe(struct device *dev, struct regmap *regmap, + const char *name); + +#endif /* _ADXL355_H_ */ diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c new file mode 100644 index 000000000000..f71f64b32a01 --- /dev/null +++ b/drivers/iio/accel/adxl355_core.c @@ -0,0 +1,612 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADXL355 3-Axis Digital Accelerometer IIO core driver + * + * Copyright (c) 2021 Puranjay Mohan + * + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/adxl354_adxl355.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "adxl355.h" + +/* ADXL355 Register Definitions */ +#define ADXL355_DEVID_AD_REG 0x00 +#define ADXL355_DEVID_MST_REG 0x01 +#define ADXL355_PARTID_REG 0x02 +#define ADXL355_STATUS_REG 0x04 +#define ADXL355_FIFO_ENTRIES_REG 0x05 +#define ADXL355_TEMP2_REG 0x06 +#define ADXL355_XDATA3_REG 0x08 +#define ADXL355_YDATA3_REG 0x0B +#define ADXL355_ZDATA3_REG 0x0E +#define ADXL355_FIFO_DATA_REG 0x11 +#define ADXL355_OFFSET_X_H_REG 0x1E +#define ADXL355_OFFSET_Y_H_REG 0x20 +#define ADXL355_OFFSET_Z_H_REG 0x22 +#define ADXL355_ACT_EN_REG 0x24 +#define ADXL355_ACT_THRESH_H_REG 0x25 +#define ADXL355_ACT_THRESH_L_REG 0x26 +#define ADXL355_ACT_COUNT_REG 0x27 +#define ADXL355_FILTER_REG 0x28 +#define ADXL355_FILTER_ODR_MSK GENMASK(3, 0) +#define ADXL355_FILTER_HPF_MSK GENMASK(6, 4) +#define ADXL355_FIFO_SAMPLES_REG 0x29 +#define ADXL355_INT_MAP_REG 0x2A +#define ADXL355_SYNC_REG 0x2B +#define ADXL355_RANGE_REG 0x2C +#define ADXL355_POWER_CTL_REG 0x2D +#define ADXL355_POWER_CTL_MODE_MSK GENMASK(1, 0) +#define ADXL355_SELF_TEST_REG 0x2E +#define ADXL355_RESET_REG 0x2F + +#define ADXL355_DEVID_AD_VAL 0xAD +#define ADXL355_DEVID_MST_VAL 0x1D +#define ADXL355_PARTID_VAL 0xED +#define ADXL355_RESET_CODE 0x52 + +#define MEGA 1000000UL +#define TERA 1000000000000ULL + +static const struct regmap_range adxl355_read_reg_range[] = { + regmap_reg_range(ADXL355_DEVID_AD_REG, ADXL355_FIFO_DATA_REG), + regmap_reg_range(ADXL355_OFFSET_X_H_REG, ADXL355_SELF_TEST_REG), +}; + +const struct regmap_access_table adxl355_readable_regs_tbl = { + .yes_ranges = adxl355_read_reg_range, + .n_yes_ranges = ARRAY_SIZE(adxl355_read_reg_range), +}; +EXPORT_SYMBOL_GPL(adxl355_readable_regs_tbl); + +static const struct regmap_range adxl355_write_reg_range[] = { + regmap_reg_range(ADXL355_OFFSET_X_H_REG, ADXL355_RESET_REG), +}; + +const struct regmap_access_table adxl355_writeable_regs_tbl = { + .yes_ranges = adxl355_write_reg_range, + .n_yes_ranges = ARRAY_SIZE(adxl355_write_reg_range), +}; +EXPORT_SYMBOL_GPL(adxl355_writeable_regs_tbl); + +enum adxl355_op_mode { + ADXL355_MEASUREMENT, + ADXL355_STANDBY, + ADXL355_TEMP_OFF, +}; + +enum adxl355_odr { + ADXL355_ODR_4000HZ, + ADXL355_ODR_2000HZ, + ADXL355_ODR_1000HZ, + ADXL355_ODR_500HZ, + ADXL355_ODR_250HZ, + ADXL355_ODR_125HZ, + ADXL355_ODR_62_5HZ, + ADXL355_ODR_31_25HZ, + ADXL355_ODR_15_625HZ, + ADXL355_ODR_7_813HZ, + ADXL355_ODR_3_906HZ, +}; + +enum adxl355_hpf_3db { + ADXL355_HPF_OFF, + ADXL355_HPF_24_7, + ADXL355_HPF_6_2084, + ADXL355_HPF_1_5545, + ADXL355_HPF_0_3862, + ADXL355_HPF_0_0954, + ADXL355_HPF_0_0238, +}; + +static const int adxl355_odr_table[][2] = { + [0] = {4000, 0}, + [1] = {2000, 0}, + [2] = {1000, 0}, + [3] = {500, 0}, + [4] = {250, 0}, + [5] = {125, 0}, + [6] = {62, 500000}, + [7] = {31, 250000}, + [8] = {15, 625000}, + [9] = {7, 813000}, + [10] = {3, 906000}, +}; + +static const int adxl355_hpf_3db_multipliers[] = { + 0, + 247000, + 62084, + 15545, + 3862, + 954, + 238, +}; + +enum adxl355_chans { + chan_x, chan_y, chan_z, +}; + +struct adxl355_chan_info { + u8 data_reg; + u8 offset_reg; +}; + +static const struct adxl355_chan_info adxl355_chans[] = { + [chan_x] = { + .data_reg = ADXL355_XDATA3_REG, + .offset_reg = ADXL355_OFFSET_X_H_REG + }, + [chan_y] = { + .data_reg = ADXL355_YDATA3_REG, + .offset_reg = ADXL355_OFFSET_Y_H_REG + }, + [chan_z] = { + .data_reg = ADXL355_ZDATA3_REG, + .offset_reg = ADXL355_OFFSET_Z_H_REG + }, +}; + +struct adxl355_data { + struct regmap *regmap; + struct device *dev; + struct mutex lock; /* lock to protect op_mode */ + enum adxl355_op_mode op_mode; + enum adxl355_odr odr; + enum adxl355_hpf_3db hpf_3db; + int calibbias[3]; + int adxl355_hpf_3db_table[7][2]; + u8 transf_buf[3] ____cacheline_aligned; +}; + +static int adxl355_set_op_mode(struct adxl355_data *data, + enum adxl355_op_mode op_mode) +{ + int ret; + + if (data->op_mode == op_mode) + return 0; + + ret = regmap_update_bits(data->regmap, ADXL355_POWER_CTL_REG, + ADXL355_POWER_CTL_MODE_MSK, op_mode); + if (ret) + return ret; + + data->op_mode = op_mode; + + return ret; +} + +static void adxl355_fill_3db_frequency_table(struct adxl355_data *data) +{ + u32 multiplier; + u64 div, rem; + u64 odr; + int i; + + odr = mul_u64_u32_shr(adxl355_odr_table[data->odr][0], MEGA, 0) + + adxl355_odr_table[data->odr][1]; + + for (i = 0; i < ARRAY_SIZE(adxl355_hpf_3db_multipliers); i++) { + multiplier = adxl355_hpf_3db_multipliers[i]; + div = div64_u64_rem(mul_u64_u32_shr(odr, multiplier, 0), + TERA * 100, &rem); + + data->adxl355_hpf_3db_table[i][0] = div; + data->adxl355_hpf_3db_table[i][1] = div_u64(rem, MEGA * 100); + } +} + +static int adxl355_setup(struct adxl355_data *data) +{ + unsigned int regval; + int ret; + + ret = regmap_read(data->regmap, ADXL355_DEVID_AD_REG, ®val); + if (ret) + return ret; + + if (regval != ADXL355_DEVID_AD_VAL) { + dev_err(data->dev, "Invalid ADI ID 0x%02x\n", regval); + return -ENODEV; + } + + ret = regmap_read(data->regmap, ADXL355_DEVID_MST_REG, ®val); + if (ret) + return ret; + + if (regval != ADXL355_DEVID_MST_VAL) { + dev_err(data->dev, "Invalid MEMS ID 0x%02x\n", regval); + return -ENODEV; + } + + ret = regmap_read(data->regmap, ADXL355_PARTID_REG, ®val); + if (ret) + return ret; + + if (regval != ADXL355_PARTID_VAL) { + dev_err(data->dev, "Invalid DEV ID 0x%02x\n", regval); + return -ENODEV; + } + + /* + * Perform a software reset to make sure the device is in a consistent + * state after start-up. + */ + ret = regmap_write(data->regmap, ADXL355_RESET_REG, ADXL355_RESET_CODE); + if (ret) + return ret; + + adxl355_fill_3db_frequency_table(data); + + return adxl355_set_op_mode(data, ADXL355_MEASUREMENT); +} + +static int adxl355_get_temp_data(struct adxl355_data *data, u8 addr) +{ + return regmap_bulk_read(data->regmap, addr, data->transf_buf, 2); +} + +static int adxl355_read_axis(struct adxl355_data *data, u8 addr) +{ + int ret; + + ret = regmap_bulk_read(data->regmap, addr, data->transf_buf, + ARRAY_SIZE(data->transf_buf)); + if (ret < 0) + return ret; + + return get_unaligned_be24(data->transf_buf); +} + +static int adxl355_find_match(const int (*freq_tbl)[2], const int n, + const int val, const int val2) +{ + int i; + + for (i = 0; i < n; i++) { + if (freq_tbl[i][0] == val && freq_tbl[i][1] == val2) + return i; + } + + return -EINVAL; +} + +static int adxl355_set_odr(struct adxl355_data *data, + enum adxl355_odr odr) +{ + int ret; + + mutex_lock(&data->lock); + + if (data->odr == odr) { + mutex_unlock(&data->lock); + return 0; + } + + ret = adxl355_set_op_mode(data, ADXL355_STANDBY); + if (ret < 0) + goto err_unlock; + + ret = regmap_update_bits(data->regmap, ADXL355_FILTER_REG, + ADXL355_FILTER_ODR_MSK, + FIELD_PREP(ADXL355_FILTER_ODR_MSK, odr)); + if (ret < 0) + goto err_set_opmode; + + data->odr = odr; + adxl355_fill_3db_frequency_table(data); + + ret = adxl355_set_op_mode(data, ADXL355_MEASUREMENT); + if (ret) + goto err_set_opmode; + + mutex_unlock(&data->lock); + return 0; + +err_set_opmode: + adxl355_set_op_mode(data, ADXL355_MEASUREMENT); +err_unlock: + mutex_unlock(&data->lock); + return ret; +} + +static int adxl355_set_hpf_3db(struct adxl355_data *data, + enum adxl355_hpf_3db hpf) +{ + int ret; + + mutex_lock(&data->lock); + + if (data->hpf_3db == hpf) { + mutex_unlock(&data->lock); + return 0; + } + + ret = adxl355_set_op_mode(data, ADXL355_STANDBY); + if (ret < 0) + goto err_unlock; + + ret = regmap_update_bits(data->regmap, ADXL355_FILTER_REG, + ADXL355_FILTER_HPF_MSK, + FIELD_PREP(ADXL355_FILTER_HPF_MSK, hpf)); + if (ret) + goto err_set_opmode; + + data->hpf_3db = hpf; + + ret = adxl355_set_op_mode(data, ADXL355_MEASUREMENT); + if (ret) + goto err_set_opmode; + + mutex_unlock(&data->lock); + return 0; + +err_set_opmode: + adxl355_set_op_mode(data, ADXL355_MEASUREMENT); +err_unlock: + mutex_unlock(&data->lock); + return ret; +} + +static int adxl355_set_calibbias(struct adxl355_data *data, + enum adxl355_chans chan, int calibbias) +{ + int ret; + + mutex_lock(&data->lock); + + ret = adxl355_set_op_mode(data, ADXL355_STANDBY); + if (ret < 0) + goto err_unlock; + + put_unaligned_be16(calibbias, data->transf_buf); + ret = regmap_bulk_write(data->regmap, + adxl355_chans[chan].offset_reg, + data->transf_buf, 2); + if (ret) + goto err_set_opmode; + + data->calibbias[chan] = calibbias; + + ret = adxl355_set_op_mode(data, ADXL355_MEASUREMENT); + if (ret) + goto err_set_opmode; + + mutex_unlock(&data->lock); + return 0; + +err_set_opmode: + adxl355_set_op_mode(data, ADXL355_MEASUREMENT); +err_unlock: + mutex_unlock(&data->lock); + return ret; +} + +static int adxl355_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct adxl355_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_TEMP: + ret = adxl355_get_temp_data(data, chan->address); + if (ret < 0) + return ret; + *val = get_unaligned_be16(data->transf_buf); + + return IIO_VAL_INT; + case IIO_ACCEL: + ret = adxl355_read_axis(data, adxl355_chans[ + chan->address].data_reg); + if (ret < 0) + return ret; + *val = sign_extend32(ret >> chan->scan_type.shift, + chan->scan_type.realbits - 1); + return IIO_VAL_INT; + default: + return -EINVAL; + } + + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + /* + * The datasheet defines an intercept of 1885 LSB at 25 degC + * and a slope of -9.05 LSB/C. The following formula can be used + * to find the temperature: + * Temp = ((RAW - 1885)/(-9.05)) + 25 but this doesn't follow + * the format of the IIO which is Temp = (RAW + OFFSET) * SCALE. + * Hence using some rearranging we get the scale as -110.497238 + * and offset as -2111.25. + */ + case IIO_TEMP: + *val = -110; + *val2 = 497238; + return IIO_VAL_INT_PLUS_MICRO; + /* + * At +/- 2g with 20-bit resolution, scale is given in datasheet + * as 3.9ug/LSB = 0.0000039 * 9.80665 = 0.00003824593 m/s^2. + */ + case IIO_ACCEL: + *val = 0; + *val2 = 38245; + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + *val = -2111; + *val2 = 250000; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_CALIBBIAS: + *val = sign_extend32(data->calibbias[chan->address], 15); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = adxl355_odr_table[data->odr][0]; + *val2 = adxl355_odr_table[data->odr][1]; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + *val = data->adxl355_hpf_3db_table[data->hpf_3db][0]; + *val2 = data->adxl355_hpf_3db_table[data->hpf_3db][1]; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int adxl355_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct adxl355_data *data = iio_priv(indio_dev); + int odr_idx, hpf_idx, calibbias; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + odr_idx = adxl355_find_match(adxl355_odr_table, + ARRAY_SIZE(adxl355_odr_table), + val, val2); + if (odr_idx < 0) + return odr_idx; + + return adxl355_set_odr(data, odr_idx); + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + hpf_idx = adxl355_find_match(data->adxl355_hpf_3db_table, + ARRAY_SIZE(data->adxl355_hpf_3db_table), + val, val2); + if (hpf_idx < 0) + return hpf_idx; + + return adxl355_set_hpf_3db(data, hpf_idx); + case IIO_CHAN_INFO_CALIBBIAS: + calibbias = clamp_t(int, val, S16_MIN, S16_MAX); + + return adxl355_set_calibbias(data, chan->address, calibbias); + default: + return -EINVAL; + } +} + +static int adxl355_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct adxl355_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (const int *)adxl355_odr_table; + *type = IIO_VAL_INT_PLUS_MICRO; + /* Values are stored in a 2D matrix */ + *length = ARRAY_SIZE(adxl355_odr_table) * 2; + + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + *vals = (const int *)data->adxl355_hpf_3db_table; + *type = IIO_VAL_INT_PLUS_MICRO; + /* Values are stored in a 2D matrix */ + *length = ARRAY_SIZE(data->adxl355_hpf_3db_table) * 2; + + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static const struct iio_info adxl355_info = { + .read_raw = adxl355_read_raw, + .write_raw = adxl355_write_raw, + .read_avail = &adxl355_read_avail, +}; + +#define ADXL355_ACCEL_CHANNEL(index, reg, axis) { \ + .type = IIO_ACCEL, \ + .address = reg, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \ + .scan_type = { \ + .sign = 's', \ + .realbits = 20, \ + .storagebits = 32, \ + .shift = 4, \ + .endianness = IIO_BE, \ + } \ +} + +static const struct iio_chan_spec adxl355_channels[] = { + ADXL355_ACCEL_CHANNEL(0, chan_x, X), + ADXL355_ACCEL_CHANNEL(1, chan_y, Y), + ADXL355_ACCEL_CHANNEL(2, chan_z, Z), + { + .type = IIO_TEMP, + .address = ADXL355_TEMP2_REG, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .scan_type = { + .sign = 's', + .realbits = 12, + .storagebits = 16, + .endianness = IIO_BE, + }, + }, +}; + +int adxl355_core_probe(struct device *dev, struct regmap *regmap, + const char *name) +{ + struct adxl355_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->regmap = regmap; + data->dev = dev; + data->op_mode = ADXL355_STANDBY; + mutex_init(&data->lock); + + indio_dev->name = name; + indio_dev->info = &adxl355_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = adxl355_channels; + indio_dev->num_channels = ARRAY_SIZE(adxl355_channels); + + ret = adxl355_setup(data); + if (ret < 0) { + dev_err(dev, "ADXL355 setup failed\n"); + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_GPL(adxl355_core_probe); + +MODULE_AUTHOR("Puranjay Mohan "); +MODULE_DESCRIPTION("ADXL355 3-Axis Digital Accelerometer core driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/adxl355_i2c.c b/drivers/iio/accel/adxl355_i2c.c new file mode 100644 index 000000000000..5a987bda9060 --- /dev/null +++ b/drivers/iio/accel/adxl355_i2c.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADXL355 3-Axis Digital Accelerometer I2C driver + * + * Copyright (c) 2021 Puranjay Mohan + */ + +#include +#include +#include +#include + +#include "adxl355.h" + +static const struct regmap_config adxl355_i2c_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x2F, + .rd_table = &adxl355_readable_regs_tbl, + .wr_table = &adxl355_writeable_regs_tbl, +}; + +static int adxl355_i2c_probe(struct i2c_client *client) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, &adxl355_i2c_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "Error initializing i2c regmap: %ld\n", + PTR_ERR(regmap)); + + return PTR_ERR(regmap); + } + + return adxl355_core_probe(&client->dev, regmap, client->name); +} + +static const struct i2c_device_id adxl355_i2c_id[] = { + { "adxl355", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adxl355_i2c_id); + +static const struct of_device_id adxl355_of_match[] = { + { .compatible = "adi,adxl355" }, + { } +}; +MODULE_DEVICE_TABLE(of, adxl355_of_match); + +static struct i2c_driver adxl355_i2c_driver = { + .driver = { + .name = "adxl355_i2c", + .of_match_table = adxl355_of_match, + }, + .probe_new = adxl355_i2c_probe, + .id_table = adxl355_i2c_id, +}; +module_i2c_driver(adxl355_i2c_driver); + +MODULE_AUTHOR("Puranjay Mohan "); +MODULE_DESCRIPTION("ADXL355 3-Axis Digital Accelerometer I2C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/adxl355_spi.c b/drivers/iio/accel/adxl355_spi.c new file mode 100644 index 000000000000..fb225aeb56e3 --- /dev/null +++ b/drivers/iio/accel/adxl355_spi.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADXL355 3-Axis Digital Accelerometer SPI driver + * + * Copyright (c) 2021 Puranjay Mohan + */ + +#include +#include +#include +#include + +#include "adxl355.h" + +static const struct regmap_config adxl355_spi_regmap_config = { + .reg_bits = 7, + .pad_bits = 1, + .val_bits = 8, + .read_flag_mask = BIT(0), + .max_register = 0x2F, + .rd_table = &adxl355_readable_regs_tbl, + .wr_table = &adxl355_writeable_regs_tbl, +}; + +static int adxl355_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &adxl355_spi_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "Error initializing spi regmap: %ld\n", + PTR_ERR(regmap)); + + return PTR_ERR(regmap); + } + + return adxl355_core_probe(&spi->dev, regmap, id->name); +} + +static const struct spi_device_id adxl355_spi_id[] = { + { "adxl355", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, adxl355_spi_id); + +static const struct of_device_id adxl355_of_match[] = { + { .compatible = "adi,adxl355" }, + { } +}; +MODULE_DEVICE_TABLE(of, adxl355_of_match); + +static struct spi_driver adxl355_spi_driver = { + .driver = { + .name = "adxl355_spi", + .of_match_table = adxl355_of_match, + }, + .probe = adxl355_spi_probe, + .id_table = adxl355_spi_id, +}; +module_spi_driver(adxl355_spi_driver); + +MODULE_AUTHOR("Puranjay Mohan "); +MODULE_DESCRIPTION("ADXL355 3-Axis Digital Accelerometer SPI driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-70-g09d2 From d722f1e06fbc53eb369b39646945c1fa92068e74 Mon Sep 17 00:00:00 2001 From: Len Baker Date: Sun, 15 Aug 2021 19:42:04 +0200 Subject: drivers/iio: Remove all strcpy() uses strcpy() performs no bounds checking on the destination buffer. This could result in linear overflows beyond the end of the buffer, leading to all kinds of misbehaviors. So, remove all the uses and add devm_kstrdup() or devm_kasprintf() instead. Also, modify the "for" loop conditions to clarify the access to the st->orientation.rotation buffer. This patch is an effort to clean up the proliferation of str*() functions in the kernel and a previous step in the path to remove the strcpy function from the kernel entirely [1]. [1] https://github.com/KSPP/linux/issues/88 Signed-off-by: Len Baker Link: https://lore.kernel.org/r/20210815174204.126593-1-len.baker@gmx.com Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c | 36 +++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c index f282e9cc34c5..6aee6c989485 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c @@ -261,6 +261,7 @@ int inv_mpu_magn_set_rate(const struct inv_mpu6050_state *st, int fifo_rate) */ int inv_mpu_magn_set_orient(struct inv_mpu6050_state *st) { + struct device *dev = regmap_get_device(st->map); const char *orient; char *str; int i; @@ -279,22 +280,27 @@ int inv_mpu_magn_set_orient(struct inv_mpu6050_state *st) st->magn_orient.rotation[4] = st->orientation.rotation[1]; st->magn_orient.rotation[5] = st->orientation.rotation[2]; /* z <- -z */ - for (i = 0; i < 3; ++i) { - orient = st->orientation.rotation[6 + i]; - /* use length + 2 for adding minus sign if needed */ - str = devm_kzalloc(regmap_get_device(st->map), - strlen(orient) + 2, GFP_KERNEL); - if (str == NULL) + for (i = 6; i < 9; ++i) { + orient = st->orientation.rotation[i]; + + /* + * The value is negated according to one of the following + * rules: + * + * 1) Drop leading minus. + * 2) Leave 0 as is. + * 3) Add leading minus. + */ + if (orient[0] == '-') + str = devm_kstrdup(dev, orient + 1, GFP_KERNEL); + else if (!strcmp(orient, "0")) + str = devm_kstrdup(dev, orient, GFP_KERNEL); + else + str = devm_kasprintf(dev, GFP_KERNEL, "-%s", orient); + if (!str) return -ENOMEM; - if (strcmp(orient, "0") == 0) { - strcpy(str, orient); - } else if (orient[0] == '-') { - strcpy(str, &orient[1]); - } else { - str[0] = '-'; - strcpy(&str[1], orient); - } - st->magn_orient.rotation[6 + i] = str; + + st->magn_orient.rotation[i] = str; } break; default: -- cgit v1.2.3-70-g09d2 From 595a0590f4fbcb39d73a7c55c79a7e7921ec1614 Mon Sep 17 00:00:00 2001 From: Liam Beguin Date: Sun, 15 Aug 2021 17:33:05 -0400 Subject: iio: adc: ad7949: define and use bitfield names Replace raw configuration register values by using FIELD_PREP and defines to improve readability. Signed-off-by: Liam Beguin Link: https://lore.kernel.org/r/20210815213309.2847711-2-liambeguin@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7949.c | 55 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/drivers/iio/adc/ad7949.c b/drivers/iio/adc/ad7949.c index 1b4b3203e428..adc4487a7d56 100644 --- a/drivers/iio/adc/ad7949.c +++ b/drivers/iio/adc/ad7949.c @@ -11,13 +11,39 @@ #include #include #include +#include -#define AD7949_MASK_CHANNEL_SEL GENMASK(9, 7) -#define AD7949_MASK_TOTAL GENMASK(13, 0) -#define AD7949_OFFSET_CHANNEL_SEL 7 -#define AD7949_CFG_READ_BACK 0x1 +#define AD7949_CFG_MASK_TOTAL GENMASK(13, 0) #define AD7949_CFG_REG_SIZE_BITS 14 +/* CFG: Configuration Update */ +#define AD7949_CFG_MASK_OVERWRITE BIT(13) + +/* INCC: Input Channel Configuration */ +#define AD7949_CFG_MASK_INCC GENMASK(12, 10) +#define AD7949_CFG_VAL_INCC_UNIPOLAR_GND 7 +#define AD7949_CFG_VAL_INCC_UNIPOLAR_COMM 6 +#define AD7949_CFG_VAL_INCC_UNIPOLAR_DIFF 4 +#define AD7949_CFG_VAL_INCC_TEMP 3 +#define AD7949_CFG_VAL_INCC_BIPOLAR 2 +#define AD7949_CFG_VAL_INCC_BIPOLAR_DIFF 0 + +/* INX: Input channel Selection in a binary fashion */ +#define AD7949_CFG_MASK_INX GENMASK(9, 7) + +/* BW: select bandwidth for low-pass filter. Full or Quarter */ +#define AD7949_CFG_MASK_BW_FULL BIT(6) + +/* REF: reference/buffer selection */ +#define AD7949_CFG_MASK_REF GENMASK(5, 3) +#define AD7949_CFG_VAL_REF_EXT_BUF 7 + +/* SEQ: channel sequencer. Allows for scanning channels */ +#define AD7949_CFG_MASK_SEQ GENMASK(2, 1) + +/* RB: Read back the CFG register */ +#define AD7949_CFG_MASK_RBN BIT(0) + enum { ID_AD7949 = 0, ID_AD7682, @@ -109,8 +135,8 @@ static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val, */ for (i = 0; i < 2; i++) { ret = ad7949_spi_write_cfg(ad7949_adc, - channel << AD7949_OFFSET_CHANNEL_SEL, - AD7949_MASK_CHANNEL_SEL); + FIELD_PREP(AD7949_CFG_MASK_INX, channel), + AD7949_CFG_MASK_INX); if (ret) return ret; if (channel == ad7949_adc->current_channel) @@ -199,8 +225,8 @@ static int ad7949_spi_reg_access(struct iio_dev *indio_dev, if (readval) *readval = ad7949_adc->cfg; else - ret = ad7949_spi_write_cfg(ad7949_adc, - writeval & AD7949_MASK_TOTAL, AD7949_MASK_TOTAL); + ret = ad7949_spi_write_cfg(ad7949_adc, writeval, + AD7949_CFG_MASK_TOTAL); return ret; } @@ -214,10 +240,19 @@ static int ad7949_spi_init(struct ad7949_adc_chip *ad7949_adc) { int ret; int val; + u16 cfg; - /* Sequencer disabled, CFG readback disabled, IN0 as default channel */ ad7949_adc->current_channel = 0; - ret = ad7949_spi_write_cfg(ad7949_adc, 0x3C79, AD7949_MASK_TOTAL); + + cfg = FIELD_PREP(AD7949_CFG_MASK_OVERWRITE, 1) | + FIELD_PREP(AD7949_CFG_MASK_INCC, AD7949_CFG_VAL_INCC_UNIPOLAR_GND) | + FIELD_PREP(AD7949_CFG_MASK_INX, ad7949_adc->current_channel) | + FIELD_PREP(AD7949_CFG_MASK_BW_FULL, 1) | + FIELD_PREP(AD7949_CFG_MASK_REF, AD7949_CFG_VAL_REF_EXT_BUF) | + FIELD_PREP(AD7949_CFG_MASK_SEQ, 0x0) | + FIELD_PREP(AD7949_CFG_MASK_RBN, 1); + + ret = ad7949_spi_write_cfg(ad7949_adc, cfg, AD7949_CFG_MASK_TOTAL); /* * Do two dummy conversions to apply the first configuration setting. -- cgit v1.2.3-70-g09d2 From 0b2a740b424e68e346e3797eaa7c155575ae7c14 Mon Sep 17 00:00:00 2001 From: Liam Beguin Date: Sun, 15 Aug 2021 17:33:06 -0400 Subject: iio: adc: ad7949: enable use with non 14/16-bit controllers This driver supports devices with 14-bit and 16-bit sample sizes. This implies different SPI transfer lengths which are not always handled properly by some SPI controllers. To work around this limitation, define a big endian buffer used to split the buffer into two 8-bit messages in the event that the controller doesn't support 14-bit or 16-bit transfers. A separate buffer is introduced here to avoid performing operations on types of different endianness. Since all transfers use the same bits_per_word value, move that logic to the probe function, and let transfers default to the value defined in the struct spi_device. Signed-off-by: Liam Beguin Link: https://lore.kernel.org/r/20210815213309.2847711-3-liambeguin@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7949.c | 86 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 58 insertions(+), 28 deletions(-) diff --git a/drivers/iio/adc/ad7949.c b/drivers/iio/adc/ad7949.c index adc4487a7d56..a263d0fcec75 100644 --- a/drivers/iio/adc/ad7949.c +++ b/drivers/iio/adc/ad7949.c @@ -14,7 +14,6 @@ #include #define AD7949_CFG_MASK_TOTAL GENMASK(13, 0) -#define AD7949_CFG_REG_SIZE_BITS 14 /* CFG: Configuration Update */ #define AD7949_CFG_MASK_OVERWRITE BIT(13) @@ -71,6 +70,7 @@ static const struct ad7949_adc_spec ad7949_adc_spec[] = { * @cfg: copy of the configuration register * @current_channel: current channel in use * @buffer: buffer to send / receive data to / from device + * @buf8b: be16 buffer to exchange data with the device in 8-bit transfers */ struct ad7949_adc_chip { struct mutex lock; @@ -81,27 +81,34 @@ struct ad7949_adc_chip { u16 cfg; unsigned int current_channel; u16 buffer ____cacheline_aligned; + __be16 buf8b; }; static int ad7949_spi_write_cfg(struct ad7949_adc_chip *ad7949_adc, u16 val, u16 mask) { int ret; - int bits_per_word = ad7949_adc->resolution; - int shift = bits_per_word - AD7949_CFG_REG_SIZE_BITS; - struct spi_message msg; - struct spi_transfer tx[] = { - { - .tx_buf = &ad7949_adc->buffer, - .len = 2, - .bits_per_word = bits_per_word, - }, - }; ad7949_adc->cfg = (val & mask) | (ad7949_adc->cfg & ~mask); - ad7949_adc->buffer = ad7949_adc->cfg << shift; - spi_message_init_with_transfers(&msg, tx, 1); - ret = spi_sync(ad7949_adc->spi, &msg); + + switch (ad7949_adc->spi->bits_per_word) { + case 16: + ad7949_adc->buffer = ad7949_adc->cfg << 2; + ret = spi_write(ad7949_adc->spi, &ad7949_adc->buffer, 2); + break; + case 14: + ad7949_adc->buffer = ad7949_adc->cfg; + ret = spi_write(ad7949_adc->spi, &ad7949_adc->buffer, 2); + break; + case 8: + /* Here, type is big endian as it must be sent in two transfers */ + ad7949_adc->buf8b = cpu_to_be16(ad7949_adc->cfg << 2); + ret = spi_write(ad7949_adc->spi, &ad7949_adc->buf8b, 2); + break; + default: + dev_err(&ad7949_adc->indio_dev->dev, "unsupported BPW\n"); + return -EINVAL; + } /* * This delay is to avoid a new request before the required time to @@ -116,16 +123,6 @@ static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val, { int ret; int i; - int bits_per_word = ad7949_adc->resolution; - int mask = GENMASK(ad7949_adc->resolution - 1, 0); - struct spi_message msg; - struct spi_transfer tx[] = { - { - .rx_buf = &ad7949_adc->buffer, - .len = 2, - .bits_per_word = bits_per_word, - }, - }; /* * 1: write CFG for sample N and read old data (sample N-2) @@ -144,9 +141,11 @@ static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val, } /* 3: write something and read actual data */ - ad7949_adc->buffer = 0; - spi_message_init_with_transfers(&msg, tx, 1); - ret = spi_sync(ad7949_adc->spi, &msg); + if (ad7949_adc->spi->bits_per_word == 8) + ret = spi_read(ad7949_adc->spi, &ad7949_adc->buf8b, 2); + else + ret = spi_read(ad7949_adc->spi, &ad7949_adc->buffer, 2); + if (ret) return ret; @@ -158,7 +157,25 @@ static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val, ad7949_adc->current_channel = channel; - *val = ad7949_adc->buffer & mask; + switch (ad7949_adc->spi->bits_per_word) { + case 16: + *val = ad7949_adc->buffer; + /* Shift-out padding bits */ + *val >>= 16 - ad7949_adc->resolution; + break; + case 14: + *val = ad7949_adc->buffer & GENMASK(13, 0); + break; + case 8: + /* Here, type is big endian as data was sent in two transfers */ + *val = be16_to_cpu(ad7949_adc->buf8b); + /* Shift-out padding bits */ + *val >>= 16 - ad7949_adc->resolution; + break; + default: + dev_err(&ad7949_adc->indio_dev->dev, "unsupported BPW\n"); + return -EINVAL; + } return 0; } @@ -266,6 +283,7 @@ static int ad7949_spi_init(struct ad7949_adc_chip *ad7949_adc) static int ad7949_spi_probe(struct spi_device *spi) { + u32 spi_ctrl_mask = spi->controller->bits_per_word_mask; struct device *dev = &spi->dev; const struct ad7949_adc_spec *spec; struct ad7949_adc_chip *ad7949_adc; @@ -292,6 +310,18 @@ static int ad7949_spi_probe(struct spi_device *spi) indio_dev->num_channels = spec->num_channels; ad7949_adc->resolution = spec->resolution; + /* Set SPI bits per word */ + if (spi_ctrl_mask & SPI_BPW_MASK(ad7949_adc->resolution)) { + spi->bits_per_word = ad7949_adc->resolution; + } else if (spi_ctrl_mask == SPI_BPW_MASK(16)) { + spi->bits_per_word = 16; + } else if (spi_ctrl_mask == SPI_BPW_MASK(8)) { + spi->bits_per_word = 8; + } else { + dev_err(dev, "unable to find common BPW with spi controller\n"); + return -EINVAL; + } + ad7949_adc->vref = devm_regulator_get(dev, "vref"); if (IS_ERR(ad7949_adc->vref)) { dev_err(dev, "fail to request regulator\n"); -- cgit v1.2.3-70-g09d2 From 37930650604982930c4f516447f0fb3a61cb647f Mon Sep 17 00:00:00 2001 From: Liam Beguin Date: Sun, 15 Aug 2021 17:33:07 -0400 Subject: iio: adc: ad7949: add vref selection support Add support for selecting the voltage reference from the devicetree. This change is required to get valid readings with all three vref hardware configurations supported by the ADC. For instance if the ADC isn't provided with an external reference, the sample request must specify an internal voltage reference to get a valid reading. Signed-off-by: Liam Beguin Link: https://lore.kernel.org/r/20210815213309.2847711-4-liambeguin@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7949.c | 92 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 76 insertions(+), 16 deletions(-) diff --git a/drivers/iio/adc/ad7949.c b/drivers/iio/adc/ad7949.c index a263d0fcec75..879bdca35e04 100644 --- a/drivers/iio/adc/ad7949.c +++ b/drivers/iio/adc/ad7949.c @@ -35,7 +35,11 @@ /* REF: reference/buffer selection */ #define AD7949_CFG_MASK_REF GENMASK(5, 3) -#define AD7949_CFG_VAL_REF_EXT_BUF 7 +#define AD7949_CFG_VAL_REF_EXT_TEMP_BUF 3 +#define AD7949_CFG_VAL_REF_EXT_TEMP 2 +#define AD7949_CFG_VAL_REF_INT_4096 1 +#define AD7949_CFG_VAL_REF_INT_2500 0 +#define AD7949_CFG_VAL_REF_EXTERNAL BIT(1) /* SEQ: channel sequencer. Allows for scanning channels */ #define AD7949_CFG_MASK_SEQ GENMASK(2, 1) @@ -66,6 +70,7 @@ static const struct ad7949_adc_spec ad7949_adc_spec[] = { * @vref: regulator generating Vref * @indio_dev: reference to iio structure * @spi: reference to spi structure + * @refsel: reference selection * @resolution: resolution of the chip * @cfg: copy of the configuration register * @current_channel: current channel in use @@ -77,6 +82,7 @@ struct ad7949_adc_chip { struct regulator *vref; struct iio_dev *indio_dev; struct spi_device *spi; + u32 refsel; u8 resolution; u16 cfg; unsigned int current_channel; @@ -221,12 +227,26 @@ static int ad7949_spi_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - ret = regulator_get_voltage(ad7949_adc->vref); - if (ret < 0) - return ret; + switch (ad7949_adc->refsel) { + case AD7949_CFG_VAL_REF_INT_2500: + *val = 2500; + break; + case AD7949_CFG_VAL_REF_INT_4096: + *val = 4096; + break; + case AD7949_CFG_VAL_REF_EXT_TEMP: + case AD7949_CFG_VAL_REF_EXT_TEMP_BUF: + ret = regulator_get_voltage(ad7949_adc->vref); + if (ret < 0) + return ret; + + /* convert value back to mV */ + *val = ret / 1000; + break; + } - *val = ret / 5000; - return IIO_VAL_INT; + *val2 = (1 << ad7949_adc->resolution) - 1; + return IIO_VAL_FRACTIONAL; } return -EINVAL; @@ -265,7 +285,7 @@ static int ad7949_spi_init(struct ad7949_adc_chip *ad7949_adc) FIELD_PREP(AD7949_CFG_MASK_INCC, AD7949_CFG_VAL_INCC_UNIPOLAR_GND) | FIELD_PREP(AD7949_CFG_MASK_INX, ad7949_adc->current_channel) | FIELD_PREP(AD7949_CFG_MASK_BW_FULL, 1) | - FIELD_PREP(AD7949_CFG_MASK_REF, AD7949_CFG_VAL_REF_EXT_BUF) | + FIELD_PREP(AD7949_CFG_MASK_REF, ad7949_adc->refsel) | FIELD_PREP(AD7949_CFG_MASK_SEQ, 0x0) | FIELD_PREP(AD7949_CFG_MASK_RBN, 1); @@ -281,6 +301,11 @@ static int ad7949_spi_init(struct ad7949_adc_chip *ad7949_adc) return ret; } +static void ad7949_disable_reg(void *reg) +{ + regulator_disable(reg); +} + static int ad7949_spi_probe(struct spi_device *spi) { u32 spi_ctrl_mask = spi->controller->bits_per_word_mask; @@ -288,6 +313,7 @@ static int ad7949_spi_probe(struct spi_device *spi) const struct ad7949_adc_spec *spec; struct ad7949_adc_chip *ad7949_adc; struct iio_dev *indio_dev; + u32 tmp; int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*ad7949_adc)); @@ -322,16 +348,52 @@ static int ad7949_spi_probe(struct spi_device *spi) return -EINVAL; } - ad7949_adc->vref = devm_regulator_get(dev, "vref"); + /* Setup internal voltage reference */ + tmp = 4096000; + device_property_read_u32(dev, "adi,internal-ref-microvolt", &tmp); + + switch (tmp) { + case 2500000: + ad7949_adc->refsel = AD7949_CFG_VAL_REF_INT_2500; + break; + case 4096000: + ad7949_adc->refsel = AD7949_CFG_VAL_REF_INT_4096; + break; + default: + dev_err(dev, "unsupported internal voltage reference\n"); + return -EINVAL; + } + + /* Setup external voltage reference, buffered? */ + ad7949_adc->vref = devm_regulator_get_optional(dev, "vrefin"); if (IS_ERR(ad7949_adc->vref)) { - dev_err(dev, "fail to request regulator\n"); - return PTR_ERR(ad7949_adc->vref); + ret = PTR_ERR(ad7949_adc->vref); + if (ret != -ENODEV) + return ret; + /* unbuffered? */ + ad7949_adc->vref = devm_regulator_get_optional(dev, "vref"); + if (IS_ERR(ad7949_adc->vref)) { + ret = PTR_ERR(ad7949_adc->vref); + if (ret != -ENODEV) + return ret; + } else { + ad7949_adc->refsel = AD7949_CFG_VAL_REF_EXT_TEMP; + } + } else { + ad7949_adc->refsel = AD7949_CFG_VAL_REF_EXT_TEMP_BUF; } - ret = regulator_enable(ad7949_adc->vref); - if (ret < 0) { - dev_err(dev, "fail to enable regulator\n"); - return ret; + if (ad7949_adc->refsel & AD7949_CFG_VAL_REF_EXTERNAL) { + ret = regulator_enable(ad7949_adc->vref); + if (ret < 0) { + dev_err(dev, "fail to enable regulator\n"); + return ret; + } + + ret = devm_add_action_or_reset(dev, ad7949_disable_reg, + ad7949_adc->vref); + if (ret) + return ret; } mutex_init(&ad7949_adc->lock); @@ -352,7 +414,6 @@ static int ad7949_spi_probe(struct spi_device *spi) err: mutex_destroy(&ad7949_adc->lock); - regulator_disable(ad7949_adc->vref); return ret; } @@ -364,7 +425,6 @@ static int ad7949_spi_remove(struct spi_device *spi) iio_device_unregister(indio_dev); mutex_destroy(&ad7949_adc->lock); - regulator_disable(ad7949_adc->vref); return 0; } -- cgit v1.2.3-70-g09d2 From 9a7b7594de4fa7abd8f6a24e9e389e1e70c11685 Mon Sep 17 00:00:00 2001 From: Liam Beguin Date: Sun, 15 Aug 2021 17:33:08 -0400 Subject: dt-bindings: iio: adc: ad7949: update voltage reference bindings Update bindings to describe support for buffered and unbuffered external voltage references selection, and add adi,internal-ref-microvolt for internal voltage reference selection. Signed-off-by: Liam Beguin Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20210815213309.2847711-5-liambeguin@gmail.com Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/adi,ad7949.yaml | 51 ++++++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7949.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7949.yaml index 9b56bd4d5510..0b10ed5f74ae 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7949.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7949.yaml @@ -26,19 +26,43 @@ properties: reg: maxItems: 1 + vrefin-supply: + description: + Buffered ADC reference voltage supply. + vref-supply: description: - ADC reference voltage supply + Unbuffered ADC reference voltage supply. + + adi,internal-ref-microvolt: + description: | + Internal reference voltage selection in microvolts. + + If no internal reference is specified, the channel will default to the + external reference defined by vrefin-supply (or vref-supply). + vrefin-supply will take precedence over vref-supply if both are defined. + + If no supplies are defined, the reference selection will default to + 4096mV internal reference. + + enum: [2500000, 4096000] + default: 4096000 + spi-max-frequency: true - "#io-channel-cells": + '#io-channel-cells': const: 1 + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + required: - compatible - reg - - vref-supply additionalProperties: false @@ -49,9 +73,30 @@ examples: #size-cells = <0>; adc@0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,ad7949"; reg = <0>; vref-supply = <&vdd_supply>; }; + + adc@1 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "adi,ad7949"; + reg = <1>; + vrefin-supply = <&vdd_supply>; + }; + + adc@2 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "adi,ad7949"; + reg = <2>; + adi,internal-ref-microvolt = <4096000>; + }; }; ... -- cgit v1.2.3-70-g09d2 From 870d26f6599d8f9768f83004a5c9a25564add3f0 Mon Sep 17 00:00:00 2001 From: Liam Beguin Date: Sun, 15 Aug 2021 17:33:09 -0400 Subject: iio: adc: ad7949: use devm managed functions Switch to devm_iio_device_register to finalize devm migration. This removes the use for iio_device_unregister() and since mutex_destroy() is not necessary here, remove it altogether. Signed-off-by: Liam Beguin Link: https://lore.kernel.org/r/20210815213309.2847711-6-liambeguin@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7949.c | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/drivers/iio/adc/ad7949.c b/drivers/iio/adc/ad7949.c index 879bdca35e04..44bb5fde83de 100644 --- a/drivers/iio/adc/ad7949.c +++ b/drivers/iio/adc/ad7949.c @@ -401,34 +401,16 @@ static int ad7949_spi_probe(struct spi_device *spi) ret = ad7949_spi_init(ad7949_adc); if (ret) { dev_err(dev, "enable to init this device: %d\n", ret); - goto err; + return ret; } - ret = iio_device_register(indio_dev); - if (ret) { + ret = devm_iio_device_register(dev, indio_dev); + if (ret) dev_err(dev, "fail to register iio device: %d\n", ret); - goto err; - } - - return 0; - -err: - mutex_destroy(&ad7949_adc->lock); return ret; } -static int ad7949_spi_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad7949_adc_chip *ad7949_adc = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - mutex_destroy(&ad7949_adc->lock); - - return 0; -} - static const struct of_device_id ad7949_spi_of_id[] = { { .compatible = "adi,ad7949" }, { .compatible = "adi,ad7682" }, @@ -451,7 +433,6 @@ static struct spi_driver ad7949_spi_driver = { .of_match_table = ad7949_spi_of_id, }, .probe = ad7949_spi_probe, - .remove = ad7949_spi_remove, .id_table = ad7949_spi_id, }; module_spi_driver(ad7949_spi_driver); -- cgit v1.2.3-70-g09d2 From 9f0b3e0cc0c88618aa9e5cecef747b1337ae0a5d Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 23 Aug 2021 14:22:00 +0300 Subject: iio: st_sensors: disable regulators after device unregistration Up until commit ea7e586bdd331 ("iio: st_sensors: move regulator retrieveal to core") only the ST pressure driver seems to have had any regulator disable. After that commit, the regulator handling was moved into the common st_sensors logic. In all instances of this regulator handling, the regulators were disabled before unregistering the IIO device. This can cause issues where the device would be powered down and still be available to userspace, allowing it to send invalid/garbage data. This change moves the st_sensors_power_disable() after the common probe functions. These common probe functions also handle unregistering the IIO device. Fixes: 774487611c949 ("iio: pressure-core: st: Provide support for the Vdd power supply") Fixes: ea7e586bdd331 ("iio: st_sensors: move regulator retrieveal to core") Cc: Lee Jones Cc: Denis CIOCCA Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210823112204.243255-2-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_i2c.c | 4 ++-- drivers/iio/accel/st_accel_spi.c | 4 ++-- drivers/iio/gyro/st_gyro_i2c.c | 4 ++-- drivers/iio/gyro/st_gyro_spi.c | 4 ++-- drivers/iio/magnetometer/st_magn_i2c.c | 4 ++-- drivers/iio/magnetometer/st_magn_spi.c | 4 ++-- drivers/iio/pressure/st_pressure_i2c.c | 4 ++-- drivers/iio/pressure/st_pressure_spi.c | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index f711756e41e3..cba57459e90a 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -193,10 +193,10 @@ static int st_accel_i2c_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); - st_sensors_power_disable(indio_dev); - st_accel_common_remove(indio_dev); + st_sensors_power_disable(indio_dev); + return 0; } diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index bb45d9ff95b8..5167fae1ee8e 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -143,10 +143,10 @@ static int st_accel_spi_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); - st_sensors_power_disable(indio_dev); - st_accel_common_remove(indio_dev); + st_sensors_power_disable(indio_dev); + return 0; } diff --git a/drivers/iio/gyro/st_gyro_i2c.c b/drivers/iio/gyro/st_gyro_i2c.c index 3ef86e16ee65..a8164fe48b85 100644 --- a/drivers/iio/gyro/st_gyro_i2c.c +++ b/drivers/iio/gyro/st_gyro_i2c.c @@ -106,10 +106,10 @@ static int st_gyro_i2c_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); - st_sensors_power_disable(indio_dev); - st_gyro_common_remove(indio_dev); + st_sensors_power_disable(indio_dev); + return 0; } diff --git a/drivers/iio/gyro/st_gyro_spi.c b/drivers/iio/gyro/st_gyro_spi.c index 41d835493347..9d8916871b4b 100644 --- a/drivers/iio/gyro/st_gyro_spi.c +++ b/drivers/iio/gyro/st_gyro_spi.c @@ -110,10 +110,10 @@ static int st_gyro_spi_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); - st_sensors_power_disable(indio_dev); - st_gyro_common_remove(indio_dev); + st_sensors_power_disable(indio_dev); + return 0; } diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c index 2dfe4ee99591..fa78f0a3b53e 100644 --- a/drivers/iio/magnetometer/st_magn_i2c.c +++ b/drivers/iio/magnetometer/st_magn_i2c.c @@ -102,10 +102,10 @@ static int st_magn_i2c_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); - st_sensors_power_disable(indio_dev); - st_magn_common_remove(indio_dev); + st_sensors_power_disable(indio_dev); + return 0; } diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c index fba978796395..ff43cbf61b05 100644 --- a/drivers/iio/magnetometer/st_magn_spi.c +++ b/drivers/iio/magnetometer/st_magn_spi.c @@ -96,10 +96,10 @@ static int st_magn_spi_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); - st_sensors_power_disable(indio_dev); - st_magn_common_remove(indio_dev); + st_sensors_power_disable(indio_dev); + return 0; } diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c index 52fa98f24478..6215de677017 100644 --- a/drivers/iio/pressure/st_pressure_i2c.c +++ b/drivers/iio/pressure/st_pressure_i2c.c @@ -119,10 +119,10 @@ static int st_press_i2c_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); - st_sensors_power_disable(indio_dev); - st_press_common_remove(indio_dev); + st_sensors_power_disable(indio_dev); + return 0; } diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c index ee393df54cee..5001aae8f00b 100644 --- a/drivers/iio/pressure/st_pressure_spi.c +++ b/drivers/iio/pressure/st_pressure_spi.c @@ -102,10 +102,10 @@ static int st_press_spi_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); - st_sensors_power_disable(indio_dev); - st_press_common_remove(indio_dev); + st_sensors_power_disable(indio_dev); + return 0; } -- cgit v1.2.3-70-g09d2 From 82bcb7fb649844a561ff1ac2e2ace4252bdff793 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 23 Aug 2021 14:22:01 +0300 Subject: iio: st_sensors: remove st_sensors_deallocate_trigger() function This change converts the st_sensors_allocate_trigger() to use device-managed functions. The parent device of the IIO device object is used. This is based on the assumption that all other devm_ calls in the ST sensors use this reference. That makes the st_sensors_deallocate_trigger() function un-needed, so it can be removed. Reviewed-by: Andy Shevchenko Signed-off-by: Alexandru Ardelean Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20210823112204.243255-3-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_core.c | 18 +-------- drivers/iio/common/st_sensors/st_sensors_trigger.c | 45 ++++++++-------------- drivers/iio/gyro/st_gyro_core.c | 18 +-------- drivers/iio/magnetometer/st_magn_core.c | 18 +-------- drivers/iio/pressure/st_pressure_core.c | 18 +-------- include/linux/iio/common/st_sensors.h | 5 --- 6 files changed, 19 insertions(+), 103 deletions(-) diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index f1e6ec380667..a7be1633bff1 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -1380,29 +1380,13 @@ int st_accel_common_probe(struct iio_dev *indio_dev) return err; } - err = iio_device_register(indio_dev); - if (err) - goto st_accel_device_register_error; - - dev_info(&indio_dev->dev, "registered accelerometer %s\n", - indio_dev->name); - - return 0; - -st_accel_device_register_error: - if (adata->irq > 0) - st_sensors_deallocate_trigger(indio_dev); - return err; + return iio_device_register(indio_dev); } EXPORT_SYMBOL(st_accel_common_probe); void st_accel_common_remove(struct iio_dev *indio_dev) { - struct st_sensor_data *adata = iio_priv(indio_dev); - iio_device_unregister(indio_dev); - if (adata->irq > 0) - st_sensors_deallocate_trigger(indio_dev); } EXPORT_SYMBOL(st_accel_common_remove); diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c index 64e0a748a855..d022157b66a2 100644 --- a/drivers/iio/common/st_sensors/st_sensors_trigger.c +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -119,11 +119,12 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, const struct iio_trigger_ops *trigger_ops) { struct st_sensor_data *sdata = iio_priv(indio_dev); + struct device *parent = indio_dev->dev.parent; unsigned long irq_trig; int err; - sdata->trig = iio_trigger_alloc(sdata->dev, "%s-trigger", - indio_dev->name); + sdata->trig = devm_iio_trigger_alloc(parent, "%s-trigger", + indio_dev->name); if (sdata->trig == NULL) { dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n"); return -ENOMEM; @@ -153,7 +154,7 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, sdata->sensor_settings->drdy_irq.addr_ihl, sdata->sensor_settings->drdy_irq.mask_ihl, 1); if (err < 0) - goto iio_trigger_free; + return err; dev_info(&indio_dev->dev, "interrupts on the falling edge or active low level\n"); } @@ -179,8 +180,7 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, if (!sdata->sensor_settings->drdy_irq.stat_drdy.addr) { dev_err(&indio_dev->dev, "edge IRQ not supported w/o stat register.\n"); - err = -EOPNOTSUPP; - goto iio_trigger_free; + return -EOPNOTSUPP; } sdata->edge_irq = true; } else { @@ -205,44 +205,29 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, sdata->sensor_settings->drdy_irq.stat_drdy.addr) irq_trig |= IRQF_SHARED; - err = request_threaded_irq(sdata->irq, - st_sensors_irq_handler, - st_sensors_irq_thread, - irq_trig, - sdata->trig->name, - sdata->trig); + err = devm_request_threaded_irq(parent, + sdata->irq, + st_sensors_irq_handler, + st_sensors_irq_thread, + irq_trig, + sdata->trig->name, + sdata->trig); if (err) { dev_err(&indio_dev->dev, "failed to request trigger IRQ.\n"); - goto iio_trigger_free; + return err; } - err = iio_trigger_register(sdata->trig); + err = devm_iio_trigger_register(parent, sdata->trig); if (err < 0) { dev_err(&indio_dev->dev, "failed to register iio trigger.\n"); - goto iio_trigger_register_error; + return err; } indio_dev->trig = iio_trigger_get(sdata->trig); return 0; - -iio_trigger_register_error: - free_irq(sdata->irq, sdata->trig); -iio_trigger_free: - iio_trigger_free(sdata->trig); - return err; } EXPORT_SYMBOL(st_sensors_allocate_trigger); -void st_sensors_deallocate_trigger(struct iio_dev *indio_dev) -{ - struct st_sensor_data *sdata = iio_priv(indio_dev); - - iio_trigger_unregister(sdata->trig); - free_irq(sdata->irq, sdata->trig); - iio_trigger_free(sdata->trig); -} -EXPORT_SYMBOL(st_sensors_deallocate_trigger); - int st_sensors_validate_device(struct iio_trigger *trig, struct iio_dev *indio_dev) { diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index e8fc8af65143..cb539b47cdf4 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -515,29 +515,13 @@ int st_gyro_common_probe(struct iio_dev *indio_dev) return err; } - err = iio_device_register(indio_dev); - if (err) - goto st_gyro_device_register_error; - - dev_info(&indio_dev->dev, "registered gyroscope %s\n", - indio_dev->name); - - return 0; - -st_gyro_device_register_error: - if (gdata->irq > 0) - st_sensors_deallocate_trigger(indio_dev); - return err; + return iio_device_register(indio_dev); } EXPORT_SYMBOL(st_gyro_common_probe); void st_gyro_common_remove(struct iio_dev *indio_dev) { - struct st_sensor_data *gdata = iio_priv(indio_dev); - iio_device_unregister(indio_dev); - if (gdata->irq > 0) - st_sensors_deallocate_trigger(indio_dev); } EXPORT_SYMBOL(st_gyro_common_remove); diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index 9ffd50d796bf..5be85e2405a5 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -650,29 +650,13 @@ int st_magn_common_probe(struct iio_dev *indio_dev) return err; } - err = iio_device_register(indio_dev); - if (err) - goto st_magn_device_register_error; - - dev_info(&indio_dev->dev, "registered magnetometer %s\n", - indio_dev->name); - - return 0; - -st_magn_device_register_error: - if (mdata->irq > 0) - st_sensors_deallocate_trigger(indio_dev); - return err; + return iio_device_register(indio_dev); } EXPORT_SYMBOL(st_magn_common_probe); void st_magn_common_remove(struct iio_dev *indio_dev) { - struct st_sensor_data *mdata = iio_priv(indio_dev); - iio_device_unregister(indio_dev); - if (mdata->irq > 0) - st_sensors_deallocate_trigger(indio_dev); } EXPORT_SYMBOL(st_magn_common_remove); diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index ab1c17fac807..17ebb5171d4c 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -721,29 +721,13 @@ int st_press_common_probe(struct iio_dev *indio_dev) return err; } - err = iio_device_register(indio_dev); - if (err) - goto st_press_device_register_error; - - dev_info(&indio_dev->dev, "registered pressure sensor %s\n", - indio_dev->name); - - return err; - -st_press_device_register_error: - if (press_data->irq > 0) - st_sensors_deallocate_trigger(indio_dev); - return err; + return iio_device_register(indio_dev); } EXPORT_SYMBOL(st_press_common_probe); void st_press_common_remove(struct iio_dev *indio_dev) { - struct st_sensor_data *press_data = iio_priv(indio_dev); - iio_device_unregister(indio_dev); - if (press_data->irq > 0) - st_sensors_deallocate_trigger(indio_dev); } EXPORT_SYMBOL(st_press_common_remove); diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h index 8bdbaf3f3796..e74b55244f35 100644 --- a/include/linux/iio/common/st_sensors.h +++ b/include/linux/iio/common/st_sensors.h @@ -273,7 +273,6 @@ irqreturn_t st_sensors_trigger_handler(int irq, void *p); int st_sensors_allocate_trigger(struct iio_dev *indio_dev, const struct iio_trigger_ops *trigger_ops); -void st_sensors_deallocate_trigger(struct iio_dev *indio_dev); int st_sensors_validate_device(struct iio_trigger *trig, struct iio_dev *indio_dev); #else @@ -282,10 +281,6 @@ static inline int st_sensors_allocate_trigger(struct iio_dev *indio_dev, { return 0; } -static inline void st_sensors_deallocate_trigger(struct iio_dev *indio_dev) -{ - return; -} #define st_sensors_validate_device NULL #endif -- cgit v1.2.3-70-g09d2 From 5363c6c17b1014f696bf0b2984af4b694062ce8f Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 23 Aug 2021 14:22:02 +0300 Subject: iio: st_sensors: remove st_sensors_power_disable() function This change converts the st_sensors_power_enable() function to use devm_add_action_or_reset() handlers to register regulator_disable hooks for when the drivers get unloaded. The parent device of the IIO device object is used. This is based on the assumption that all other devm_ calls in the ST sensors use this reference. This makes the st_sensors_power_disable() un-needed. Removing this also changes unload order a bit, as all ST drivers would call st_sensors_power_disable() first and iio_device_unregister() after that. Reviewed-by: Andy Shevchenko Signed-off-by: Alexandru Ardelean Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20210823112204.243255-4-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_i2c.c | 13 +--------- drivers/iio/accel/st_accel_spi.c | 13 +--------- drivers/iio/common/st_sensors/st_sensors_core.c | 34 +++++++++++-------------- drivers/iio/gyro/st_gyro_i2c.c | 13 +--------- drivers/iio/gyro/st_gyro_spi.c | 13 +--------- drivers/iio/magnetometer/st_magn_i2c.c | 13 +--------- drivers/iio/magnetometer/st_magn_spi.c | 13 +--------- drivers/iio/pressure/st_pressure_i2c.c | 13 +--------- drivers/iio/pressure/st_pressure_spi.c | 13 +--------- include/linux/iio/common/st_sensors.h | 2 -- 10 files changed, 23 insertions(+), 117 deletions(-) diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index cba57459e90a..b377575efc41 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -177,16 +177,7 @@ static int st_accel_i2c_probe(struct i2c_client *client) if (ret) return ret; - ret = st_accel_common_probe(indio_dev); - if (ret < 0) - goto st_accel_power_off; - - return 0; - -st_accel_power_off: - st_sensors_power_disable(indio_dev); - - return ret; + return st_accel_common_probe(indio_dev); } static int st_accel_i2c_remove(struct i2c_client *client) @@ -195,8 +186,6 @@ static int st_accel_i2c_remove(struct i2c_client *client) st_accel_common_remove(indio_dev); - st_sensors_power_disable(indio_dev); - return 0; } diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index 5167fae1ee8e..4ca87e73bdb3 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -127,16 +127,7 @@ static int st_accel_spi_probe(struct spi_device *spi) if (err) return err; - err = st_accel_common_probe(indio_dev); - if (err < 0) - goto st_accel_power_off; - - return 0; - -st_accel_power_off: - st_sensors_power_disable(indio_dev); - - return err; + return st_accel_common_probe(indio_dev); } static int st_accel_spi_remove(struct spi_device *spi) @@ -145,8 +136,6 @@ static int st_accel_spi_remove(struct spi_device *spi) st_accel_common_remove(indio_dev); - st_sensors_power_disable(indio_dev); - return 0; } diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index 0bbb090b108c..a5a140de9a23 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -215,13 +215,19 @@ int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable) } EXPORT_SYMBOL(st_sensors_set_axis_enable); +static void st_reg_disable(void *reg) +{ + regulator_disable(reg); +} + int st_sensors_power_enable(struct iio_dev *indio_dev) { struct st_sensor_data *pdata = iio_priv(indio_dev); + struct device *parent = indio_dev->dev.parent; int err; /* Regulators not mandatory, but if requested we should enable them. */ - pdata->vdd = devm_regulator_get(indio_dev->dev.parent, "vdd"); + pdata->vdd = devm_regulator_get(parent, "vdd"); if (IS_ERR(pdata->vdd)) { dev_err(&indio_dev->dev, "unable to get Vdd supply\n"); return PTR_ERR(pdata->vdd); @@ -233,36 +239,26 @@ int st_sensors_power_enable(struct iio_dev *indio_dev) return err; } - pdata->vdd_io = devm_regulator_get(indio_dev->dev.parent, "vddio"); + err = devm_add_action_or_reset(parent, st_reg_disable, pdata->vdd); + if (err) + return err; + + pdata->vdd_io = devm_regulator_get(parent, "vddio"); if (IS_ERR(pdata->vdd_io)) { dev_err(&indio_dev->dev, "unable to get Vdd_IO supply\n"); - err = PTR_ERR(pdata->vdd_io); - goto st_sensors_disable_vdd; + return PTR_ERR(pdata->vdd_io); } err = regulator_enable(pdata->vdd_io); if (err != 0) { dev_warn(&indio_dev->dev, "Failed to enable specified Vdd_IO supply\n"); - goto st_sensors_disable_vdd; + return err; } - return 0; - -st_sensors_disable_vdd: - regulator_disable(pdata->vdd); - return err; + return devm_add_action_or_reset(parent, st_reg_disable, pdata->vdd_io); } EXPORT_SYMBOL(st_sensors_power_enable); -void st_sensors_power_disable(struct iio_dev *indio_dev) -{ - struct st_sensor_data *pdata = iio_priv(indio_dev); - - regulator_disable(pdata->vdd); - regulator_disable(pdata->vdd_io); -} -EXPORT_SYMBOL(st_sensors_power_disable); - static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev, struct st_sensors_platform_data *pdata) { diff --git a/drivers/iio/gyro/st_gyro_i2c.c b/drivers/iio/gyro/st_gyro_i2c.c index a8164fe48b85..0bd80dfd389f 100644 --- a/drivers/iio/gyro/st_gyro_i2c.c +++ b/drivers/iio/gyro/st_gyro_i2c.c @@ -90,16 +90,7 @@ static int st_gyro_i2c_probe(struct i2c_client *client, if (err) return err; - err = st_gyro_common_probe(indio_dev); - if (err < 0) - goto st_gyro_power_off; - - return 0; - -st_gyro_power_off: - st_sensors_power_disable(indio_dev); - - return err; + return st_gyro_common_probe(indio_dev); } static int st_gyro_i2c_remove(struct i2c_client *client) @@ -108,8 +99,6 @@ static int st_gyro_i2c_remove(struct i2c_client *client) st_gyro_common_remove(indio_dev); - st_sensors_power_disable(indio_dev); - return 0; } diff --git a/drivers/iio/gyro/st_gyro_spi.c b/drivers/iio/gyro/st_gyro_spi.c index 9d8916871b4b..f74b09fa5cde 100644 --- a/drivers/iio/gyro/st_gyro_spi.c +++ b/drivers/iio/gyro/st_gyro_spi.c @@ -94,16 +94,7 @@ static int st_gyro_spi_probe(struct spi_device *spi) if (err) return err; - err = st_gyro_common_probe(indio_dev); - if (err < 0) - goto st_gyro_power_off; - - return 0; - -st_gyro_power_off: - st_sensors_power_disable(indio_dev); - - return err; + return st_gyro_common_probe(indio_dev); } static int st_gyro_spi_remove(struct spi_device *spi) @@ -112,8 +103,6 @@ static int st_gyro_spi_remove(struct spi_device *spi) st_gyro_common_remove(indio_dev); - st_sensors_power_disable(indio_dev); - return 0; } diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c index fa78f0a3b53e..0a5117dffcf4 100644 --- a/drivers/iio/magnetometer/st_magn_i2c.c +++ b/drivers/iio/magnetometer/st_magn_i2c.c @@ -86,16 +86,7 @@ static int st_magn_i2c_probe(struct i2c_client *client, if (err) return err; - err = st_magn_common_probe(indio_dev); - if (err < 0) - goto st_magn_power_off; - - return 0; - -st_magn_power_off: - st_sensors_power_disable(indio_dev); - - return err; + return st_magn_common_probe(indio_dev); } static int st_magn_i2c_remove(struct i2c_client *client) @@ -104,8 +95,6 @@ static int st_magn_i2c_remove(struct i2c_client *client) st_magn_common_remove(indio_dev); - st_sensors_power_disable(indio_dev); - return 0; } diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c index ff43cbf61b05..1f3bf02b24e0 100644 --- a/drivers/iio/magnetometer/st_magn_spi.c +++ b/drivers/iio/magnetometer/st_magn_spi.c @@ -80,16 +80,7 @@ static int st_magn_spi_probe(struct spi_device *spi) if (err) return err; - err = st_magn_common_probe(indio_dev); - if (err < 0) - goto st_magn_power_off; - - return 0; - -st_magn_power_off: - st_sensors_power_disable(indio_dev); - - return err; + return st_magn_common_probe(indio_dev); } static int st_magn_spi_remove(struct spi_device *spi) @@ -98,8 +89,6 @@ static int st_magn_spi_remove(struct spi_device *spi) st_magn_common_remove(indio_dev); - st_sensors_power_disable(indio_dev); - return 0; } diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c index 6215de677017..afeeab485c0d 100644 --- a/drivers/iio/pressure/st_pressure_i2c.c +++ b/drivers/iio/pressure/st_pressure_i2c.c @@ -103,16 +103,7 @@ static int st_press_i2c_probe(struct i2c_client *client, if (ret) return ret; - ret = st_press_common_probe(indio_dev); - if (ret < 0) - goto st_press_power_off; - - return 0; - -st_press_power_off: - st_sensors_power_disable(indio_dev); - - return ret; + return st_press_common_probe(indio_dev); } static int st_press_i2c_remove(struct i2c_client *client) @@ -121,8 +112,6 @@ static int st_press_i2c_remove(struct i2c_client *client) st_press_common_remove(indio_dev); - st_sensors_power_disable(indio_dev); - return 0; } diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c index 5001aae8f00b..834ad6d40a70 100644 --- a/drivers/iio/pressure/st_pressure_spi.c +++ b/drivers/iio/pressure/st_pressure_spi.c @@ -86,16 +86,7 @@ static int st_press_spi_probe(struct spi_device *spi) if (err) return err; - err = st_press_common_probe(indio_dev); - if (err < 0) - goto st_press_power_off; - - return 0; - -st_press_power_off: - st_sensors_power_disable(indio_dev); - - return err; + return st_press_common_probe(indio_dev); } static int st_press_spi_remove(struct spi_device *spi) @@ -104,8 +95,6 @@ static int st_press_spi_remove(struct spi_device *spi) st_press_common_remove(indio_dev); - st_sensors_power_disable(indio_dev); - return 0; } diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h index e74b55244f35..fc90c202d15e 100644 --- a/include/linux/iio/common/st_sensors.h +++ b/include/linux/iio/common/st_sensors.h @@ -293,8 +293,6 @@ int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable); int st_sensors_power_enable(struct iio_dev *indio_dev); -void st_sensors_power_disable(struct iio_dev *indio_dev); - int st_sensors_debugfs_reg_access(struct iio_dev *indio_dev, unsigned reg, unsigned writeval, unsigned *readval); -- cgit v1.2.3-70-g09d2 From 6b658c31bb6bc033f6ae60141c676383fb78784c Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 23 Aug 2021 14:22:03 +0300 Subject: iio: st_sensors: remove all driver remove functions At this point all ST driver remove functions do iio_device_unregister(). This change removes them from them and replaces all iio_device_register() with devm_iio_device_register(). This can be done in a single change relatively easy, since all these remove functions are define in st_sensors.h. Reviewed-by: Andy Shevchenko Signed-off-by: Alexandru Ardelean Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20210823112204.243255-5-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_core.c | 9 ++------- drivers/iio/accel/st_accel_i2c.c | 10 ---------- drivers/iio/accel/st_accel_spi.c | 10 ---------- drivers/iio/gyro/st_gyro_core.c | 9 ++------- drivers/iio/gyro/st_gyro_i2c.c | 10 ---------- drivers/iio/gyro/st_gyro_spi.c | 10 ---------- drivers/iio/imu/st_lsm9ds0/st_lsm9ds0.h | 1 - drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c | 15 +-------------- drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c | 6 ------ drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c | 6 ------ drivers/iio/magnetometer/st_magn_core.c | 9 ++------- drivers/iio/magnetometer/st_magn_i2c.c | 10 ---------- drivers/iio/magnetometer/st_magn_spi.c | 10 ---------- drivers/iio/pressure/st_pressure_core.c | 9 ++------- drivers/iio/pressure/st_pressure_i2c.c | 10 ---------- drivers/iio/pressure/st_pressure_spi.c | 10 ---------- include/linux/iio/common/st_sensors.h | 4 ---- 17 files changed, 9 insertions(+), 139 deletions(-) diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index a7be1633bff1..01695abd9d2f 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -1335,6 +1335,7 @@ int st_accel_common_probe(struct iio_dev *indio_dev) { struct st_sensor_data *adata = iio_priv(indio_dev); struct st_sensors_platform_data *pdata = dev_get_platdata(adata->dev); + struct device *parent = indio_dev->dev.parent; int err; indio_dev->modes = INDIO_DIRECT_MODE; @@ -1380,16 +1381,10 @@ int st_accel_common_probe(struct iio_dev *indio_dev) return err; } - return iio_device_register(indio_dev); + return devm_iio_device_register(parent, indio_dev); } EXPORT_SYMBOL(st_accel_common_probe); -void st_accel_common_remove(struct iio_dev *indio_dev) -{ - iio_device_unregister(indio_dev); -} -EXPORT_SYMBOL(st_accel_common_remove); - MODULE_AUTHOR("Denis Ciocca "); MODULE_DESCRIPTION("STMicroelectronics accelerometers driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index b377575efc41..c0ce78eebad9 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -180,15 +180,6 @@ static int st_accel_i2c_probe(struct i2c_client *client) return st_accel_common_probe(indio_dev); } -static int st_accel_i2c_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - - st_accel_common_remove(indio_dev); - - return 0; -} - static struct i2c_driver st_accel_driver = { .driver = { .name = "st-accel-i2c", @@ -196,7 +187,6 @@ static struct i2c_driver st_accel_driver = { .acpi_match_table = ACPI_PTR(st_accel_acpi_match), }, .probe_new = st_accel_i2c_probe, - .remove = st_accel_i2c_remove, .id_table = st_accel_id_table, }; module_i2c_driver(st_accel_driver); diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index 4ca87e73bdb3..b74a1c6d03de 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -130,15 +130,6 @@ static int st_accel_spi_probe(struct spi_device *spi) return st_accel_common_probe(indio_dev); } -static int st_accel_spi_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - - st_accel_common_remove(indio_dev); - - return 0; -} - static const struct spi_device_id st_accel_id_table[] = { { LIS3DH_ACCEL_DEV_NAME }, { LSM330D_ACCEL_DEV_NAME }, @@ -166,7 +157,6 @@ static struct spi_driver st_accel_driver = { .of_match_table = st_accel_of_match, }, .probe = st_accel_spi_probe, - .remove = st_accel_spi_remove, .id_table = st_accel_id_table, }; module_spi_driver(st_accel_driver); diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index cb539b47cdf4..3609082a6778 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -478,6 +478,7 @@ int st_gyro_common_probe(struct iio_dev *indio_dev) { struct st_sensor_data *gdata = iio_priv(indio_dev); struct st_sensors_platform_data *pdata; + struct device *parent = indio_dev->dev.parent; int err; indio_dev->modes = INDIO_DIRECT_MODE; @@ -515,16 +516,10 @@ int st_gyro_common_probe(struct iio_dev *indio_dev) return err; } - return iio_device_register(indio_dev); + return devm_iio_device_register(parent, indio_dev); } EXPORT_SYMBOL(st_gyro_common_probe); -void st_gyro_common_remove(struct iio_dev *indio_dev) -{ - iio_device_unregister(indio_dev); -} -EXPORT_SYMBOL(st_gyro_common_remove); - MODULE_AUTHOR("Denis Ciocca "); MODULE_DESCRIPTION("STMicroelectronics gyroscopes driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/gyro/st_gyro_i2c.c b/drivers/iio/gyro/st_gyro_i2c.c index 0bd80dfd389f..163c7ba300c1 100644 --- a/drivers/iio/gyro/st_gyro_i2c.c +++ b/drivers/iio/gyro/st_gyro_i2c.c @@ -93,15 +93,6 @@ static int st_gyro_i2c_probe(struct i2c_client *client, return st_gyro_common_probe(indio_dev); } -static int st_gyro_i2c_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - - st_gyro_common_remove(indio_dev); - - return 0; -} - static const struct i2c_device_id st_gyro_id_table[] = { { L3G4200D_GYRO_DEV_NAME }, { LSM330D_GYRO_DEV_NAME }, @@ -122,7 +113,6 @@ static struct i2c_driver st_gyro_driver = { .of_match_table = st_gyro_of_match, }, .probe = st_gyro_i2c_probe, - .remove = st_gyro_i2c_remove, .id_table = st_gyro_id_table, }; module_i2c_driver(st_gyro_driver); diff --git a/drivers/iio/gyro/st_gyro_spi.c b/drivers/iio/gyro/st_gyro_spi.c index f74b09fa5cde..b0023f9b9771 100644 --- a/drivers/iio/gyro/st_gyro_spi.c +++ b/drivers/iio/gyro/st_gyro_spi.c @@ -97,15 +97,6 @@ static int st_gyro_spi_probe(struct spi_device *spi) return st_gyro_common_probe(indio_dev); } -static int st_gyro_spi_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - - st_gyro_common_remove(indio_dev); - - return 0; -} - static const struct spi_device_id st_gyro_id_table[] = { { L3G4200D_GYRO_DEV_NAME }, { LSM330D_GYRO_DEV_NAME }, @@ -126,7 +117,6 @@ static struct spi_driver st_gyro_driver = { .of_match_table = st_gyro_of_match, }, .probe = st_gyro_spi_probe, - .remove = st_gyro_spi_remove, .id_table = st_gyro_id_table, }; module_spi_driver(st_gyro_driver); diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0.h b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0.h index 146393afd9a7..76678cdefb07 100644 --- a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0.h +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0.h @@ -18,6 +18,5 @@ struct st_lsm9ds0 { }; int st_lsm9ds0_probe(struct st_lsm9ds0 *lsm9ds0, struct regmap *regmap); -int st_lsm9ds0_remove(struct st_lsm9ds0 *lsm9ds0); #endif /* ST_LSM9DS0_H */ diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c index 5e6625140db7..d276f663fe57 100644 --- a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c @@ -142,23 +142,10 @@ int st_lsm9ds0_probe(struct st_lsm9ds0 *lsm9ds0, struct regmap *regmap) return ret; /* Setup magnetometer device */ - ret = st_lsm9ds0_probe_magn(lsm9ds0, regmap); - if (ret) - st_accel_common_remove(lsm9ds0->accel); - - return ret; + return st_lsm9ds0_probe_magn(lsm9ds0, regmap); } EXPORT_SYMBOL_GPL(st_lsm9ds0_probe); -int st_lsm9ds0_remove(struct st_lsm9ds0 *lsm9ds0) -{ - st_magn_common_remove(lsm9ds0->magn); - st_accel_common_remove(lsm9ds0->accel); - - return 0; -} -EXPORT_SYMBOL_GPL(st_lsm9ds0_remove); - MODULE_AUTHOR("Andy Shevchenko "); MODULE_DESCRIPTION("STMicroelectronics LSM9DS0 IMU core driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c index 78bede358747..8f205c477e6f 100644 --- a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c @@ -64,18 +64,12 @@ static int st_lsm9ds0_i2c_probe(struct i2c_client *client) return st_lsm9ds0_probe(lsm9ds0, regmap); } -static int st_lsm9ds0_i2c_remove(struct i2c_client *client) -{ - return st_lsm9ds0_remove(i2c_get_clientdata(client)); -} - static struct i2c_driver st_lsm9ds0_driver = { .driver = { .name = "st-lsm9ds0-i2c", .of_match_table = st_lsm9ds0_of_match, }, .probe_new = st_lsm9ds0_i2c_probe, - .remove = st_lsm9ds0_i2c_remove, .id_table = st_lsm9ds0_id_table, }; module_i2c_driver(st_lsm9ds0_driver); diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c index 180b54e66438..0ddfa53166af 100644 --- a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c @@ -63,18 +63,12 @@ static int st_lsm9ds0_spi_probe(struct spi_device *spi) return st_lsm9ds0_probe(lsm9ds0, regmap); } -static int st_lsm9ds0_spi_remove(struct spi_device *spi) -{ - return st_lsm9ds0_remove(spi_get_drvdata(spi)); -} - static struct spi_driver st_lsm9ds0_driver = { .driver = { .name = "st-lsm9ds0-spi", .of_match_table = st_lsm9ds0_of_match, }, .probe = st_lsm9ds0_spi_probe, - .remove = st_lsm9ds0_spi_remove, .id_table = st_lsm9ds0_id_table, }; module_spi_driver(st_lsm9ds0_driver); diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index 5be85e2405a5..1458906a3765 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -612,6 +612,7 @@ int st_magn_common_probe(struct iio_dev *indio_dev) { struct st_sensor_data *mdata = iio_priv(indio_dev); struct st_sensors_platform_data *pdata = dev_get_platdata(mdata->dev); + struct device *parent = indio_dev->dev.parent; int err; indio_dev->modes = INDIO_DIRECT_MODE; @@ -650,16 +651,10 @@ int st_magn_common_probe(struct iio_dev *indio_dev) return err; } - return iio_device_register(indio_dev); + return devm_iio_device_register(parent, indio_dev); } EXPORT_SYMBOL(st_magn_common_probe); -void st_magn_common_remove(struct iio_dev *indio_dev) -{ - iio_device_unregister(indio_dev); -} -EXPORT_SYMBOL(st_magn_common_remove); - MODULE_AUTHOR("Denis Ciocca "); MODULE_DESCRIPTION("STMicroelectronics magnetometers driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c index 0a5117dffcf4..7237711fc09b 100644 --- a/drivers/iio/magnetometer/st_magn_i2c.c +++ b/drivers/iio/magnetometer/st_magn_i2c.c @@ -89,15 +89,6 @@ static int st_magn_i2c_probe(struct i2c_client *client, return st_magn_common_probe(indio_dev); } -static int st_magn_i2c_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - - st_magn_common_remove(indio_dev); - - return 0; -} - static const struct i2c_device_id st_magn_id_table[] = { { LSM303DLH_MAGN_DEV_NAME }, { LSM303DLHC_MAGN_DEV_NAME }, @@ -117,7 +108,6 @@ static struct i2c_driver st_magn_driver = { .of_match_table = st_magn_of_match, }, .probe = st_magn_i2c_probe, - .remove = st_magn_i2c_remove, .id_table = st_magn_id_table, }; module_i2c_driver(st_magn_driver); diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c index 1f3bf02b24e0..489d4462862f 100644 --- a/drivers/iio/magnetometer/st_magn_spi.c +++ b/drivers/iio/magnetometer/st_magn_spi.c @@ -83,15 +83,6 @@ static int st_magn_spi_probe(struct spi_device *spi) return st_magn_common_probe(indio_dev); } -static int st_magn_spi_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - - st_magn_common_remove(indio_dev); - - return 0; -} - static const struct spi_device_id st_magn_id_table[] = { { LIS3MDL_MAGN_DEV_NAME }, { LSM303AGR_MAGN_DEV_NAME }, @@ -108,7 +99,6 @@ static struct spi_driver st_magn_driver = { .of_match_table = st_magn_of_match, }, .probe = st_magn_spi_probe, - .remove = st_magn_spi_remove, .id_table = st_magn_id_table, }; module_spi_driver(st_magn_driver); diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index 17ebb5171d4c..cebcc1d93d0b 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -678,6 +678,7 @@ int st_press_common_probe(struct iio_dev *indio_dev) { struct st_sensor_data *press_data = iio_priv(indio_dev); struct st_sensors_platform_data *pdata = dev_get_platdata(press_data->dev); + struct device *parent = indio_dev->dev.parent; int err; indio_dev->modes = INDIO_DIRECT_MODE; @@ -721,16 +722,10 @@ int st_press_common_probe(struct iio_dev *indio_dev) return err; } - return iio_device_register(indio_dev); + return devm_iio_device_register(parent, indio_dev); } EXPORT_SYMBOL(st_press_common_probe); -void st_press_common_remove(struct iio_dev *indio_dev) -{ - iio_device_unregister(indio_dev); -} -EXPORT_SYMBOL(st_press_common_remove); - MODULE_AUTHOR("Denis Ciocca "); MODULE_DESCRIPTION("STMicroelectronics pressures driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c index afeeab485c0d..1939e999a427 100644 --- a/drivers/iio/pressure/st_pressure_i2c.c +++ b/drivers/iio/pressure/st_pressure_i2c.c @@ -106,15 +106,6 @@ static int st_press_i2c_probe(struct i2c_client *client, return st_press_common_probe(indio_dev); } -static int st_press_i2c_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - - st_press_common_remove(indio_dev); - - return 0; -} - static struct i2c_driver st_press_driver = { .driver = { .name = "st-press-i2c", @@ -122,7 +113,6 @@ static struct i2c_driver st_press_driver = { .acpi_match_table = ACPI_PTR(st_press_acpi_match), }, .probe = st_press_i2c_probe, - .remove = st_press_i2c_remove, .id_table = st_press_id_table, }; module_i2c_driver(st_press_driver); diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c index 834ad6d40a70..9b2523c5bc94 100644 --- a/drivers/iio/pressure/st_pressure_spi.c +++ b/drivers/iio/pressure/st_pressure_spi.c @@ -89,15 +89,6 @@ static int st_press_spi_probe(struct spi_device *spi) return st_press_common_probe(indio_dev); } -static int st_press_spi_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - - st_press_common_remove(indio_dev); - - return 0; -} - static const struct spi_device_id st_press_id_table[] = { { LPS001WP_PRESS_DEV_NAME }, { LPS25H_PRESS_DEV_NAME }, @@ -116,7 +107,6 @@ static struct spi_driver st_press_driver = { .of_match_table = st_press_of_match, }, .probe = st_press_spi_probe, - .remove = st_press_spi_remove, .id_table = st_press_id_table, }; module_spi_driver(st_press_driver); diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h index fc90c202d15e..d17ae1e5ca19 100644 --- a/include/linux/iio/common/st_sensors.h +++ b/include/linux/iio/common/st_sensors.h @@ -323,21 +323,17 @@ void st_sensors_dev_name_probe(struct device *dev, char *name, int len); /* Accelerometer */ const struct st_sensor_settings *st_accel_get_settings(const char *name); int st_accel_common_probe(struct iio_dev *indio_dev); -void st_accel_common_remove(struct iio_dev *indio_dev); /* Gyroscope */ const struct st_sensor_settings *st_gyro_get_settings(const char *name); int st_gyro_common_probe(struct iio_dev *indio_dev); -void st_gyro_common_remove(struct iio_dev *indio_dev); /* Magnetometer */ const struct st_sensor_settings *st_magn_get_settings(const char *name); int st_magn_common_probe(struct iio_dev *indio_dev); -void st_magn_common_remove(struct iio_dev *indio_dev); /* Pressure */ const struct st_sensor_settings *st_press_get_settings(const char *name); int st_press_common_probe(struct iio_dev *indio_dev); -void st_press_common_remove(struct iio_dev *indio_dev); #endif /* ST_SENSORS_H */ -- cgit v1.2.3-70-g09d2 From e42696515414a15774c80f1d454194ce0cd9f145 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 23 Aug 2021 14:22:04 +0300 Subject: iio: st_sensors: remove reference to parent device object on st_sensor_data The idea behind it, is that all devm_ calls in ST sensors are bound to the parent device object. However, the reference to that object is kept on both the st_sensor_data struct and the IIO object parent (indio_dev->dev.parent). This change only adds a bit consistency and uses the reference stored on indio_dev->dev.parent, to enforce the assumption that all ST sensors' devm_ calls are bound to the same reference as the one store on st_sensor_data. Reviewed-by: Andy Shevchenko Signed-off-by: Alexandru Ardelean Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20210823112204.243255-6-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_core.c | 6 +++--- drivers/iio/common/st_sensors/st_sensors_i2c.c | 1 - drivers/iio/common/st_sensors/st_sensors_spi.c | 1 - drivers/iio/common/st_sensors/st_sensors_trigger.c | 8 +++++--- drivers/iio/gyro/st_gyro_core.c | 2 +- drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c | 2 -- drivers/iio/magnetometer/st_magn_core.c | 4 ++-- drivers/iio/pressure/st_pressure_core.c | 2 +- include/linux/iio/common/st_sensors.h | 2 -- 9 files changed, 12 insertions(+), 16 deletions(-) diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 01695abd9d2f..31ea19d0ba71 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -1210,7 +1210,7 @@ static int apply_acpi_orientation(struct iio_dev *indio_dev) }; - adev = ACPI_COMPANION(adata->dev); + adev = ACPI_COMPANION(indio_dev->dev.parent); if (!adev) return 0; @@ -1334,8 +1334,8 @@ EXPORT_SYMBOL(st_accel_get_settings); int st_accel_common_probe(struct iio_dev *indio_dev) { struct st_sensor_data *adata = iio_priv(indio_dev); - struct st_sensors_platform_data *pdata = dev_get_platdata(adata->dev); struct device *parent = indio_dev->dev.parent; + struct st_sensors_platform_data *pdata = dev_get_platdata(parent); int err; indio_dev->modes = INDIO_DIRECT_MODE; @@ -1355,7 +1355,7 @@ int st_accel_common_probe(struct iio_dev *indio_dev) */ err = apply_acpi_orientation(indio_dev); if (err) { - err = iio_read_mount_matrix(adata->dev, &adata->mount_matrix); + err = iio_read_mount_matrix(parent, &adata->mount_matrix); if (err) return err; } diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c index b3ff88700866..18bd3c3d99bc 100644 --- a/drivers/iio/common/st_sensors/st_sensors_i2c.c +++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c @@ -57,7 +57,6 @@ int st_sensors_i2c_configure(struct iio_dev *indio_dev, indio_dev->name = client->name; - sdata->dev = &client->dev; sdata->irq = client->irq; return 0; diff --git a/drivers/iio/common/st_sensors/st_sensors_spi.c b/drivers/iio/common/st_sensors/st_sensors_spi.c index 0d1d66c77cd8..7c60050e90dc 100644 --- a/drivers/iio/common/st_sensors/st_sensors_spi.c +++ b/drivers/iio/common/st_sensors/st_sensors_spi.c @@ -109,7 +109,6 @@ int st_sensors_spi_configure(struct iio_dev *indio_dev, indio_dev->name = spi->modalias; - sdata->dev = &spi->dev; sdata->irq = spi->irq; return 0; diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c index d022157b66a2..392d74449886 100644 --- a/drivers/iio/common/st_sensors/st_sensors_trigger.c +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -42,7 +42,8 @@ static bool st_sensors_new_samples_available(struct iio_dev *indio_dev, sdata->sensor_settings->drdy_irq.stat_drdy.addr, &status); if (ret < 0) { - dev_err(sdata->dev, "error checking samples available\n"); + dev_err(indio_dev->dev.parent, + "error checking samples available\n"); return false; } @@ -87,7 +88,7 @@ static irqreturn_t st_sensors_irq_thread(int irq, void *p) st_sensors_new_samples_available(indio_dev, sdata)) { iio_trigger_poll_chained(p); } else { - dev_dbg(sdata->dev, "spurious IRQ\n"); + dev_dbg(indio_dev->dev.parent, "spurious IRQ\n"); return IRQ_NONE; } @@ -107,7 +108,8 @@ static irqreturn_t st_sensors_irq_thread(int irq, void *p) */ while (sdata->hw_irq_trigger && st_sensors_new_samples_available(indio_dev, sdata)) { - dev_dbg(sdata->dev, "more samples came in during polling\n"); + dev_dbg(indio_dev->dev.parent, + "more samples came in during polling\n"); sdata->hw_timestamp = iio_get_time_ns(indio_dev); iio_trigger_poll_chained(p); } diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index 3609082a6778..201050b76fe5 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -492,7 +492,7 @@ int st_gyro_common_probe(struct iio_dev *indio_dev) indio_dev->channels = gdata->sensor_settings->ch; indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS; - err = iio_read_mount_matrix(gdata->dev, &gdata->mount_matrix); + err = iio_read_mount_matrix(parent, &gdata->mount_matrix); if (err) return err; diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c index d276f663fe57..b3a43a3b04ff 100644 --- a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c @@ -90,7 +90,6 @@ static int st_lsm9ds0_probe_accel(struct st_lsm9ds0 *lsm9ds0, struct regmap *reg data = iio_priv(lsm9ds0->accel); data->sensor_settings = (struct st_sensor_settings *)settings; - data->dev = dev; data->irq = lsm9ds0->irq; data->regmap = regmap; data->vdd = lsm9ds0->vdd; @@ -119,7 +118,6 @@ static int st_lsm9ds0_probe_magn(struct st_lsm9ds0 *lsm9ds0, struct regmap *regm data = iio_priv(lsm9ds0->magn); data->sensor_settings = (struct st_sensor_settings *)settings; - data->dev = dev; data->irq = lsm9ds0->irq; data->regmap = regmap; data->vdd = lsm9ds0->vdd; diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index 1458906a3765..0806a1e65ce4 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -611,8 +611,8 @@ EXPORT_SYMBOL(st_magn_get_settings); int st_magn_common_probe(struct iio_dev *indio_dev) { struct st_sensor_data *mdata = iio_priv(indio_dev); - struct st_sensors_platform_data *pdata = dev_get_platdata(mdata->dev); struct device *parent = indio_dev->dev.parent; + struct st_sensors_platform_data *pdata = dev_get_platdata(parent); int err; indio_dev->modes = INDIO_DIRECT_MODE; @@ -626,7 +626,7 @@ int st_magn_common_probe(struct iio_dev *indio_dev) indio_dev->channels = mdata->sensor_settings->ch; indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS; - err = iio_read_mount_matrix(mdata->dev, &mdata->mount_matrix); + err = iio_read_mount_matrix(parent, &mdata->mount_matrix); if (err) return err; diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index cebcc1d93d0b..26a1ee43d56e 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -677,8 +677,8 @@ EXPORT_SYMBOL(st_press_get_settings); int st_press_common_probe(struct iio_dev *indio_dev) { struct st_sensor_data *press_data = iio_priv(indio_dev); - struct st_sensors_platform_data *pdata = dev_get_platdata(press_data->dev); struct device *parent = indio_dev->dev.parent; + struct st_sensors_platform_data *pdata = dev_get_platdata(parent); int err; indio_dev->modes = INDIO_DIRECT_MODE; diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h index d17ae1e5ca19..22f67845cdd3 100644 --- a/include/linux/iio/common/st_sensors.h +++ b/include/linux/iio/common/st_sensors.h @@ -220,7 +220,6 @@ struct st_sensor_settings { /** * struct st_sensor_data - ST sensor device status - * @dev: Pointer to instance of struct device (I2C or SPI). * @trig: The trigger in use by the core driver. * @mount_matrix: The mounting matrix of the sensor. * @sensor_settings: Pointer to the specific sensor settings in use. @@ -240,7 +239,6 @@ struct st_sensor_settings { * @buffer_data: Data used by buffer part. */ struct st_sensor_data { - struct device *dev; struct iio_trigger *trig; struct iio_mount_matrix mount_matrix; struct st_sensor_settings *sensor_settings; -- cgit v1.2.3-70-g09d2 From 1d761ca978382f84f05e221fbe4703605286f1be Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Tue, 17 Aug 2021 10:13:30 +0200 Subject: iio: gyro: remove dead config dependencies on INPUT_MPU3050 Commit b1fe0cf06f92 ("Input: delete MPU3050 driver") deletes the superseded MPU3050 driver and its corresponding config INPUT_MPU3050. The dependencies on the superseding driver in ./drivers/iio/gyro/Kconfig to ensure that the two drivers are not built into the same kernel is a dead dependency and not required anymore. So, remove those config dependencies on INPUT_MPU3050 for MPU3050_I2C. This issue was detected with ./scripts/checkkconfigsymbols.py. Signed-off-by: Lukas Bulwahn Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20210817081330.9645-1-lukas.bulwahn@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig index 20b5ac7ab66a..a672f7d12bbb 100644 --- a/drivers/iio/gyro/Kconfig +++ b/drivers/iio/gyro/Kconfig @@ -126,7 +126,6 @@ config MPU3050 config MPU3050_I2C tristate "Invensense MPU3050 devices on I2C" - depends on !(INPUT_MPU3050=y || INPUT_MPU3050=m) depends on I2C select MPU3050 select REGMAP_I2C -- cgit v1.2.3-70-g09d2 From 26df977a909f818b7d346b3990735513e7e0bf93 Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Wed, 18 Aug 2021 10:05:25 +0200 Subject: iio: ad5770r: make devicetree property reading consistent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bindings file for this driver is defining the property as 'reg' but the driver was reading it with the 'num' name. The bindings actually had the 'num' property when added in commit ea52c21268e6 ("dt-bindings: iio: dac: Add docs for AD5770R DAC") and then changed it to 'reg' in commit 2cf3818f18b2 ("dt-bindings: iio: dac: AD5570R fix bindings errors"). However, both these commits landed in v5.7 so the assumption is that either 'num' is not being used or if it is, the validations were not done. Anyways, if someone comes back yelling about this, we might just support both of the properties in the future. Not ideal, but that's life... Fixes: 2cf3818f18b2 ("dt-bindings: iio: dac: AD5570R fix bindings errors") Signed-off-by: Nuno Sá Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210818080525.62790-1-nuno.sa@analog.com Cc: Stable@vger.kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5770r.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/dac/ad5770r.c b/drivers/iio/dac/ad5770r.c index 8107f7bbbe3c..7e2fd32e993a 100644 --- a/drivers/iio/dac/ad5770r.c +++ b/drivers/iio/dac/ad5770r.c @@ -522,7 +522,7 @@ static int ad5770r_channel_config(struct ad5770r_state *st) return -EINVAL; device_for_each_child_node(&st->spi->dev, child) { - ret = fwnode_property_read_u32(child, "num", &num); + ret = fwnode_property_read_u32(child, "reg", &num); if (ret) goto err_child_out; if (num >= AD5770R_MAX_CHANNELS) { -- cgit v1.2.3-70-g09d2 From 919726c9e0efc6dd6476095d37b3ba8e79566c75 Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Wed, 25 Aug 2021 10:41:48 +0200 Subject: iio: ltc2983: add support for optional reset gpio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check if an optional reset gpio is present and if so, make sure to reset the device. Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20210825084149.11587-1-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/ltc2983.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index 3b4a0e60e605..22e6a26ce6b1 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -1470,6 +1470,7 @@ static int ltc2983_probe(struct spi_device *spi) { struct ltc2983_data *st; struct iio_dev *indio_dev; + struct gpio_desc *gpio; const char *name = spi_get_device_id(spi)->name; int ret; @@ -1494,6 +1495,16 @@ static int ltc2983_probe(struct spi_device *spi) if (ret) return ret; + gpio = devm_gpiod_get_optional(&st->spi->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(gpio)) + return PTR_ERR(gpio); + + if (gpio) { + /* bring the device out of reset */ + usleep_range(1000, 1200); + gpiod_set_value_cansleep(gpio, 0); + } + ret = ltc2983_setup(st, true); if (ret) return ret; -- cgit v1.2.3-70-g09d2 From 25d4abbf3ddcccb022d890ad1dc0d87262783b03 Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Wed, 25 Aug 2021 10:41:49 +0200 Subject: iio: ltc2983: fail probe if no channels are given MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If there are no channels defined in the devicetree, there's no point in probing the device. We were actually requesting a zero sized 'kmalloc' array but since we were not touching the ZERO_SIZE_PTR afterwards, nothing bad was actually happening. Hence this is not really a fix but rather an improvement. Reviewed-by: Alexandru Ardelean Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20210825084149.11587-2-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/ltc2983.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index 22e6a26ce6b1..301c3f13fb26 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -1275,6 +1275,11 @@ static int ltc2983_parse_dt(struct ltc2983_data *st) &st->filter_notch_freq); st->num_channels = of_get_available_child_count(dev->of_node); + if (!st->num_channels) { + dev_err(&st->spi->dev, "At least one channel must be given!"); + return -EINVAL; + } + st->sensors = devm_kcalloc(dev, st->num_channels, sizeof(*st->sensors), GFP_KERNEL); if (!st->sensors) -- cgit v1.2.3-70-g09d2 From 050098500ae4f73f76a775b26fdf6ee5ee5bef40 Mon Sep 17 00:00:00 2001 From: Alexander Vorwerk Date: Sat, 21 Aug 2021 00:49:14 +0200 Subject: staging: iio: cdc: remove braces from single line if blocks Remove braces from single line if blocks to clear checkpatch warnings. WARNING: braces {} are not necessary for single statement blocks Signed-off-by: Alexander Vorwerk Link: https://lore.kernel.org/r/20210820224914.1260-1-alec@vc-celle.de Signed-off-by: Jonathan Cameron --- drivers/staging/iio/cdc/ad7746.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c index 78ac720266e6..71c709771676 100644 --- a/drivers/staging/iio/cdc/ad7746.c +++ b/drivers/staging/iio/cdc/ad7746.c @@ -241,10 +241,8 @@ static int ad7746_select_channel(struct iio_dev *indio_dev, if (ret < 0) return ret; - if (chip->capdac_set != chan->channel) { - + if (chip->capdac_set != chan->channel) chip->capdac_set = chan->channel; - } break; case IIO_VOLTAGE: case IIO_TEMP: -- cgit v1.2.3-70-g09d2 From b0fc3f1dbe2a577c22dc52900856597ad5ea6bb3 Mon Sep 17 00:00:00 2001 From: Tang Bin Date: Mon, 23 Aug 2021 17:59:21 +0800 Subject: iio: adc: twl6030-gpadc: Use the defined variable to clean code Use the defined variable "dev" to make the code cleaner. Signed-off-by: Tang Bin Link: https://lore.kernel.org/r/20210823095921.16828-1-tangbin@cmss.chinamobile.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/twl6030-gpadc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c index c6416ad795ca..afdb59e0b526 100644 --- a/drivers/iio/adc/twl6030-gpadc.c +++ b/drivers/iio/adc/twl6030-gpadc.c @@ -900,7 +900,7 @@ static int twl6030_gpadc_probe(struct platform_device *pdev) ret = pdata->calibrate(gpadc); if (ret < 0) { - dev_err(&pdev->dev, "failed to read calibration registers\n"); + dev_err(dev, "failed to read calibration registers\n"); return ret; } @@ -914,14 +914,14 @@ static int twl6030_gpadc_probe(struct platform_device *pdev) ret = twl6030_gpadc_enable_irq(TWL6030_GPADC_RT_SW1_EOC_MASK); if (ret < 0) { - dev_err(&pdev->dev, "failed to enable GPADC interrupt\n"); + dev_err(dev, "failed to enable GPADC interrupt\n"); return ret; } ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, TWL6030_GPADCS, TWL6030_REG_TOGGLE1); if (ret < 0) { - dev_err(&pdev->dev, "failed to enable GPADC module\n"); + dev_err(dev, "failed to enable GPADC module\n"); return ret; } -- cgit v1.2.3-70-g09d2 From e112dc4e18eafc5ee9d5700e3c059ac9897ae2a1 Mon Sep 17 00:00:00 2001 From: Navin Sankar Velliangiri Date: Tue, 24 Aug 2021 10:31:23 +0530 Subject: iio: temperature: Add MAX31865 RTD Support This patch adds support for Maxim MAX31865 RTD temperature sensor support. More information can be found in: https://datasheets.maximintegrated.com/en/ds/MAX31865.pdf Signed-off-by: Navin Sankar Velliangiri Link: https://lore.kernel.org/r/20210824050123.71289-1-navin@linumiz.com Signed-off-by: Jonathan Cameron --- .../ABI/testing/sysfs-bus-iio-temperature-max31865 | 20 ++ drivers/iio/temperature/Kconfig | 10 + drivers/iio/temperature/Makefile | 1 + drivers/iio/temperature/max31865.c | 349 +++++++++++++++++++++ 4 files changed, 380 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-temperature-max31865 create mode 100644 drivers/iio/temperature/max31865.c diff --git a/Documentation/ABI/testing/sysfs-bus-iio-temperature-max31865 b/Documentation/ABI/testing/sysfs-bus-iio-temperature-max31865 new file mode 100644 index 000000000000..4b072da92218 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-temperature-max31865 @@ -0,0 +1,20 @@ +What: /sys/bus/iio/devices/iio:deviceX/fault_ovuv +KernelVersion: 5.11 +Contact: linux-iio@vger.kernel.org +Description: + Overvoltage or Undervoltage Input fault. The internal circuitry + is protected from excessive voltages applied to the thermocouple + cables at FORCE+, FORCE2, RTDIN+ & RTDIN-. This circuitry turn + off when the input voltage is negative or greater than VDD. + + Reading returns '1' if input voltage is negative or greater + than VDD, otherwise '0'. + +What: /sys/bus/iio/devices/iio:deviceX/in_filter_notch_center_frequency +KernelVersion: 5.11 +Contact: linux-iio@vger.kernel.org +Description: + Notch frequency in Hz for a noise rejection filter. Used i.e for + line noise rejection. + + Valid notch filter values are 50 Hz and 60 Hz. diff --git a/drivers/iio/temperature/Kconfig b/drivers/iio/temperature/Kconfig index f20ae3c963cb..e8ed849e3b76 100644 --- a/drivers/iio/temperature/Kconfig +++ b/drivers/iio/temperature/Kconfig @@ -138,4 +138,14 @@ config MAX31856 This driver can also be built as a module. If so, the module will be called max31856. +config MAX31865 + tristate "MAX31865 RTD to Digital converter" + depends on SPI + help + If you say yes here you get support for MAX31865 + thermocouple sensor chip connected via SPI. + + This driver can also be build as a module. If so, the module + will be called max31865. + endmenu diff --git a/drivers/iio/temperature/Makefile b/drivers/iio/temperature/Makefile index e3392c4b29b4..dd08e562ffe0 100644 --- a/drivers/iio/temperature/Makefile +++ b/drivers/iio/temperature/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_LTC2983) += ltc2983.o obj-$(CONFIG_HID_SENSOR_TEMP) += hid-sensor-temperature.o obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o obj-$(CONFIG_MAX31856) += max31856.o +obj-$(CONFIG_MAX31865) += max31865.o obj-$(CONFIG_MLX90614) += mlx90614.o obj-$(CONFIG_MLX90632) += mlx90632.o obj-$(CONFIG_TMP006) += tmp006.o diff --git a/drivers/iio/temperature/max31865.c b/drivers/iio/temperature/max31865.c new file mode 100644 index 000000000000..4c8d6e6cf677 --- /dev/null +++ b/drivers/iio/temperature/max31865.c @@ -0,0 +1,349 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (c) Linumiz 2021 + * + * max31865.c - Maxim MAX31865 RTD-to-Digital Converter sensor driver + * + * Author: Navin Sankar Velliangiri + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The MSB of the register value determines whether the following byte will + * be written or read. If it is 0, read will follow and if it is 1, write + * will follow. + */ +#define MAX31865_RD_WR_BIT BIT(7) + +#define MAX31865_CFG_VBIAS BIT(7) +#define MAX31865_CFG_1SHOT BIT(5) +#define MAX31865_3WIRE_RTD BIT(4) +#define MAX31865_FAULT_STATUS_CLEAR BIT(1) +#define MAX31865_FILTER_50HZ BIT(0) + +/* The MAX31865 registers */ +#define MAX31865_CFG_REG 0x00 +#define MAX31865_RTD_MSB 0x01 +#define MAX31865_FAULT_STATUS 0x07 + +#define MAX31865_FAULT_OVUV BIT(2) + +static const char max31865_show_samp_freq[] = "50 60"; + +static const struct iio_chan_spec max31865_channels[] = { + { /* RTD Temperature */ + .type = IIO_TEMP, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) + }, +}; + +struct max31865_data { + struct spi_device *spi; + struct mutex lock; + bool filter_50hz; + bool three_wire; + u8 buf[2] ____cacheline_aligned; +}; + +static int max31865_read(struct max31865_data *data, u8 reg, + unsigned int read_size) +{ + return spi_write_then_read(data->spi, ®, 1, data->buf, read_size); +} + +static int max31865_write(struct max31865_data *data, size_t len) +{ + return spi_write(data->spi, data->buf, len); +} + +static int enable_bias(struct max31865_data *data) +{ + u8 cfg; + int ret; + + ret = max31865_read(data, MAX31865_CFG_REG, 1); + if (ret) + return ret; + + cfg = data->buf[0]; + + data->buf[0] = MAX31865_CFG_REG | MAX31865_RD_WR_BIT; + data->buf[1] = cfg | MAX31865_CFG_VBIAS; + + return max31865_write(data, 2); +} + +static int disable_bias(struct max31865_data *data) +{ + u8 cfg; + int ret; + + ret = max31865_read(data, MAX31865_CFG_REG, 1); + if (ret) + return ret; + + cfg = data->buf[0]; + cfg &= ~MAX31865_CFG_VBIAS; + + data->buf[0] = MAX31865_CFG_REG | MAX31865_RD_WR_BIT; + data->buf[1] = cfg; + + return max31865_write(data, 2); +} + +static int max31865_rtd_read(struct max31865_data *data, int *val) +{ + u8 reg; + int ret; + + /* Enable BIAS to start the conversion */ + ret = enable_bias(data); + if (ret) + return ret; + + /* wait 10.5ms before initiating the conversion */ + msleep(11); + + ret = max31865_read(data, MAX31865_CFG_REG, 1); + if (ret) + return ret; + + reg = data->buf[0]; + reg |= MAX31865_CFG_1SHOT | MAX31865_FAULT_STATUS_CLEAR; + data->buf[0] = MAX31865_CFG_REG | MAX31865_RD_WR_BIT; + data->buf[1] = reg; + + ret = max31865_write(data, 2); + if (ret) + return ret; + + if (data->filter_50hz) { + /* 50Hz filter mode requires 62.5ms to complete */ + msleep(63); + } else { + /* 60Hz filter mode requires 52ms to complete */ + msleep(52); + } + + ret = max31865_read(data, MAX31865_RTD_MSB, 2); + if (ret) + return ret; + + *val = get_unaligned_be16(&data->buf) >> 1; + + return disable_bias(data); +} + +static int max31865_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct max31865_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&data->lock); + ret = max31865_rtd_read(data, val); + mutex_unlock(&data->lock); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* Temp. Data resolution is 0.03125 degree centigrade */ + *val = 31; + *val2 = 250000; /* 1000 * 0.03125 */ + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int max31865_init(struct max31865_data *data) +{ + u8 cfg; + int ret; + + ret = max31865_read(data, MAX31865_CFG_REG, 1); + if (ret) + return ret; + + cfg = data->buf[0]; + + if (data->three_wire) + /* 3-wire RTD connection */ + cfg |= MAX31865_3WIRE_RTD; + + if (data->filter_50hz) + /* 50Hz noise rejection filter */ + cfg |= MAX31865_FILTER_50HZ; + + data->buf[0] = MAX31865_CFG_REG | MAX31865_RD_WR_BIT; + data->buf[1] = cfg; + + return max31865_write(data, 2); +} + +static ssize_t show_fault(struct device *dev, u8 faultbit, char *buf) +{ + int ret; + bool fault; + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct max31865_data *data = iio_priv(indio_dev); + + ret = max31865_read(data, MAX31865_FAULT_STATUS, 1); + if (ret) + return ret; + + fault = data->buf[0] & faultbit; + + return sprintf(buf, "%d\n", fault); +} + +static ssize_t show_fault_ovuv(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return show_fault(dev, MAX31865_FAULT_OVUV, buf); +} + +static ssize_t show_filter(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct max31865_data *data = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", data->filter_50hz ? 50 : 60); +} + +static ssize_t set_filter(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct max31865_data *data = iio_priv(indio_dev); + unsigned int freq; + int ret; + + ret = kstrtouint(buf, 10, &freq); + if (ret) + return ret; + + switch (freq) { + case 50: + data->filter_50hz = true; + break; + case 60: + data->filter_50hz = false; + break; + default: + return -EINVAL; + } + + mutex_lock(&data->lock); + ret = max31865_init(data); + mutex_unlock(&data->lock); + if (ret) + return ret; + + return len; +} + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(max31865_show_samp_freq); +static IIO_DEVICE_ATTR(fault_ovuv, 0444, show_fault_ovuv, NULL, 0); +static IIO_DEVICE_ATTR(in_filter_notch_center_frequency, 0644, + show_filter, set_filter, 0); + +static struct attribute *max31865_attributes[] = { + &iio_dev_attr_fault_ovuv.dev_attr.attr, + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_filter_notch_center_frequency.dev_attr.attr, + NULL, +}; + +static const struct attribute_group max31865_group = { + .attrs = max31865_attributes, +}; + +static const struct iio_info max31865_info = { + .read_raw = max31865_read_raw, + .attrs = &max31865_group, +}; + +static int max31865_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct iio_dev *indio_dev; + struct max31865_data *data; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->spi = spi; + data->filter_50hz = false; + mutex_init(&data->lock); + + indio_dev->info = &max31865_info; + indio_dev->name = id->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = max31865_channels; + indio_dev->num_channels = ARRAY_SIZE(max31865_channels); + + if (of_property_read_bool(spi->dev.of_node, "maxim,3-wire")) { + /* select 3 wire */ + data->three_wire = 1; + } else { + /* select 2 or 4 wire */ + data->three_wire = 0; + } + + ret = max31865_init(data); + if (ret) { + dev_err(&spi->dev, "error: Failed to configure max31865\n"); + return ret; + } + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id max31865_id[] = { + { "max31865", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, max31865_id); + +static const struct of_device_id max31865_of_match[] = { + { .compatible = "maxim,max31865" }, + { } +}; +MODULE_DEVICE_TABLE(of, max31865_of_match); + +static struct spi_driver max31865_driver = { + .driver = { + .name = "max31865", + .of_match_table = max31865_of_match, + }, + .probe = max31865_probe, + .id_table = max31865_id, +}; +module_spi_driver(max31865_driver); + +MODULE_AUTHOR("Navin Sankar Velliangiri "); +MODULE_DESCRIPTION("Maxim MAX31865 RTD-to-Digital Converter sensor driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-70-g09d2 From c5dc9e3635019a6ca9dfe7860e6a456d1811f36b Mon Sep 17 00:00:00 2001 From: Navin Sankar Velliangiri Date: Tue, 24 Aug 2021 10:36:50 +0530 Subject: dt-bindings: iio: temperature: add MAXIM max31865 support Add DT bindings for MAXIM max31865 RTD sensor. Signed-off-by: Navin Sankar Velliangiri Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20210824050650.72619-1-navin@linumiz.com Signed-off-by: Jonathan Cameron --- .../bindings/iio/temperature/maxim,max31865.yaml | 52 ++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/temperature/maxim,max31865.yaml diff --git a/Documentation/devicetree/bindings/iio/temperature/maxim,max31865.yaml b/Documentation/devicetree/bindings/iio/temperature/maxim,max31865.yaml new file mode 100644 index 000000000000..aafb33b16549 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/temperature/maxim,max31865.yaml @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/temperature/maxim,max31865.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Maxim MAX31865 Resistance Temperature Detector. + +maintainers: + - Navin Sankar Velliangiri + +description: | + https://datasheets.maximintegrated.com/en/ds/MAX31865.pdf + +properties: + compatible: + const: maxim,max31865 + + reg: + maxItems: 1 + + maxim,3-wire: + description: + Identifies the number of wires used by the RTD. Setting this property + enables 3-wire RTD connection. Else 2-wire or 4-wire RTD connection. + type: boolean + + spi-max-frequency: true + spi-cpha: true + +required: + - compatible + - reg + - spi-cpha + +additionalProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + temp_sensor@0 { + compatible = "maxim,max31865"; + reg = <0>; + spi-max-frequency = <400000>; + spi-cpha; + maxim,3-wire; + }; + }; +... -- cgit v1.2.3-70-g09d2 From 76e28aa97fa0702f51b4fea2c362a86642e31e23 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Wed, 25 Aug 2021 05:07:38 +0300 Subject: iio: magnetometer: ak8975: add AK09116 support Add additional AK09116 to the magnetometer driver which has the same register mapping and scaling as the AK09112 device. Signed-off-by: Matt Ranostay Link: https://lore.kernel.org/r/20210825020738.35877-1-matt.ranostay@konsulko.com Signed-off-by: Jonathan Cameron --- .../iio/magnetometer/asahi-kasei,ak8975.yaml | 2 ++ drivers/iio/magnetometer/Kconfig | 2 +- drivers/iio/magnetometer/ak8975.c | 35 ++++++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml b/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml index a0a1ffe017df..c552b2b7751a 100644 --- a/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml +++ b/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml @@ -17,11 +17,13 @@ properties: - asahi-kasei,ak8963 - asahi-kasei,ak09911 - asahi-kasei,ak09912 + - asahi-kasei,ak09916 - enum: - ak8975 - ak8963 - ak09911 - ak09912 + - ak09916 deprecated: true reg: diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index 74ad5701c6c2..565ee41ccb3a 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -28,7 +28,7 @@ config AK8975 select IIO_TRIGGERED_BUFFER help Say yes here to build support for Asahi Kasei AK8975, AK8963, - AK09911 or AK09912 3-Axis Magnetometer. + AK09911, AK09912 or AK09916 3-Axis Magnetometer. To compile this driver as a module, choose M here: the module will be called ak8975. diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 42b8a2680e3a..6e82dc54a417 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -78,6 +78,7 @@ */ #define AK09912_REG_WIA1 0x00 #define AK09912_REG_WIA2 0x01 +#define AK09916_DEVICE_ID 0x09 #define AK09912_DEVICE_ID 0x04 #define AK09911_DEVICE_ID 0x05 @@ -208,6 +209,7 @@ enum asahi_compass_chipset { AK8963, AK09911, AK09912, + AK09916, }; enum ak_ctrl_reg_addr { @@ -345,6 +347,31 @@ static const struct ak_def ak_def_array[] = { AK09912_REG_HXL, AK09912_REG_HYL, AK09912_REG_HZL}, + }, + { + .type = AK09916, + .raw_to_gauss = ak09912_raw_to_gauss, + .range = 32752, + .ctrl_regs = { + AK09912_REG_ST1, + AK09912_REG_ST2, + AK09912_REG_CNTL2, + AK09912_REG_ASAX, + AK09912_MAX_REGS}, + .ctrl_masks = { + AK09912_REG_ST1_DRDY_MASK, + AK09912_REG_ST2_HOFL_MASK, + 0, + AK09912_REG_CNTL2_MODE_MASK}, + .ctrl_modes = { + AK09912_REG_CNTL_MODE_POWER_DOWN, + AK09912_REG_CNTL_MODE_ONCE, + AK09912_REG_CNTL_MODE_SELF_TEST, + AK09912_REG_CNTL_MODE_FUSE_ROM}, + .data_regs = { + AK09912_REG_HXL, + AK09912_REG_HYL, + AK09912_REG_HZL}, } }; @@ -425,6 +452,7 @@ static int ak8975_who_i_am(struct i2c_client *client, /* * Signature for each device: * Device | WIA1 | WIA2 + * AK09916 | DEVICE_ID_| AK09916_DEVICE_ID * AK09912 | DEVICE_ID | AK09912_DEVICE_ID * AK09911 | DEVICE_ID | AK09911_DEVICE_ID * AK8975 | DEVICE_ID | NA @@ -452,6 +480,10 @@ static int ak8975_who_i_am(struct i2c_client *client, if (wia_val[1] == AK09912_DEVICE_ID) return 0; break; + case AK09916: + if (wia_val[1] == AK09916_DEVICE_ID) + return 0; + break; default: dev_err(&client->dev, "Type %d unknown\n", type); } @@ -1057,6 +1089,7 @@ static const struct i2c_device_id ak8975_id[] = { {"AK8963", AK8963}, {"ak09911", AK09911}, {"ak09912", AK09912}, + {"ak09916", AK09916}, {} }; @@ -1071,6 +1104,8 @@ static const struct of_device_id ak8975_of_match[] = { { .compatible = "ak09911", }, { .compatible = "asahi-kasei,ak09912", }, { .compatible = "ak09912", }, + { .compatible = "asahi-kasei,ak09916", }, + { .compatible = "ak09916", }, {} }; MODULE_DEVICE_TABLE(of, ak8975_of_match); -- cgit v1.2.3-70-g09d2 From f928670651dac9e09a8a848e0d49d0e83fbb610d Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Wed, 1 Sep 2021 15:30:04 +0300 Subject: dt-bindings: iio: adc: at91-sama5d2: add compatible for sama7g5-adc Add compatible for microchip,sama7g5-adc device. Acked-by: Rob Herring Signed-off-by: Eugen Hristev Link: https://lore.kernel.org/r/20210901123013.329792-2-eugen.hristev@microchip.com Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/atmel,sama5d2-adc.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/iio/adc/atmel,sama5d2-adc.yaml b/Documentation/devicetree/bindings/iio/adc/atmel,sama5d2-adc.yaml index 79c13b408eda..efed361215b4 100644 --- a/Documentation/devicetree/bindings/iio/adc/atmel,sama5d2-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/atmel,sama5d2-adc.yaml @@ -15,6 +15,7 @@ properties: enum: - atmel,sama5d2-adc - microchip,sam9x60-adc + - microchip,sama7g5-adc reg: maxItems: 1 -- cgit v1.2.3-70-g09d2 From eaefa151f48a3305cf73cbc02890eb63cdb48b16 Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Wed, 1 Sep 2021 15:30:05 +0300 Subject: iio: adc: at91-sama5d2_adc: initialize hardware after clock is started The hw_init hardware init call must happen after the clock is prepared and enabled. Otherwise, writing to the registers might lead to a block or external abort. Signed-off-by: Eugen Hristev Link: https://lore.kernel.org/r/20210901123013.329792-3-eugen.hristev@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index ea5ca163d879..1f4d461c2c18 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -1833,12 +1833,12 @@ static int at91_adc_probe(struct platform_device *pdev) goto vref_disable; } - at91_adc_hw_init(indio_dev); - ret = clk_prepare_enable(st->per_clk); if (ret) goto vref_disable; + at91_adc_hw_init(indio_dev); + platform_set_drvdata(pdev, indio_dev); ret = at91_adc_buffer_and_trigger_init(&pdev->dev, indio_dev); -- cgit v1.2.3-70-g09d2 From 841a5b651815aba221cc003b0457dfc201a8a0c1 Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Wed, 1 Sep 2021 15:30:06 +0300 Subject: iio: adc: at91-sama5d2_adc: remove unused definition Remove unused register definition Signed-off-by: Eugen Hristev Link: https://lore.kernel.org/r/20210901123013.329792-4-eugen.hristev@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 1f4d461c2c18..9d71dcffcf93 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -143,8 +143,6 @@ #define AT91_SAMA5D2_COR 0x4c #define AT91_SAMA5D2_COR_DIFF_OFFSET 16 -/* Channel Data Register 0 */ -#define AT91_SAMA5D2_CDR0 0x50 /* Analog Control Register */ #define AT91_SAMA5D2_ACR 0x94 /* Analog Control Register - Pen detect sensitivity mask */ -- cgit v1.2.3-70-g09d2 From 8940de2e48902e95b588b6244d5a1b61a4d75c4a Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Wed, 1 Sep 2021 15:30:07 +0300 Subject: iio: adc: at91-sama5d2_adc: convert to platform specific data structures Convert the driver to platform specific structures. This means: - create a register layout struct that will hold offsets for registers - create a platform struct that will hold platform information (number of channels, indexes for different channels and pointer to layout struct) - convert specific macros that are platform dependent into platform variables This step is in fact a no-op, but allows the driver to be more flexible and for future enhancement including adding new platforms that are partly compatible with the current driver and differ slightly in register layout or capabilities for example. Signed-off-by: Eugen Hristev Link: https://lore.kernel.org/r/20210901123013.329792-5-eugen.hristev@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 417 ++++++++++++++++++++++--------------- 1 file changed, 253 insertions(+), 164 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 9d71dcffcf93..ae9978dcb4c3 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -27,8 +27,9 @@ #include #include +struct at91_adc_reg_layout { /* Control Register */ -#define AT91_SAMA5D2_CR 0x00 + u16 CR; /* Software Reset */ #define AT91_SAMA5D2_CR_SWRST BIT(0) /* Start Conversion */ @@ -39,7 +40,7 @@ #define AT91_SAMA5D2_CR_CMPRST BIT(4) /* Mode Register */ -#define AT91_SAMA5D2_MR 0x04 + u16 MR; /* Trigger Selection */ #define AT91_SAMA5D2_MR_TRGSEL(v) ((v) << 1) /* ADTRG */ @@ -82,19 +83,19 @@ #define AT91_SAMA5D2_MR_USEQ BIT(31) /* Channel Sequence Register 1 */ -#define AT91_SAMA5D2_SEQR1 0x08 + u16 SEQR1; /* Channel Sequence Register 2 */ -#define AT91_SAMA5D2_SEQR2 0x0c + u16 SEQR2; /* Channel Enable Register */ -#define AT91_SAMA5D2_CHER 0x10 + u16 CHER; /* Channel Disable Register */ -#define AT91_SAMA5D2_CHDR 0x14 + u16 CHDR; /* Channel Status Register */ -#define AT91_SAMA5D2_CHSR 0x18 + u16 CHSR; /* Last Converted Data Register */ -#define AT91_SAMA5D2_LCDR 0x20 + u16 LCDR; /* Interrupt Enable Register */ -#define AT91_SAMA5D2_IER 0x24 + u16 IER; /* Interrupt Enable Register - TS X measurement ready */ #define AT91_SAMA5D2_IER_XRDY BIT(20) /* Interrupt Enable Register - TS Y measurement ready */ @@ -109,22 +110,23 @@ #define AT91_SAMA5D2_IER_PEN BIT(29) /* Interrupt Enable Register - No pen detect */ #define AT91_SAMA5D2_IER_NOPEN BIT(30) + /* Interrupt Disable Register */ -#define AT91_SAMA5D2_IDR 0x28 + u16 IDR; /* Interrupt Mask Register */ -#define AT91_SAMA5D2_IMR 0x2c + u16 IMR; /* Interrupt Status Register */ -#define AT91_SAMA5D2_ISR 0x30 + u16 ISR; /* Interrupt Status Register - Pen touching sense status */ #define AT91_SAMA5D2_ISR_PENS BIT(31) /* Last Channel Trigger Mode Register */ -#define AT91_SAMA5D2_LCTMR 0x34 + u16 LCTMR; /* Last Channel Compare Window Register */ -#define AT91_SAMA5D2_LCCWR 0x38 + u16 LCCWR; /* Overrun Status Register */ -#define AT91_SAMA5D2_OVER 0x3c + u16 OVER; /* Extended Mode Register */ -#define AT91_SAMA5D2_EMR 0x40 + u16 EMR; /* Extended Mode Register - Oversampling rate */ #define AT91_SAMA5D2_EMR_OSR(V) ((V) << 16) #define AT91_SAMA5D2_EMR_OSR_MASK GENMASK(17, 16) @@ -134,22 +136,22 @@ /* Extended Mode Register - Averaging on single trigger event */ #define AT91_SAMA5D2_EMR_ASTE(V) ((V) << 20) + /* Compare Window Register */ -#define AT91_SAMA5D2_CWR 0x44 + u16 CWR; /* Channel Gain Register */ -#define AT91_SAMA5D2_CGR 0x48 - + u16 CGR; /* Channel Offset Register */ -#define AT91_SAMA5D2_COR 0x4c + u16 COR; #define AT91_SAMA5D2_COR_DIFF_OFFSET 16 /* Analog Control Register */ -#define AT91_SAMA5D2_ACR 0x94 + u16 ACR; /* Analog Control Register - Pen detect sensitivity mask */ #define AT91_SAMA5D2_ACR_PENDETSENS_MASK GENMASK(1, 0) /* Touchscreen Mode Register */ -#define AT91_SAMA5D2_TSMR 0xb0 + u16 TSMR; /* Touchscreen Mode Register - No touch mode */ #define AT91_SAMA5D2_TSMR_TSMODE_NONE 0 /* Touchscreen Mode Register - 4 wire screen, no pressure measurement */ @@ -178,13 +180,13 @@ #define AT91_SAMA5D2_TSMR_PENDET_ENA BIT(24) /* Touchscreen X Position Register */ -#define AT91_SAMA5D2_XPOSR 0xb4 + u16 XPOSR; /* Touchscreen Y Position Register */ -#define AT91_SAMA5D2_YPOSR 0xb8 + u16 YPOSR; /* Touchscreen Pressure Register */ -#define AT91_SAMA5D2_PRESSR 0xbc + u16 PRESSR; /* Trigger Register */ -#define AT91_SAMA5D2_TRGR 0xc0 + u16 TRGR; /* Mask for TRGMOD field of TRGR register */ #define AT91_SAMA5D2_TRGR_TRGMOD_MASK GENMASK(2, 0) /* No trigger, only software trigger can start conversions */ @@ -203,30 +205,52 @@ #define AT91_SAMA5D2_TRGR_TRGPER(x) ((x) << 16) /* Correction Select Register */ -#define AT91_SAMA5D2_COSR 0xd0 + u16 COSR; /* Correction Value Register */ -#define AT91_SAMA5D2_CVR 0xd4 + u16 CVR; /* Channel Error Correction Register */ -#define AT91_SAMA5D2_CECR 0xd8 + u16 CECR; /* Write Protection Mode Register */ -#define AT91_SAMA5D2_WPMR 0xe4 + u16 WPMR; /* Write Protection Status Register */ -#define AT91_SAMA5D2_WPSR 0xe8 + u16 WPSR; /* Version Register */ -#define AT91_SAMA5D2_VERSION 0xfc - -#define AT91_SAMA5D2_HW_TRIG_CNT 3 -#define AT91_SAMA5D2_SINGLE_CHAN_CNT 12 -#define AT91_SAMA5D2_DIFF_CHAN_CNT 6 - -#define AT91_SAMA5D2_TIMESTAMP_CHAN_IDX (AT91_SAMA5D2_SINGLE_CHAN_CNT + \ - AT91_SAMA5D2_DIFF_CHAN_CNT + 1) + u16 VERSION; +}; -#define AT91_SAMA5D2_TOUCH_X_CHAN_IDX (AT91_SAMA5D2_SINGLE_CHAN_CNT + \ - AT91_SAMA5D2_DIFF_CHAN_CNT * 2) -#define AT91_SAMA5D2_TOUCH_Y_CHAN_IDX (AT91_SAMA5D2_TOUCH_X_CHAN_IDX + 1) -#define AT91_SAMA5D2_TOUCH_P_CHAN_IDX (AT91_SAMA5D2_TOUCH_Y_CHAN_IDX + 1) -#define AT91_SAMA5D2_MAX_CHAN_IDX AT91_SAMA5D2_TOUCH_P_CHAN_IDX +static const struct at91_adc_reg_layout sama5d2_layout = { + .CR = 0x00, + .MR = 0x04, + .SEQR1 = 0x08, + .SEQR2 = 0x0c, + .CHER = 0x10, + .CHDR = 0x14, + .CHSR = 0x18, + .LCDR = 0x20, + .IER = 0x24, + .IDR = 0x28, + .IMR = 0x2c, + .ISR = 0x30, + .LCTMR = 0x34, + .LCCWR = 0x38, + .OVER = 0x3c, + .EMR = 0x40, + .CWR = 0x44, + .CGR = 0x48, + .COR = 0x4c, + .ACR = 0x94, + .TSMR = 0xb0, + .XPOSR = 0xb4, + .YPOSR = 0xb8, + .PRESSR = 0xbc, + .TRGR = 0xc0, + .COSR = 0xd0, + .CVR = 0xd4, + .CECR = 0xd8, + .WPMR = 0xe4, + .WPSR = 0xe8, + .VERSION = 0xfc, +}; #define AT91_SAMA5D2_TOUCH_SAMPLE_PERIOD_US 2000 /* 2ms */ #define AT91_SAMA5D2_TOUCH_PEN_DETECT_DEBOUNCE_US 200 @@ -235,18 +259,6 @@ #define AT91_SAMA5D2_MAX_POS_BITS 12 -/* - * Maximum number of bytes to hold conversion from all channels - * without the timestamp. - */ -#define AT91_BUFFER_MAX_CONVERSION_BYTES ((AT91_SAMA5D2_SINGLE_CHAN_CNT + \ - AT91_SAMA5D2_DIFF_CHAN_CNT) * 2) - -/* This total must also include the timestamp */ -#define AT91_BUFFER_MAX_BYTES (AT91_BUFFER_MAX_CONVERSION_BYTES + 8) - -#define AT91_BUFFER_MAX_HWORDS (AT91_BUFFER_MAX_BYTES / 2) - #define AT91_HWFIFO_MAX_SIZE_STR "128" #define AT91_HWFIFO_MAX_SIZE 128 @@ -255,12 +267,12 @@ #define AT91_OSR_4SAMPLES 4 #define AT91_OSR_16SAMPLES 16 -#define AT91_SAMA5D2_CHAN_SINGLE(num, addr) \ +#define AT91_SAMA5D2_CHAN_SINGLE(index, num, addr) \ { \ .type = IIO_VOLTAGE, \ .channel = num, \ .address = addr, \ - .scan_index = num, \ + .scan_index = index, \ .scan_type = { \ .sign = 'u', \ .realbits = 14, \ @@ -274,14 +286,14 @@ .indexed = 1, \ } -#define AT91_SAMA5D2_CHAN_DIFF(num, num2, addr) \ +#define AT91_SAMA5D2_CHAN_DIFF(index, num, num2, addr) \ { \ .type = IIO_VOLTAGE, \ .differential = 1, \ .channel = num, \ .channel2 = num2, \ .address = addr, \ - .scan_index = num + AT91_SAMA5D2_SINGLE_CHAN_CNT, \ + .scan_index = index, \ .scan_type = { \ .sign = 's', \ .realbits = 14, \ @@ -328,13 +340,51 @@ .datasheet_name = name, \ } -#define at91_adc_readl(st, reg) readl_relaxed(st->base + reg) -#define at91_adc_writel(st, reg, val) writel_relaxed(val, st->base + reg) +#define at91_adc_readl(st, reg) \ + readl_relaxed((st)->base + (st)->soc_info.platform->layout->reg) +#define at91_adc_read_chan(st, reg) \ + readl_relaxed((st)->base + reg) +#define at91_adc_writel(st, reg, val) \ + writel_relaxed(val, (st)->base + (st)->soc_info.platform->layout->reg) + +/** + * struct at91_adc_platform - at91-sama5d2 platform information struct + * @layout: pointer to the reg layout struct + * @adc_channels: pointer to an array of channels for registering in + * the iio subsystem + * @nr_channels: number of physical channels available + * @touch_chan_x: index of the touchscreen X channel + * @touch_chan_y: index of the touchscreen Y channel + * @touch_chan_p: index of the touchscreen P channel + * @max_channels: number of total channels + * @max_index: highest channel index (highest index may be higher + * than the total channel number) + * @hw_trig_cnt: number of possible hardware triggers + */ +struct at91_adc_platform { + const struct at91_adc_reg_layout *layout; + const struct iio_chan_spec (*adc_channels)[]; + unsigned int nr_channels; + unsigned int touch_chan_x; + unsigned int touch_chan_y; + unsigned int touch_chan_p; + unsigned int max_channels; + unsigned int max_index; + unsigned int hw_trig_cnt; +}; +/** + * struct at91_adc_soc_info - at91-sama5d2 soc information struct + * @startup_time: device startup time + * @min_sample_rate: minimum sample rate in Hz + * @max_sample_rate: maximum sample rate in Hz + * @platform: pointer to the platform structure + */ struct at91_adc_soc_info { unsigned startup_time; unsigned min_sample_rate; unsigned max_sample_rate; + const struct at91_adc_platform *platform; }; struct at91_adc_trigger { @@ -382,6 +432,15 @@ struct at91_adc_touch { struct work_struct workq; }; +/* + * Buffer size requirements: + * No channels * bytes_per_channel(2) + timestamp bytes (8) + * Divided by 2 because we need half words. + * We assume 32 channels for now, has to be increased if needed. + * Nobody minds a buffer being too big. + */ +#define AT91_BUFFER_MAX_HWORDS ((32 * 2 + 8) / 2) + struct at91_adc_state { void __iomem *base; int irq; @@ -437,29 +496,51 @@ static const struct at91_adc_trigger at91_adc_trigger_list[] = { }, }; -static const struct iio_chan_spec at91_adc_channels[] = { - AT91_SAMA5D2_CHAN_SINGLE(0, 0x50), - AT91_SAMA5D2_CHAN_SINGLE(1, 0x54), - AT91_SAMA5D2_CHAN_SINGLE(2, 0x58), - AT91_SAMA5D2_CHAN_SINGLE(3, 0x5c), - AT91_SAMA5D2_CHAN_SINGLE(4, 0x60), - AT91_SAMA5D2_CHAN_SINGLE(5, 0x64), - AT91_SAMA5D2_CHAN_SINGLE(6, 0x68), - AT91_SAMA5D2_CHAN_SINGLE(7, 0x6c), - AT91_SAMA5D2_CHAN_SINGLE(8, 0x70), - AT91_SAMA5D2_CHAN_SINGLE(9, 0x74), - AT91_SAMA5D2_CHAN_SINGLE(10, 0x78), - AT91_SAMA5D2_CHAN_SINGLE(11, 0x7c), - AT91_SAMA5D2_CHAN_DIFF(0, 1, 0x50), - AT91_SAMA5D2_CHAN_DIFF(2, 3, 0x58), - AT91_SAMA5D2_CHAN_DIFF(4, 5, 0x60), - AT91_SAMA5D2_CHAN_DIFF(6, 7, 0x68), - AT91_SAMA5D2_CHAN_DIFF(8, 9, 0x70), - AT91_SAMA5D2_CHAN_DIFF(10, 11, 0x78), - IIO_CHAN_SOFT_TIMESTAMP(AT91_SAMA5D2_TIMESTAMP_CHAN_IDX), - AT91_SAMA5D2_CHAN_TOUCH(AT91_SAMA5D2_TOUCH_X_CHAN_IDX, "x", IIO_MOD_X), - AT91_SAMA5D2_CHAN_TOUCH(AT91_SAMA5D2_TOUCH_Y_CHAN_IDX, "y", IIO_MOD_Y), - AT91_SAMA5D2_CHAN_PRESSURE(AT91_SAMA5D2_TOUCH_P_CHAN_IDX, "pressure"), +static const struct iio_chan_spec at91_sama5d2_adc_channels[] = { + AT91_SAMA5D2_CHAN_SINGLE(0, 0, 0x50), + AT91_SAMA5D2_CHAN_SINGLE(1, 1, 0x54), + AT91_SAMA5D2_CHAN_SINGLE(2, 2, 0x58), + AT91_SAMA5D2_CHAN_SINGLE(3, 3, 0x5c), + AT91_SAMA5D2_CHAN_SINGLE(4, 4, 0x60), + AT91_SAMA5D2_CHAN_SINGLE(5, 5, 0x64), + AT91_SAMA5D2_CHAN_SINGLE(6, 6, 0x68), + AT91_SAMA5D2_CHAN_SINGLE(7, 7, 0x6c), + AT91_SAMA5D2_CHAN_SINGLE(8, 8, 0x70), + AT91_SAMA5D2_CHAN_SINGLE(9, 9, 0x74), + AT91_SAMA5D2_CHAN_SINGLE(10, 10, 0x78), + AT91_SAMA5D2_CHAN_SINGLE(11, 11, 0x7c), + /* original ABI has the differential channels with a gap in between */ + AT91_SAMA5D2_CHAN_DIFF(12, 0, 1, 0x50), + AT91_SAMA5D2_CHAN_DIFF(14, 2, 3, 0x58), + AT91_SAMA5D2_CHAN_DIFF(16, 4, 5, 0x60), + AT91_SAMA5D2_CHAN_DIFF(18, 6, 7, 0x68), + AT91_SAMA5D2_CHAN_DIFF(20, 8, 9, 0x70), + AT91_SAMA5D2_CHAN_DIFF(22, 10, 11, 0x78), + IIO_CHAN_SOFT_TIMESTAMP(23), + AT91_SAMA5D2_CHAN_TOUCH(24, "x", IIO_MOD_X), + AT91_SAMA5D2_CHAN_TOUCH(25, "y", IIO_MOD_Y), + AT91_SAMA5D2_CHAN_PRESSURE(26, "pressure"), +}; + +static const struct at91_adc_platform sama5d2_platform = { + .layout = &sama5d2_layout, + .adc_channels = &at91_sama5d2_adc_channels, +#define AT91_SAMA5D2_SINGLE_CHAN_CNT 12 +#define AT91_SAMA5D2_DIFF_CHAN_CNT 6 + .nr_channels = AT91_SAMA5D2_SINGLE_CHAN_CNT + + AT91_SAMA5D2_DIFF_CHAN_CNT, +#define AT91_SAMA5D2_TOUCH_X_CHAN_IDX (AT91_SAMA5D2_SINGLE_CHAN_CNT + \ + AT91_SAMA5D2_DIFF_CHAN_CNT * 2) + .touch_chan_x = AT91_SAMA5D2_TOUCH_X_CHAN_IDX, +#define AT91_SAMA5D2_TOUCH_Y_CHAN_IDX (AT91_SAMA5D2_TOUCH_X_CHAN_IDX + 1) + .touch_chan_y = AT91_SAMA5D2_TOUCH_Y_CHAN_IDX, +#define AT91_SAMA5D2_TOUCH_P_CHAN_IDX (AT91_SAMA5D2_TOUCH_Y_CHAN_IDX + 1) + .touch_chan_p = AT91_SAMA5D2_TOUCH_P_CHAN_IDX, +#define AT91_SAMA5D2_MAX_CHAN_IDX AT91_SAMA5D2_TOUCH_P_CHAN_IDX + .max_channels = ARRAY_SIZE(at91_sama5d2_adc_channels), + .max_index = AT91_SAMA5D2_MAX_CHAN_IDX, +#define AT91_SAMA5D2_HW_TRIG_CNT 3 + .hw_trig_cnt = AT91_SAMA5D2_HW_TRIG_CNT, }; static int at91_adc_chan_xlate(struct iio_dev *indio_dev, int chan) @@ -493,6 +574,7 @@ static unsigned int at91_adc_active_scan_mask_to_reg(struct iio_dev *indio_dev) { u32 mask = 0; u8 bit; + struct at91_adc_state *st = iio_priv(indio_dev); for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->num_channels) { @@ -501,13 +583,13 @@ static unsigned int at91_adc_active_scan_mask_to_reg(struct iio_dev *indio_dev) mask |= BIT(chan->channel); } - return mask & GENMASK(11, 0); + return mask & GENMASK(st->soc_info.platform->nr_channels, 0); } static void at91_adc_config_emr(struct at91_adc_state *st) { /* configure the extended mode register */ - unsigned int emr = at91_adc_readl(st, AT91_SAMA5D2_EMR); + unsigned int emr = at91_adc_readl(st, EMR); /* select oversampling per single trigger event */ emr |= AT91_SAMA5D2_EMR_ASTE(1); @@ -531,7 +613,7 @@ static void at91_adc_config_emr(struct at91_adc_state *st) break; } - at91_adc_writel(st, AT91_SAMA5D2_EMR, emr); + at91_adc_writel(st, EMR, emr); } static int at91_adc_adjust_val_osr(struct at91_adc_state *st, int *val) @@ -584,9 +666,9 @@ static int at91_adc_configure_touch(struct at91_adc_state *st, bool state) if (!state) { /* disabling touch IRQs and setting mode to no touch enabled */ - at91_adc_writel(st, AT91_SAMA5D2_IDR, + at91_adc_writel(st, IDR, AT91_SAMA5D2_IER_PEN | AT91_SAMA5D2_IER_NOPEN); - at91_adc_writel(st, AT91_SAMA5D2_TSMR, 0); + at91_adc_writel(st, TSMR, 0); return 0; } /* @@ -612,26 +694,26 @@ static int at91_adc_configure_touch(struct at91_adc_state *st, bool state) tsmr |= AT91_SAMA5D2_TSMR_PENDET_ENA; tsmr |= AT91_SAMA5D2_TSMR_TSFREQ(2) & AT91_SAMA5D2_TSMR_TSFREQ_MASK; - at91_adc_writel(st, AT91_SAMA5D2_TSMR, tsmr); + at91_adc_writel(st, TSMR, tsmr); - acr = at91_adc_readl(st, AT91_SAMA5D2_ACR); + acr = at91_adc_readl(st, ACR); acr &= ~AT91_SAMA5D2_ACR_PENDETSENS_MASK; acr |= 0x02 & AT91_SAMA5D2_ACR_PENDETSENS_MASK; - at91_adc_writel(st, AT91_SAMA5D2_ACR, acr); + at91_adc_writel(st, ACR, acr); /* Sample Period Time = (TRGPER + 1) / ADCClock */ st->touch_st.sample_period_val = round_up((AT91_SAMA5D2_TOUCH_SAMPLE_PERIOD_US * clk_khz / 1000) - 1, 1); /* enable pen detect IRQ */ - at91_adc_writel(st, AT91_SAMA5D2_IER, AT91_SAMA5D2_IER_PEN); + at91_adc_writel(st, IER, AT91_SAMA5D2_IER_PEN); return 0; } static u16 at91_adc_touch_pos(struct at91_adc_state *st, int reg) { - u32 val; + u32 val = 0; u32 scale, result, pos; /* @@ -640,7 +722,11 @@ static u16 at91_adc_touch_pos(struct at91_adc_state *st, int reg) * max = 2^AT91_SAMA5D2_MAX_POS_BITS - 1 */ /* first half of register is the x or y, second half is the scale */ - val = at91_adc_readl(st, reg); + if (reg == st->soc_info.platform->layout->XPOSR) + val = at91_adc_readl(st, XPOSR); + else if (reg == st->soc_info.platform->layout->YPOSR) + val = at91_adc_readl(st, YPOSR); + if (!val) dev_dbg(&st->indio_dev->dev, "pos is 0\n"); @@ -658,13 +744,13 @@ static u16 at91_adc_touch_pos(struct at91_adc_state *st, int reg) static u16 at91_adc_touch_x_pos(struct at91_adc_state *st) { - st->touch_st.x_pos = at91_adc_touch_pos(st, AT91_SAMA5D2_XPOSR); + st->touch_st.x_pos = at91_adc_touch_pos(st, st->soc_info.platform->layout->XPOSR); return st->touch_st.x_pos; } static u16 at91_adc_touch_y_pos(struct at91_adc_state *st) { - return at91_adc_touch_pos(st, AT91_SAMA5D2_YPOSR); + return at91_adc_touch_pos(st, st->soc_info.platform->layout->YPOSR); } static u16 at91_adc_touch_pressure(struct at91_adc_state *st) @@ -676,7 +762,7 @@ static u16 at91_adc_touch_pressure(struct at91_adc_state *st) u32 factor = 1000; /* calculate the pressure */ - val = at91_adc_readl(st, AT91_SAMA5D2_PRESSR); + val = at91_adc_readl(st, PRESSR); z1 = val & AT91_SAMA5D2_XYZ_MASK; z2 = (val >> 16) & AT91_SAMA5D2_XYZ_MASK; @@ -700,9 +786,9 @@ static int at91_adc_read_position(struct at91_adc_state *st, int chan, u16 *val) *val = 0; if (!st->touch_st.touching) return -ENODATA; - if (chan == AT91_SAMA5D2_TOUCH_X_CHAN_IDX) + if (chan == st->soc_info.platform->touch_chan_x) *val = at91_adc_touch_x_pos(st); - else if (chan == AT91_SAMA5D2_TOUCH_Y_CHAN_IDX) + else if (chan == st->soc_info.platform->touch_chan_y) *val = at91_adc_touch_y_pos(st); else return -ENODATA; @@ -715,7 +801,7 @@ static int at91_adc_read_pressure(struct at91_adc_state *st, int chan, u16 *val) *val = 0; if (!st->touch_st.touching) return -ENODATA; - if (chan == AT91_SAMA5D2_TOUCH_P_CHAN_IDX) + if (chan == st->soc_info.platform->touch_chan_p) *val = at91_adc_touch_pressure(st); else return -ENODATA; @@ -727,7 +813,7 @@ static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state) { struct iio_dev *indio = iio_trigger_get_drvdata(trig); struct at91_adc_state *st = iio_priv(indio); - u32 status = at91_adc_readl(st, AT91_SAMA5D2_TRGR); + u32 status = at91_adc_readl(st, TRGR); /* clear TRGMOD */ status &= ~AT91_SAMA5D2_TRGR_TRGMOD_MASK; @@ -736,7 +822,7 @@ static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state) status |= st->selected_trig->trgmod_value; /* set/unset hw trigger */ - at91_adc_writel(st, AT91_SAMA5D2_TRGR, status); + at91_adc_writel(st, TRGR, status); return 0; } @@ -753,7 +839,7 @@ static void at91_adc_reenable_trigger(struct iio_trigger *trig) enable_irq(st->irq); /* Needed to ACK the DRDY interruption */ - at91_adc_readl(st, AT91_SAMA5D2_LCDR); + at91_adc_readl(st, LCDR); } static const struct iio_trigger_ops at91_adc_trigger_ops = { @@ -848,7 +934,7 @@ static int at91_adc_dma_start(struct iio_dev *indio_dev) } /* enable general overrun error signaling */ - at91_adc_writel(st, AT91_SAMA5D2_IER, AT91_SAMA5D2_IER_GOVRE); + at91_adc_writel(st, IER, AT91_SAMA5D2_IER_GOVRE); /* Issue pending DMA requests */ dma_async_issue_pending(st->dma_st.dma_chan); @@ -878,7 +964,7 @@ static bool at91_adc_current_chan_is_touch(struct iio_dev *indio_dev) return !!bitmap_subset(indio_dev->active_scan_mask, &st->touch_st.channels_bitmask, - AT91_SAMA5D2_MAX_CHAN_IDX + 1); + st->soc_info.platform->max_index + 1); } static int at91_adc_buffer_prepare(struct iio_dev *indio_dev) @@ -915,7 +1001,7 @@ static int at91_adc_buffer_prepare(struct iio_dev *indio_dev) chan->type == IIO_PRESSURE) continue; - cor = at91_adc_readl(st, AT91_SAMA5D2_COR); + cor = at91_adc_readl(st, COR); if (chan->differential) cor |= (BIT(chan->channel) | BIT(chan->channel2)) << @@ -924,13 +1010,13 @@ static int at91_adc_buffer_prepare(struct iio_dev *indio_dev) cor &= ~(BIT(chan->channel) << AT91_SAMA5D2_COR_DIFF_OFFSET); - at91_adc_writel(st, AT91_SAMA5D2_COR, cor); + at91_adc_writel(st, COR, cor); - at91_adc_writel(st, AT91_SAMA5D2_CHER, BIT(chan->channel)); + at91_adc_writel(st, CHER, BIT(chan->channel)); } if (at91_adc_buffer_check_use_irq(indio_dev, st)) - at91_adc_writel(st, AT91_SAMA5D2_IER, AT91_SAMA5D2_IER_DRDY); + at91_adc_writel(st, IER, AT91_SAMA5D2_IER_DRDY); return 0; } @@ -966,17 +1052,17 @@ static int at91_adc_buffer_postdisable(struct iio_dev *indio_dev) chan->type == IIO_PRESSURE) continue; - at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel)); + at91_adc_writel(st, CHDR, BIT(chan->channel)); if (st->dma_st.dma_chan) - at91_adc_readl(st, chan->address); + at91_adc_read_chan(st, chan->address); } if (at91_adc_buffer_check_use_irq(indio_dev, st)) - at91_adc_writel(st, AT91_SAMA5D2_IDR, AT91_SAMA5D2_IER_DRDY); + at91_adc_writel(st, IDR, AT91_SAMA5D2_IER_DRDY); /* read overflow register to clear possible overflow status */ - at91_adc_readl(st, AT91_SAMA5D2_OVER); + at91_adc_readl(st, OVER); /* if we are using DMA we must clear registers and end DMA */ if (st->dma_st.dma_chan) @@ -1024,7 +1110,7 @@ static void at91_adc_trigger_handler_nodma(struct iio_dev *indio_dev, * Check if the conversion is ready. If not, wait a little bit, and * in case of timeout exit with an error. */ - while ((at91_adc_readl(st, AT91_SAMA5D2_ISR) & mask) != mask && + while ((at91_adc_readl(st, ISR) & mask) != mask && timeout) { usleep_range(50, 100); timeout--; @@ -1052,7 +1138,7 @@ static void at91_adc_trigger_handler_nodma(struct iio_dev *indio_dev, * Thus, emit a warning. */ if (chan->type == IIO_VOLTAGE) { - val = at91_adc_readl(st, chan->address); + val = at91_adc_read_chan(st, chan->address); at91_adc_adjust_val_osr(st, &val); st->buffer[i] = val; } else { @@ -1073,7 +1159,7 @@ static void at91_adc_trigger_handler_dma(struct iio_dev *indio_dev) s64 interval; int sample_index = 0, sample_count, sample_size; - u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR); + u32 status = at91_adc_readl(st, ISR); /* if we reached this point, we cannot sample faster */ if (status & AT91_SAMA5D2_IER_GOVRE) pr_info_ratelimited("%s: conversion overrun detected\n", @@ -1125,7 +1211,7 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p) * actually polling the trigger now. */ if (iio_trigger_validate_own_device(indio_dev->trig, indio_dev)) - at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_START); + at91_adc_writel(st, CR, AT91_SAMA5D2_CR_START); if (st->dma_st.dma_chan) at91_adc_trigger_handler_dma(indio_dev); @@ -1172,11 +1258,11 @@ static void at91_adc_setup_samp_freq(struct iio_dev *indio_dev, unsigned freq) startup = at91_adc_startup_time(st->soc_info.startup_time, freq / 1000); - mr = at91_adc_readl(st, AT91_SAMA5D2_MR); + mr = at91_adc_readl(st, MR); mr &= ~(AT91_SAMA5D2_MR_STARTUP_MASK | AT91_SAMA5D2_MR_PRESCAL_MASK); mr |= AT91_SAMA5D2_MR_STARTUP(startup); mr |= AT91_SAMA5D2_MR_PRESCAL(prescal); - at91_adc_writel(st, AT91_SAMA5D2_MR, mr); + at91_adc_writel(st, MR, mr); dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u\n", freq, startup, prescal); @@ -1196,7 +1282,7 @@ static void at91_adc_touch_data_handler(struct iio_dev *indio_dev) int i = 0; for_each_set_bit(bit, indio_dev->active_scan_mask, - AT91_SAMA5D2_MAX_CHAN_IDX + 1) { + st->soc_info.platform->max_index + 1) { struct iio_chan_spec const *chan = at91_adc_chan_get(indio_dev, bit); @@ -1222,12 +1308,11 @@ static void at91_adc_touch_data_handler(struct iio_dev *indio_dev) static void at91_adc_pen_detect_interrupt(struct at91_adc_state *st) { - at91_adc_writel(st, AT91_SAMA5D2_IDR, AT91_SAMA5D2_IER_PEN); - at91_adc_writel(st, AT91_SAMA5D2_IER, AT91_SAMA5D2_IER_NOPEN | + at91_adc_writel(st, IDR, AT91_SAMA5D2_IER_PEN); + at91_adc_writel(st, IER, AT91_SAMA5D2_IER_NOPEN | AT91_SAMA5D2_IER_XRDY | AT91_SAMA5D2_IER_YRDY | AT91_SAMA5D2_IER_PRDY); - at91_adc_writel(st, AT91_SAMA5D2_TRGR, - AT91_SAMA5D2_TRGR_TRGMOD_PERIODIC | + at91_adc_writel(st, TRGR, AT91_SAMA5D2_TRGR_TRGMOD_PERIODIC | AT91_SAMA5D2_TRGR_TRGPER(st->touch_st.sample_period_val)); st->touch_st.touching = true; } @@ -1236,16 +1321,15 @@ static void at91_adc_no_pen_detect_interrupt(struct iio_dev *indio_dev) { struct at91_adc_state *st = iio_priv(indio_dev); - at91_adc_writel(st, AT91_SAMA5D2_TRGR, - AT91_SAMA5D2_TRGR_TRGMOD_NO_TRIGGER); - at91_adc_writel(st, AT91_SAMA5D2_IDR, AT91_SAMA5D2_IER_NOPEN | + at91_adc_writel(st, TRGR, AT91_SAMA5D2_TRGR_TRGMOD_NO_TRIGGER); + at91_adc_writel(st, IDR, AT91_SAMA5D2_IER_NOPEN | AT91_SAMA5D2_IER_XRDY | AT91_SAMA5D2_IER_YRDY | AT91_SAMA5D2_IER_PRDY); st->touch_st.touching = false; at91_adc_touch_data_handler(indio_dev); - at91_adc_writel(st, AT91_SAMA5D2_IER, AT91_SAMA5D2_IER_PEN); + at91_adc_writel(st, IER, AT91_SAMA5D2_IER_PEN); } static void at91_adc_workq_handler(struct work_struct *workq) @@ -1263,8 +1347,8 @@ static irqreturn_t at91_adc_interrupt(int irq, void *private) { struct iio_dev *indio = private; struct at91_adc_state *st = iio_priv(indio); - u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR); - u32 imr = at91_adc_readl(st, AT91_SAMA5D2_IMR); + u32 status = at91_adc_readl(st, ISR); + u32 imr = at91_adc_readl(st, IMR); u32 rdy_mask = AT91_SAMA5D2_IER_XRDY | AT91_SAMA5D2_IER_YRDY | AT91_SAMA5D2_IER_PRDY; @@ -1285,9 +1369,9 @@ static irqreturn_t at91_adc_interrupt(int irq, void *private) * touching, but the measurements are not ready yet. * read and ignore. */ - status = at91_adc_readl(st, AT91_SAMA5D2_XPOSR); - status = at91_adc_readl(st, AT91_SAMA5D2_YPOSR); - status = at91_adc_readl(st, AT91_SAMA5D2_PRESSR); + status = at91_adc_readl(st, XPOSR); + status = at91_adc_readl(st, YPOSR); + status = at91_adc_readl(st, PRESSR); } else if (iio_buffer_enabled(indio) && (status & AT91_SAMA5D2_IER_DRDY)) { /* triggered buffer without DMA */ @@ -1299,7 +1383,7 @@ static irqreturn_t at91_adc_interrupt(int irq, void *private) WARN(true, "Unexpected irq occurred\n"); } else if (!iio_buffer_enabled(indio)) { /* software requested conversion */ - st->conversion_value = at91_adc_readl(st, st->chan->address); + st->conversion_value = at91_adc_read_chan(st, st->chan->address); st->conversion_done = true; wake_up_interruptible(&st->wq_data_available); } @@ -1360,10 +1444,10 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, cor = (BIT(chan->channel) | BIT(chan->channel2)) << AT91_SAMA5D2_COR_DIFF_OFFSET; - at91_adc_writel(st, AT91_SAMA5D2_COR, cor); - at91_adc_writel(st, AT91_SAMA5D2_CHER, BIT(chan->channel)); - at91_adc_writel(st, AT91_SAMA5D2_IER, BIT(chan->channel)); - at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_START); + at91_adc_writel(st, COR, cor); + at91_adc_writel(st, CHER, BIT(chan->channel)); + at91_adc_writel(st, IER, BIT(chan->channel)); + at91_adc_writel(st, CR, AT91_SAMA5D2_CR_START); ret = wait_event_interruptible_timeout(st->wq_data_available, st->conversion_done, @@ -1379,11 +1463,11 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, st->conversion_done = false; } - at91_adc_writel(st, AT91_SAMA5D2_IDR, BIT(chan->channel)); - at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel)); + at91_adc_writel(st, IDR, BIT(chan->channel)); + at91_adc_writel(st, CHDR, BIT(chan->channel)); /* Needed to ACK the DRDY interruption */ - at91_adc_readl(st, AT91_SAMA5D2_LCDR); + at91_adc_readl(st, LCDR); mutex_unlock(&st->lock); @@ -1455,14 +1539,15 @@ static void at91_adc_dma_init(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct at91_adc_state *st = iio_priv(indio_dev); struct dma_slave_config config = {0}; + /* we have 2 bytes for each channel */ + unsigned int sample_size = st->soc_info.platform->nr_channels * 2; /* * We make the buffer double the size of the fifo, * such that DMA uses one half of the buffer (full fifo size) * and the software uses the other half to read/write. */ unsigned int pages = DIV_ROUND_UP(AT91_HWFIFO_MAX_SIZE * - AT91_BUFFER_MAX_CONVERSION_BYTES * 2, - PAGE_SIZE); + sample_size * 2, PAGE_SIZE); if (st->dma_st.dma_chan) return; @@ -1486,7 +1571,7 @@ static void at91_adc_dma_init(struct platform_device *pdev) /* Configure DMA channel to read data register */ config.direction = DMA_DEV_TO_MEM; config.src_addr = (phys_addr_t)(st->dma_st.phys_addr - + AT91_SAMA5D2_LCDR); + + st->soc_info.platform->layout->LCDR); config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; config.src_maxburst = 1; config.dst_maxburst = 1; @@ -1515,9 +1600,10 @@ static void at91_adc_dma_disable(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct at91_adc_state *st = iio_priv(indio_dev); + /* we have 2 bytes for each channel */ + unsigned int sample_size = st->soc_info.platform->nr_channels * 2; unsigned int pages = DIV_ROUND_UP(AT91_HWFIFO_MAX_SIZE * - AT91_BUFFER_MAX_CONVERSION_BYTES * 2, - PAGE_SIZE); + sample_size * 2, PAGE_SIZE); /* if we are not using DMA, just return */ if (!st->dma_st.dma_chan) @@ -1578,14 +1664,14 @@ static int at91_adc_update_scan_mode(struct iio_dev *indio_dev, struct at91_adc_state *st = iio_priv(indio_dev); if (bitmap_subset(scan_mask, &st->touch_st.channels_bitmask, - AT91_SAMA5D2_MAX_CHAN_IDX + 1)) + st->soc_info.platform->max_index + 1)) return 0; /* * if the new bitmap is a combination of touchscreen and regular * channels, then we are not fine */ if (bitmap_intersects(&st->touch_st.channels_bitmask, scan_mask, - AT91_SAMA5D2_MAX_CHAN_IDX + 1)) + st->soc_info.platform->max_index + 1)) return -EINVAL; return 0; } @@ -1594,13 +1680,13 @@ static void at91_adc_hw_init(struct iio_dev *indio_dev) { struct at91_adc_state *st = iio_priv(indio_dev); - at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST); - at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff); + at91_adc_writel(st, CR, AT91_SAMA5D2_CR_SWRST); + at91_adc_writel(st, IDR, 0xffffffff); /* * Transfer field must be set to 2 according to the datasheet and * allows different analog settings for each channel. */ - at91_adc_writel(st, AT91_SAMA5D2_MR, + at91_adc_writel(st, MR, AT91_SAMA5D2_MR_TRANSFER(2) | AT91_SAMA5D2_MR_ANACH); at91_adc_setup_samp_freq(indio_dev, st->soc_info.min_sample_rate); @@ -1716,21 +1802,23 @@ static int at91_adc_probe(struct platform_device *pdev) if (!indio_dev) return -ENOMEM; + st = iio_priv(indio_dev); + st->indio_dev = indio_dev; + + st->soc_info.platform = of_device_get_match_data(&pdev->dev); + indio_dev->name = dev_name(&pdev->dev); indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; indio_dev->info = &at91_adc_info; - indio_dev->channels = at91_adc_channels; - indio_dev->num_channels = ARRAY_SIZE(at91_adc_channels); - - st = iio_priv(indio_dev); - st->indio_dev = indio_dev; + indio_dev->channels = *st->soc_info.platform->adc_channels; + indio_dev->num_channels = st->soc_info.platform->max_channels; bitmap_set(&st->touch_st.channels_bitmask, - AT91_SAMA5D2_TOUCH_X_CHAN_IDX, 1); + st->soc_info.platform->touch_chan_x, 1); bitmap_set(&st->touch_st.channels_bitmask, - AT91_SAMA5D2_TOUCH_Y_CHAN_IDX, 1); + st->soc_info.platform->touch_chan_y, 1); bitmap_set(&st->touch_st.channels_bitmask, - AT91_SAMA5D2_TOUCH_P_CHAN_IDX, 1); + st->soc_info.platform->touch_chan_p, 1); st->oversampling_ratio = AT91_OSR_1SAMPLES; @@ -1770,7 +1858,7 @@ static int at91_adc_probe(struct platform_device *pdev) st->selected_trig = NULL; /* find the right trigger, or no trigger at all */ - for (i = 0; i < AT91_SAMA5D2_HW_TRIG_CNT + 1; i++) + for (i = 0; i < st->soc_info.platform->hw_trig_cnt + 1; i++) if (at91_adc_trigger_list[i].edge_type == edge_type) { st->selected_trig = &at91_adc_trigger_list[i]; break; @@ -1855,7 +1943,7 @@ static int at91_adc_probe(struct platform_device *pdev) st->selected_trig->name); dev_info(&pdev->dev, "version: %x\n", - readl_relaxed(st->base + AT91_SAMA5D2_VERSION)); + readl_relaxed(st->base + st->soc_info.platform->layout->VERSION)); return 0; @@ -1898,7 +1986,7 @@ static __maybe_unused int at91_adc_suspend(struct device *dev) * and can be used by for other devices. * Otherwise, ADC will hog them and we can't go to suspend mode. */ - at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST); + at91_adc_writel(st, CR, AT91_SAMA5D2_CR_SWRST); clk_disable_unprepare(st->per_clk); regulator_disable(st->vref); @@ -1958,6 +2046,7 @@ static SIMPLE_DEV_PM_OPS(at91_adc_pm_ops, at91_adc_suspend, at91_adc_resume); static const struct of_device_id at91_adc_dt_match[] = { { .compatible = "atmel,sama5d2-adc", + .data = (const void *)&sama5d2_platform, }, { /* sentinel */ } -- cgit v1.2.3-70-g09d2 From e6d5eee4dfa28a3517b8089c7a6f3eb3f3fa1456 Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Wed, 1 Sep 2021 15:30:08 +0300 Subject: iio: adc: at91-sama5d2_adc: add support for separate end of conversion registers Some platforms have separated the end-of-conversion information from the usual ISR/IMR/IER/IDR registers, into EOC_ISR/EOC_IMR/EOC_IER/EOC_IDR. To cope with both variants, helpers are being added, that will make code more clear and more easy to read. Signed-off-by: Eugen Hristev Link: https://lore.kernel.org/r/20210901123013.329792-6-eugen.hristev@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 66 ++++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index ae9978dcb4c3..21ebd379b46b 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -117,6 +117,14 @@ struct at91_adc_reg_layout { u16 IMR; /* Interrupt Status Register */ u16 ISR; +/* End of Conversion Interrupt Enable Register */ + u16 EOC_IER; +/* End of Conversion Interrupt Disable Register */ + u16 EOC_IDR; +/* End of Conversion Interrupt Mask Register */ + u16 EOC_IMR; +/* End of Conversion Interrupt Status Register */ + u16 EOC_ISR; /* Interrupt Status Register - Pen touching sense status */ #define AT91_SAMA5D2_ISR_PENS BIT(31) /* Last Channel Trigger Mode Register */ @@ -586,6 +594,44 @@ static unsigned int at91_adc_active_scan_mask_to_reg(struct iio_dev *indio_dev) return mask & GENMASK(st->soc_info.platform->nr_channels, 0); } +static void at91_adc_irq_status(struct at91_adc_state *st, u32 *status, + u32 *eoc) +{ + *status = at91_adc_readl(st, ISR); + if (st->soc_info.platform->layout->EOC_ISR) + *eoc = at91_adc_readl(st, EOC_ISR); + else + *eoc = *status; +} + +static void at91_adc_irq_mask(struct at91_adc_state *st, u32 *status, u32 *eoc) +{ + *status = at91_adc_readl(st, IMR); + if (st->soc_info.platform->layout->EOC_IMR) + *eoc = at91_adc_readl(st, EOC_IMR); + else + *eoc = *status; +} + +static void at91_adc_eoc_dis(struct at91_adc_state *st, unsigned int channel) +{ + /* + * On some products having the EOC bits in a separate register, + * errata recommends not writing this register (EOC_IDR). + * On products having the EOC bits in the IDR register, it's fine to write it. + */ + if (!st->soc_info.platform->layout->EOC_IDR) + at91_adc_writel(st, IDR, BIT(channel)); +} + +static void at91_adc_eoc_ena(struct at91_adc_state *st, unsigned int channel) +{ + if (!st->soc_info.platform->layout->EOC_IDR) + at91_adc_writel(st, IER, BIT(channel)); + else + at91_adc_writel(st, EOC_IER, BIT(channel)); +} + static void at91_adc_config_emr(struct at91_adc_state *st) { /* configure the extended mode register */ @@ -1105,13 +1151,15 @@ static void at91_adc_trigger_handler_nodma(struct iio_dev *indio_dev, u8 bit; u32 mask = at91_adc_active_scan_mask_to_reg(indio_dev); unsigned int timeout = 50; + u32 status, imr, eoc = 0, eoc_imr; /* * Check if the conversion is ready. If not, wait a little bit, and * in case of timeout exit with an error. */ - while ((at91_adc_readl(st, ISR) & mask) != mask && - timeout) { + while (((eoc & mask) != mask) && timeout) { + at91_adc_irq_status(st, &status, &eoc); + at91_adc_irq_mask(st, &imr, &eoc_imr); usleep_range(50, 100); timeout--; } @@ -1347,12 +1395,14 @@ static irqreturn_t at91_adc_interrupt(int irq, void *private) { struct iio_dev *indio = private; struct at91_adc_state *st = iio_priv(indio); - u32 status = at91_adc_readl(st, ISR); - u32 imr = at91_adc_readl(st, IMR); + u32 status, eoc, imr, eoc_imr; u32 rdy_mask = AT91_SAMA5D2_IER_XRDY | AT91_SAMA5D2_IER_YRDY | AT91_SAMA5D2_IER_PRDY; - if (!(status & imr)) + at91_adc_irq_status(st, &status, &eoc); + at91_adc_irq_mask(st, &imr, &eoc_imr); + + if (!(status & imr) && !(eoc & eoc_imr)) return IRQ_NONE; if (status & AT91_SAMA5D2_IER_PEN) { /* pen detected IRQ */ @@ -1446,7 +1496,7 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, at91_adc_writel(st, COR, cor); at91_adc_writel(st, CHER, BIT(chan->channel)); - at91_adc_writel(st, IER, BIT(chan->channel)); + at91_adc_eoc_ena(st, chan->channel); at91_adc_writel(st, CR, AT91_SAMA5D2_CR_START); ret = wait_event_interruptible_timeout(st->wq_data_available, @@ -1463,7 +1513,7 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, st->conversion_done = false; } - at91_adc_writel(st, IDR, BIT(chan->channel)); + at91_adc_eoc_dis(st, st->chan->channel); at91_adc_writel(st, CHDR, BIT(chan->channel)); /* Needed to ACK the DRDY interruption */ @@ -1681,6 +1731,8 @@ static void at91_adc_hw_init(struct iio_dev *indio_dev) struct at91_adc_state *st = iio_priv(indio_dev); at91_adc_writel(st, CR, AT91_SAMA5D2_CR_SWRST); + if (st->soc_info.platform->layout->EOC_IDR) + at91_adc_writel(st, EOC_IDR, 0xffffffff); at91_adc_writel(st, IDR, 0xffffffff); /* * Transfer field must be set to 2 according to the datasheet and -- cgit v1.2.3-70-g09d2 From d8004c5f46ded7c69276fe5d377dc919aefce67d Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Wed, 1 Sep 2021 15:30:09 +0300 Subject: iio: adc: at91-sama5d2_adc: add helper for COR register Add helper for the COR register. This helper allows to modify the COR register, removes duplicate code and improves readability. The COR offset is now part of the register layout. This will allow different platform with a different offset to use the same helper. Signed-off-by: Eugen Hristev Link: https://lore.kernel.org/r/20210901123013.329792-7-eugen.hristev@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 40 +++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 21ebd379b46b..3de22b66c075 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -151,8 +151,8 @@ struct at91_adc_reg_layout { u16 CGR; /* Channel Offset Register */ u16 COR; -#define AT91_SAMA5D2_COR_DIFF_OFFSET 16 - +/* Channel Offset Register differential offset - constant, not a register */ + u16 COR_diff_offset; /* Analog Control Register */ u16 ACR; /* Analog Control Register - Pen detect sensitivity mask */ @@ -246,6 +246,7 @@ static const struct at91_adc_reg_layout sama5d2_layout = { .CWR = 0x44, .CGR = 0x48, .COR = 0x4c, + .COR_diff_offset = 16, .ACR = 0x94, .TSMR = 0xb0, .XPOSR = 0xb4, @@ -594,6 +595,21 @@ static unsigned int at91_adc_active_scan_mask_to_reg(struct iio_dev *indio_dev) return mask & GENMASK(st->soc_info.platform->nr_channels, 0); } +static void at91_adc_cor(struct at91_adc_state *st, + struct iio_chan_spec const *chan) +{ + u32 cor, cur_cor; + + cor = BIT(chan->channel) | BIT(chan->channel2); + + cur_cor = at91_adc_readl(st, COR); + cor <<= st->soc_info.platform->layout->COR_diff_offset; + if (chan->differential) + at91_adc_writel(st, COR, cur_cor | cor); + else + at91_adc_writel(st, COR, cur_cor & ~cor); +} + static void at91_adc_irq_status(struct at91_adc_state *st, u32 *status, u32 *eoc) { @@ -1038,8 +1054,6 @@ static int at91_adc_buffer_prepare(struct iio_dev *indio_dev) indio_dev->num_channels) { struct iio_chan_spec const *chan = at91_adc_chan_get(indio_dev, bit); - u32 cor; - if (!chan) continue; /* these channel types cannot be handled by this trigger */ @@ -1047,16 +1061,7 @@ static int at91_adc_buffer_prepare(struct iio_dev *indio_dev) chan->type == IIO_PRESSURE) continue; - cor = at91_adc_readl(st, COR); - - if (chan->differential) - cor |= (BIT(chan->channel) | BIT(chan->channel2)) << - AT91_SAMA5D2_COR_DIFF_OFFSET; - else - cor &= ~(BIT(chan->channel) << - AT91_SAMA5D2_COR_DIFF_OFFSET); - - at91_adc_writel(st, COR, cor); + at91_adc_cor(st, chan); at91_adc_writel(st, CHER, BIT(chan->channel)); } @@ -1444,7 +1449,6 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) { struct at91_adc_state *st = iio_priv(indio_dev); - u32 cor = 0; u16 tmp_val; int ret; @@ -1490,11 +1494,7 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, st->chan = chan; - if (chan->differential) - cor = (BIT(chan->channel) | BIT(chan->channel2)) << - AT91_SAMA5D2_COR_DIFF_OFFSET; - - at91_adc_writel(st, COR, cor); + at91_adc_cor(st, chan); at91_adc_writel(st, CHER, BIT(chan->channel)); at91_adc_eoc_ena(st, chan->channel); at91_adc_writel(st, CR, AT91_SAMA5D2_CR_START); -- cgit v1.2.3-70-g09d2 From 840bf6cb983f48794e79499d454e57164f9c6596 Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Wed, 1 Sep 2021 15:30:10 +0300 Subject: iio: adc: at91-sama5d2_adc: add support for sama7g5 device Add support to sama7g5 ADC which is similar with sama5d2/sam9x60 device. Differences are highlighted by compatible. Main differences include 16 channels instead of 12 and missing resistive touchscreen. Signed-off-by: Eugen Hristev Link: https://lore.kernel.org/r/20210901123013.329792-8-eugen.hristev@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 78 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 3de22b66c075..832446296a34 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -261,6 +261,38 @@ static const struct at91_adc_reg_layout sama5d2_layout = { .VERSION = 0xfc, }; +static const struct at91_adc_reg_layout sama7g5_layout = { + .CR = 0x00, + .MR = 0x04, + .SEQR1 = 0x08, + .SEQR2 = 0x0c, + .CHER = 0x10, + .CHDR = 0x14, + .CHSR = 0x18, + .LCDR = 0x20, + .IER = 0x24, + .IDR = 0x28, + .IMR = 0x2c, + .ISR = 0x30, + .EOC_IER = 0x34, + .EOC_IDR = 0x38, + .EOC_IMR = 0x3c, + .EOC_ISR = 0x40, + .OVER = 0x4c, + .EMR = 0x50, + .CWR = 0x54, + .COR = 0x5c, + .COR_diff_offset = 0, + .ACR = 0xe0, + .TRGR = 0x100, + .COSR = 0x104, + .CVR = 0x108, + .CECR = 0x10c, + .WPMR = 0x118, + .WPSR = 0x11c, + .VERSION = 0x130, +}; + #define AT91_SAMA5D2_TOUCH_SAMPLE_PERIOD_US 2000 /* 2ms */ #define AT91_SAMA5D2_TOUCH_PEN_DETECT_DEBOUNCE_US 200 @@ -531,6 +563,34 @@ static const struct iio_chan_spec at91_sama5d2_adc_channels[] = { AT91_SAMA5D2_CHAN_PRESSURE(26, "pressure"), }; +static const struct iio_chan_spec at91_sama7g5_adc_channels[] = { + AT91_SAMA5D2_CHAN_SINGLE(0, 0, 0x60), + AT91_SAMA5D2_CHAN_SINGLE(1, 1, 0x64), + AT91_SAMA5D2_CHAN_SINGLE(2, 2, 0x68), + AT91_SAMA5D2_CHAN_SINGLE(3, 3, 0x6c), + AT91_SAMA5D2_CHAN_SINGLE(4, 4, 0x70), + AT91_SAMA5D2_CHAN_SINGLE(5, 5, 0x74), + AT91_SAMA5D2_CHAN_SINGLE(6, 6, 0x78), + AT91_SAMA5D2_CHAN_SINGLE(7, 7, 0x7c), + AT91_SAMA5D2_CHAN_SINGLE(8, 8, 0x80), + AT91_SAMA5D2_CHAN_SINGLE(9, 9, 0x84), + AT91_SAMA5D2_CHAN_SINGLE(10, 10, 0x88), + AT91_SAMA5D2_CHAN_SINGLE(11, 11, 0x8c), + AT91_SAMA5D2_CHAN_SINGLE(12, 12, 0x90), + AT91_SAMA5D2_CHAN_SINGLE(13, 13, 0x94), + AT91_SAMA5D2_CHAN_SINGLE(14, 14, 0x98), + AT91_SAMA5D2_CHAN_SINGLE(15, 15, 0x9c), + AT91_SAMA5D2_CHAN_DIFF(16, 0, 1, 0x60), + AT91_SAMA5D2_CHAN_DIFF(17, 2, 3, 0x68), + AT91_SAMA5D2_CHAN_DIFF(18, 4, 5, 0x70), + AT91_SAMA5D2_CHAN_DIFF(19, 6, 7, 0x78), + AT91_SAMA5D2_CHAN_DIFF(20, 8, 9, 0x80), + AT91_SAMA5D2_CHAN_DIFF(21, 10, 11, 0x88), + AT91_SAMA5D2_CHAN_DIFF(22, 12, 13, 0x90), + AT91_SAMA5D2_CHAN_DIFF(23, 14, 15, 0x98), + IIO_CHAN_SOFT_TIMESTAMP(24), +}; + static const struct at91_adc_platform sama5d2_platform = { .layout = &sama5d2_layout, .adc_channels = &at91_sama5d2_adc_channels, @@ -552,6 +612,21 @@ static const struct at91_adc_platform sama5d2_platform = { .hw_trig_cnt = AT91_SAMA5D2_HW_TRIG_CNT, }; +static const struct at91_adc_platform sama7g5_platform = { + .layout = &sama7g5_layout, + .adc_channels = &at91_sama7g5_adc_channels, +#define AT91_SAMA7G5_SINGLE_CHAN_CNT 16 +#define AT91_SAMA7G5_DIFF_CHAN_CNT 8 + .nr_channels = AT91_SAMA7G5_SINGLE_CHAN_CNT + + AT91_SAMA7G5_DIFF_CHAN_CNT, +#define AT91_SAMA7G5_MAX_CHAN_IDX (AT91_SAMA7G5_SINGLE_CHAN_CNT + \ + AT91_SAMA7G5_DIFF_CHAN_CNT) + .max_channels = ARRAY_SIZE(at91_sama7g5_adc_channels), + .max_index = AT91_SAMA7G5_MAX_CHAN_IDX, +#define AT91_SAMA7G5_HW_TRIG_CNT 3 + .hw_trig_cnt = AT91_SAMA7G5_HW_TRIG_CNT, +}; + static int at91_adc_chan_xlate(struct iio_dev *indio_dev, int chan) { int i; @@ -2099,6 +2174,9 @@ static const struct of_device_id at91_adc_dt_match[] = { { .compatible = "atmel,sama5d2-adc", .data = (const void *)&sama5d2_platform, + }, { + .compatible = "microchip,sama7g5-adc", + .data = (const void *)&sama7g5_platform, }, { /* sentinel */ } -- cgit v1.2.3-70-g09d2 From 874b4912d94ffe2d01dc0a8c8a3ebf2c05c3ac29 Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Wed, 1 Sep 2021 15:30:11 +0300 Subject: iio: adc: at91-sama5d2_adc: update copyright and authors information Update copyright and authors information (corrected e-mail address), and add myself as one of the authors. Signed-off-by: Eugen Hristev Link: https://lore.kernel.org/r/20210901123013.329792-9-eugen.hristev@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 832446296a34..dabe8cdcfd08 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -4,6 +4,8 @@ * * Copyright (C) 2015 Atmel, * 2015 Ludovic Desroches + * 2021 Microchip Technology, Inc. and its subsidiaries + * 2021 Eugen Hristev */ #include @@ -2194,6 +2196,7 @@ static struct platform_driver at91_adc_driver = { }; module_platform_driver(at91_adc_driver) -MODULE_AUTHOR("Ludovic Desroches "); +MODULE_AUTHOR("Ludovic Desroches "); +MODULE_AUTHOR("Eugen Hristev Date: Fri, 3 Sep 2021 10:37:07 +0300 Subject: iio: adc: ti-ads8344: convert probe to device-managed This change converts the driver to register via devm_iio_device_register(). The regulator disable is moved on a devm_add_action_or_reset() hook. And the spi_set_drvdata() isn't required anymore. And finally, the ads8344_remove() can be removed as well. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210903073707.46892-1-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads8344.c | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/drivers/iio/adc/ti-ads8344.c b/drivers/iio/adc/ti-ads8344.c index a345a30d74fa..c96d2a9ba924 100644 --- a/drivers/iio/adc/ti-ads8344.c +++ b/drivers/iio/adc/ti-ads8344.c @@ -133,6 +133,11 @@ static const struct iio_info ads8344_info = { .read_raw = ads8344_read_raw, }; +static void ads8344_reg_disable(void *data) +{ + regulator_disable(data); +} + static int ads8344_probe(struct spi_device *spi) { struct iio_dev *indio_dev; @@ -161,26 +166,11 @@ static int ads8344_probe(struct spi_device *spi) if (ret) return ret; - spi_set_drvdata(spi, indio_dev); - - ret = iio_device_register(indio_dev); - if (ret) { - regulator_disable(adc->reg); + ret = devm_add_action_or_reset(&spi->dev, ads8344_reg_disable, adc->reg); + if (ret) return ret; - } - - return 0; -} - -static int ads8344_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ads8344 *adc = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - regulator_disable(adc->reg); - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct of_device_id ads8344_of_match[] = { @@ -195,7 +185,6 @@ static struct spi_driver ads8344_driver = { .of_match_table = ads8344_of_match, }, .probe = ads8344_probe, - .remove = ads8344_remove, }; module_spi_driver(ads8344_driver); -- cgit v1.2.3-70-g09d2 From 2bdb2f00a895cacef579332937a6033bba54bd20 Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Tue, 31 Aug 2021 15:14:45 +0800 Subject: dt-bindings: iio: adc: Add ast2600-adc bindings Add device tree bindings document for the aspeed ast2600 adc device driver. Signed-off-by: Billy Tsai Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20210831071458.2334-3-billy_tsai@aspeedtech.com Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/aspeed,ast2600-adc.yaml | 100 +++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml b/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml new file mode 100644 index 000000000000..b283c8ca2bbf --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/aspeed,ast2600-adc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ADC that forms part of an ASPEED server management processor. + +maintainers: + - Billy Tsai + +description: | + • 10-bits resolution for 16 voltage channels. + • The device split into two individual engine and each contains 8 voltage + channels. + • Channel scanning can be non-continuous. + • Programmable ADC clock frequency. + • Programmable upper and lower threshold for each channels. + • Interrupt when larger or less than threshold for each channels. + • Support hysteresis for each channels. + • Built-in a compensating method. + • Built-in a register to trim internal reference voltage. + • Internal or External reference voltage. + • Support 2 Internal reference voltage 1.2v or 2.5v. + • Integrate dividing circuit for battery sensing. + +properties: + compatible: + enum: + - aspeed,ast2600-adc0 + - aspeed,ast2600-adc1 + description: + Their trimming data, which is used to calibrate internal reference volage, + locates in different address of OTP. + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + description: + Input clock used to derive the sample clock. Expected to be the + SoC's APB clock. + + resets: + maxItems: 1 + + "#io-channel-cells": + const: 1 + + vref-supply: + description: + The external regulator supply ADC reference voltage. + + aspeed,int-vref-microvolt: + enum: [1200000, 2500000] + description: + ADC internal reference voltage in microvolts. + + aspeed,battery-sensing: + type: boolean + description: + Inform the driver that last channel will be used to sensor battery. + + aspeed,trim-data-valid: + type: boolean + description: | + The ADC reference voltage can be calibrated to obtain the trimming + data which will be stored in otp. This property informs the driver that + the data store in the otp is valid. + +required: + - compatible + - reg + - clocks + - resets + - "#io-channel-cells" + +additionalProperties: false + +examples: + - | + #include + adc0: adc@1e6e9000 { + compatible = "aspeed,ast2600-adc0"; + reg = <0x1e6e9000 0x100>; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_ADC>; + #io-channel-cells = <1>; + aspeed,int-vref-microvolt = <2500000>; + }; + adc1: adc@1e6e9100 { + compatible = "aspeed,ast2600-adc1"; + reg = <0x1e6e9100 0x100>; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_ADC>; + #io-channel-cells = <1>; + aspeed,int-vref-microvolt = <2500000>; + }; +... -- cgit v1.2.3-70-g09d2 From 26a9f730ce38605809ff4ff4426249d5f8dd301d Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Tue, 31 Aug 2021 15:14:46 +0800 Subject: iio: adc: aspeed: completes the bitfield declare. This patch completes the declare of ADC register bitfields and uses the same prefix ASPEED_ADC_* for these bitfields. In addition, tidy up space alignment of the codes. Signed-off-by: Billy Tsai Link: https://lore.kernel.org/r/20210831071458.2334-4-billy_tsai@aspeedtech.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/aspeed_adc.c | 64 ++++++++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 17 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 19efaa41bc34..d893151efd2f 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -3,6 +3,7 @@ * Aspeed AST2400/2500 ADC * * Copyright (C) 2017 Google, Inc. + * Copyright (C) 2021 Aspeed Technology Inc. */ #include @@ -16,6 +17,7 @@ #include #include #include +#include #include #include @@ -28,15 +30,39 @@ #define ASPEED_REG_INTERRUPT_CONTROL 0x04 #define ASPEED_REG_VGA_DETECT_CONTROL 0x08 #define ASPEED_REG_CLOCK_CONTROL 0x0C -#define ASPEED_REG_MAX 0xC0 - -#define ASPEED_OPERATION_MODE_POWER_DOWN (0x0 << 1) -#define ASPEED_OPERATION_MODE_STANDBY (0x1 << 1) -#define ASPEED_OPERATION_MODE_NORMAL (0x7 << 1) - -#define ASPEED_ENGINE_ENABLE BIT(0) - -#define ASPEED_ADC_CTRL_INIT_RDY BIT(8) +#define ASPEED_REG_COMPENSATION_TRIM 0xC4 +/* + * The register offset between 0xC8~0xCC can be read and won't affect the + * hardware logic in each version of ADC. + */ +#define ASPEED_REG_MAX 0xD0 + +#define ASPEED_ADC_ENGINE_ENABLE BIT(0) +#define ASPEED_ADC_OP_MODE GENMASK(3, 1) +#define ASPEED_ADC_OP_MODE_PWR_DOWN 0 +#define ASPEED_ADC_OP_MODE_STANDBY 1 +#define ASPEED_ADC_OP_MODE_NORMAL 7 +#define ASPEED_ADC_CTRL_COMPENSATION BIT(4) +#define ASPEED_ADC_AUTO_COMPENSATION BIT(5) +/* + * Bit 6 determines not only the reference voltage range but also the dividing + * circuit for battery sensing. + */ +#define ASPEED_ADC_REF_VOLTAGE GENMASK(7, 6) +#define ASPEED_ADC_REF_VOLTAGE_2500mV 0 +#define ASPEED_ADC_REF_VOLTAGE_1200mV 1 +#define ASPEED_ADC_REF_VOLTAGE_EXT_HIGH 2 +#define ASPEED_ADC_REF_VOLTAGE_EXT_LOW 3 +#define ASPEED_ADC_BAT_SENSING_DIV BIT(6) +#define ASPEED_ADC_BAT_SENSING_DIV_2_3 0 +#define ASPEED_ADC_BAT_SENSING_DIV_1_3 1 +#define ASPEED_ADC_CTRL_INIT_RDY BIT(8) +#define ASPEED_ADC_CH7_MODE BIT(12) +#define ASPEED_ADC_CH7_NORMAL 0 +#define ASPEED_ADC_CH7_BAT 1 +#define ASPEED_ADC_BAT_SENSING_ENABLE BIT(13) +#define ASPEED_ADC_CTRL_CHANNEL GENMASK(31, 16) +#define ASPEED_ADC_CTRL_CHANNEL_ENABLE(ch) FIELD_PREP(ASPEED_ADC_CTRL_CHANNEL, BIT(ch)) #define ASPEED_ADC_INIT_POLLING_TIME 500 #define ASPEED_ADC_INIT_TIMEOUT 500000 @@ -226,7 +252,9 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (model_data->wait_init_sequence) { /* Enable engine in normal mode. */ - writel(ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE, + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, + ASPEED_ADC_OP_MODE_NORMAL) | + ASPEED_ADC_ENGINE_ENABLE, data->base + ASPEED_REG_ENGINE_CONTROL); /* Wait for initial sequence complete. */ @@ -245,10 +273,12 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (ret) goto clk_enable_error; - adc_engine_control_reg_val = GENMASK(31, 16) | - ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE; + adc_engine_control_reg_val = + ASPEED_ADC_CTRL_CHANNEL | + FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | + ASPEED_ADC_ENGINE_ENABLE; writel(adc_engine_control_reg_val, - data->base + ASPEED_REG_ENGINE_CONTROL); + data->base + ASPEED_REG_ENGINE_CONTROL); model_data = of_device_get_match_data(&pdev->dev); indio_dev->name = model_data->model_name; @@ -264,8 +294,8 @@ static int aspeed_adc_probe(struct platform_device *pdev) return 0; iio_register_error: - writel(ASPEED_OPERATION_MODE_POWER_DOWN, - data->base + ASPEED_REG_ENGINE_CONTROL); + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), + data->base + ASPEED_REG_ENGINE_CONTROL); clk_disable_unprepare(data->clk_scaler->clk); clk_enable_error: poll_timeout_error: @@ -283,8 +313,8 @@ static int aspeed_adc_remove(struct platform_device *pdev) struct aspeed_adc_data *data = iio_priv(indio_dev); iio_device_unregister(indio_dev); - writel(ASPEED_OPERATION_MODE_POWER_DOWN, - data->base + ASPEED_REG_ENGINE_CONTROL); + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), + data->base + ASPEED_REG_ENGINE_CONTROL); clk_disable_unprepare(data->clk_scaler->clk); reset_control_assert(data->rst); clk_hw_unregister_divider(data->clk_scaler); -- cgit v1.2.3-70-g09d2 From af1c6b50a2947cee3ffffb32aa8debb100c786bb Mon Sep 17 00:00:00 2001 From: Lucas Stankus Date: Wed, 1 Sep 2021 16:14:28 -0300 Subject: dt-bindings: iio: accel: Add binding documentation for ADXL313 Add device tree binding documentation for ADXL313 3-axis accelerometer. Signed-off-by: Lucas Stankus Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/2eff22d1d22f7e72efdabdc681d02e922682c434.1630523106.git.lucas.p.stankus@gmail.com Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/accel/adi,adxl313.yaml | 86 ++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml new file mode 100644 index 000000000000..d6afc1b8c272 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml @@ -0,0 +1,86 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/accel/adi,adxl313.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices ADXL313 3-Axis Digital Accelerometer + +maintainers: + - Lucas Stankus + +description: | + Analog Devices ADXL313 3-Axis Digital Accelerometer that supports + both I2C & SPI interfaces. + https://www.analog.com/en/products/adxl313.html + +properties: + compatible: + enum: + - adi,adxl313 + + reg: + maxItems: 1 + + spi-3wire: true + + spi-max-frequency: true + + vs-supply: + description: Regulator that supplies power to the accelerometer + + vdd-supply: + description: Regulator that supplies the digital interface supply voltage + + interrupts: + minItems: 1 + maxItems: 2 + + interrupt-names: + minItems: 1 + maxItems: 2 + items: + enum: + - INT1 + - INT2 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + #include + i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + /* Example for a I2C device node */ + accelerometer@53 { + compatible = "adi,adxl313"; + reg = <0x53>; + interrupt-parent = <&gpio0>; + interrupts = <0 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "INT1"; + }; + }; + - | + #include + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + + /* Example for a SPI device node */ + accelerometer@0 { + compatible = "adi,adxl313"; + reg = <0>; + spi-max-frequency = <5000000>; + interrupt-parent = <&gpio0>; + interrupts = <0 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "INT1"; + }; + }; -- cgit v1.2.3-70-g09d2 From 636d44633039348c955947cee561f372846b478b Mon Sep 17 00:00:00 2001 From: Lucas Stankus Date: Wed, 1 Sep 2021 16:14:54 -0300 Subject: iio: accel: Add driver support for ADXL313 ADXL313 is a small, thin, low power, 3-axis accelerometer with high resolution measurement up to +/-4g. It includes an integrated 32-level FIFO and has activity and inactivity sensing capabilities. Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL313.pdf Signed-off-by: Lucas Stankus Reviewed-by: Alexandru Ardelean Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/d16e2d1967e46bb2b1024b6d23bc4889da77dc6b.1630523106.git.lucas.p.stankus@gmail.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 6 + drivers/iio/accel/Kconfig | 29 ++++ drivers/iio/accel/Makefile | 3 + drivers/iio/accel/adxl313.h | 54 +++++++ drivers/iio/accel/adxl313_core.c | 332 +++++++++++++++++++++++++++++++++++++++ drivers/iio/accel/adxl313_i2c.c | 66 ++++++++ drivers/iio/accel/adxl313_spi.c | 92 +++++++++++ 7 files changed, 582 insertions(+) create mode 100644 drivers/iio/accel/adxl313.h create mode 100644 drivers/iio/accel/adxl313_core.c create mode 100644 drivers/iio/accel/adxl313_i2c.c create mode 100644 drivers/iio/accel/adxl313_spi.c diff --git a/MAINTAINERS b/MAINTAINERS index e7e2626b83dd..41b969921d0a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -590,6 +590,12 @@ L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/adv_swbutton.c +ADXL313 THREE-AXIS DIGITAL ACCELEROMETER DRIVER +M: Lucas Stankus +S: Supported +F: Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml +F: drivers/iio/accel/adxl313* + ADXL34X THREE-AXIS DIGITAL ACCELEROMETER DRIVER (ADXL345/ADXL346) M: Michael Hennerich S: Supported diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 05a3504119a8..f1bc18f19342 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -30,6 +30,35 @@ config ADIS16209 To compile this driver as a module, say M here: the module will be called adis16209. +config ADXL313 + tristate + +config ADXL313_I2C + tristate "Analog Devices ADXL313 3-Axis Digital Accelerometer I2C Driver" + depends on I2C + select ADXL313 + select REGMAP_I2C + help + Say Y here if you want to build i2c support for the Analog Devices + ADXL313 3-axis digital accelerometer. + + To compile this driver as a module, choose M here: the module + will be called adxl313_i2c and you will also get adxl313_core + for the core module. + +config ADXL313_SPI + tristate "Analog Devices ADXL313 3-Axis Digital Accelerometer SPI Driver" + depends on SPI + select ADXL313 + select REGMAP_SPI + help + Say Y here if you want to build spi support for the Analog Devices + ADXL313 3-axis digital accelerometer. + + To compile this driver as a module, choose M here: the module + will be called adxl313_spi and you will also get adxl313_core + for the core module. + config ADXL345 tristate diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 7f4d97bf41f9..d03e2f6bba08 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -6,6 +6,9 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_ADIS16201) += adis16201.o obj-$(CONFIG_ADIS16209) += adis16209.o +obj-$(CONFIG_ADXL313) += adxl313_core.o +obj-$(CONFIG_ADXL313_I2C) += adxl313_i2c.o +obj-$(CONFIG_ADXL313_SPI) += adxl313_spi.o obj-$(CONFIG_ADXL345) += adxl345_core.o obj-$(CONFIG_ADXL345_I2C) += adxl345_i2c.o obj-$(CONFIG_ADXL345_SPI) += adxl345_spi.o diff --git a/drivers/iio/accel/adxl313.h b/drivers/iio/accel/adxl313.h new file mode 100644 index 000000000000..4415f2fc07e1 --- /dev/null +++ b/drivers/iio/accel/adxl313.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ADXL313 3-Axis Digital Accelerometer + * + * Copyright (c) 2021 Lucas Stankus + */ + +#ifndef _ADXL313_H_ +#define _ADXL313_H_ + +/* ADXL313 register definitions */ +#define ADXL313_REG_DEVID0 0x00 +#define ADXL313_REG_DEVID1 0x01 +#define ADXL313_REG_PARTID 0x02 +#define ADXL313_REG_XID 0x04 +#define ADXL313_REG_SOFT_RESET 0x18 +#define ADXL313_REG_OFS_AXIS(index) (0x1E + (index)) +#define ADXL313_REG_THRESH_ACT 0x24 +#define ADXL313_REG_ACT_INACT_CTL 0x27 +#define ADXL313_REG_BW_RATE 0x2C +#define ADXL313_REG_POWER_CTL 0x2D +#define ADXL313_REG_INT_MAP 0x2F +#define ADXL313_REG_DATA_FORMAT 0x31 +#define ADXL313_REG_DATA_AXIS(index) (0x32 + ((index) * 2)) +#define ADXL313_REG_FIFO_CTL 0x38 +#define ADXL313_REG_FIFO_STATUS 0x39 + +#define ADXL313_DEVID0 0xAD +#define ADXL313_DEVID1 0x1D +#define ADXL313_PARTID 0xCB +#define ADXL313_SOFT_RESET 0x52 + +#define ADXL313_RATE_MSK GENMASK(3, 0) +#define ADXL313_RATE_BASE 6 + +#define ADXL313_POWER_CTL_MSK GENMASK(3, 2) +#define ADXL313_MEASUREMENT_MODE BIT(3) + +#define ADXL313_RANGE_MSK GENMASK(1, 0) +#define ADXL313_RANGE_4G 3 + +#define ADXL313_FULL_RES BIT(3) +#define ADXL313_SPI_3WIRE BIT(6) +#define ADXL313_I2C_DISABLE BIT(6) + +extern const struct regmap_access_table adxl313_readable_regs_table; + +extern const struct regmap_access_table adxl313_writable_regs_table; + +int adxl313_core_probe(struct device *dev, + struct regmap *regmap, + const char *name, + int (*setup)(struct device *, struct regmap *)); +#endif /* _ADXL313_H_ */ diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c new file mode 100644 index 000000000000..0d243341f1a7 --- /dev/null +++ b/drivers/iio/accel/adxl313_core.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADXL313 3-Axis Digital Accelerometer + * + * Copyright (c) 2021 Lucas Stankus + * + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL313.pdf + */ + +#include +#include +#include +#include + +#include "adxl313.h" + +static const struct regmap_range adxl313_readable_reg_range[] = { + regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_XID), + regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET), + regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)), + regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL), + regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_FIFO_STATUS), +}; + +const struct regmap_access_table adxl313_readable_regs_table = { + .yes_ranges = adxl313_readable_reg_range, + .n_yes_ranges = ARRAY_SIZE(adxl313_readable_reg_range), +}; +EXPORT_SYMBOL_GPL(adxl313_readable_regs_table); + +static const struct regmap_range adxl313_writable_reg_range[] = { + regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET), + regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)), + regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL), + regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_INT_MAP), + regmap_reg_range(ADXL313_REG_DATA_FORMAT, ADXL313_REG_DATA_FORMAT), + regmap_reg_range(ADXL313_REG_FIFO_CTL, ADXL313_REG_FIFO_CTL), +}; + +const struct regmap_access_table adxl313_writable_regs_table = { + .yes_ranges = adxl313_writable_reg_range, + .n_yes_ranges = ARRAY_SIZE(adxl313_writable_reg_range), +}; +EXPORT_SYMBOL_GPL(adxl313_writable_regs_table); + +struct adxl313_data { + struct regmap *regmap; + struct mutex lock; /* lock to protect transf_buf */ + __le16 transf_buf ____cacheline_aligned; +}; + +static const int adxl313_odr_freqs[][2] = { + [0] = { 6, 250000 }, + [1] = { 12, 500000 }, + [2] = { 25, 0 }, + [3] = { 50, 0 }, + [4] = { 100, 0 }, + [5] = { 200, 0 }, + [6] = { 400, 0 }, + [7] = { 800, 0 }, + [8] = { 1600, 0 }, + [9] = { 3200, 0 }, +}; + +#define ADXL313_ACCEL_CHANNEL(index, axis) { \ + .type = IIO_ACCEL, \ + .address = index, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_type = { \ + .realbits = 13, \ + }, \ +} + +static const struct iio_chan_spec adxl313_channels[] = { + ADXL313_ACCEL_CHANNEL(0, X), + ADXL313_ACCEL_CHANNEL(1, Y), + ADXL313_ACCEL_CHANNEL(2, Z), +}; + +static int adxl313_set_odr(struct adxl313_data *data, + unsigned int freq1, unsigned int freq2) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(adxl313_odr_freqs); i++) { + if (adxl313_odr_freqs[i][0] == freq1 && + adxl313_odr_freqs[i][1] == freq2) + break; + } + + if (i == ARRAY_SIZE(adxl313_odr_freqs)) + return -EINVAL; + + return regmap_update_bits(data->regmap, ADXL313_REG_BW_RATE, + ADXL313_RATE_MSK, + FIELD_PREP(ADXL313_RATE_MSK, ADXL313_RATE_BASE + i)); +} + +static int adxl313_read_axis(struct adxl313_data *data, + struct iio_chan_spec const *chan) +{ + int ret; + + mutex_lock(&data->lock); + + ret = regmap_bulk_read(data->regmap, + ADXL313_REG_DATA_AXIS(chan->address), + &data->transf_buf, sizeof(data->transf_buf)); + if (ret) + goto unlock_ret; + + ret = le16_to_cpu(data->transf_buf); + +unlock_ret: + mutex_unlock(&data->lock); + return ret; +} + +static int adxl313_read_freq_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (const int *)adxl313_odr_freqs; + *length = ARRAY_SIZE(adxl313_odr_freqs) * 2; + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int adxl313_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct adxl313_data *data = iio_priv(indio_dev); + unsigned int regval; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = adxl313_read_axis(data, chan); + if (ret < 0) + return ret; + + *val = sign_extend32(ret, chan->scan_type.realbits - 1); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* + * Scale for any g range is given in datasheet as + * 1024 LSB/g = 0.0009765625 * 9.80665 = 0.009576806640625 m/s^2 + */ + *val = 0; + *val2 = 9576806; + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_CALIBBIAS: + ret = regmap_read(data->regmap, + ADXL313_REG_OFS_AXIS(chan->address), ®val); + if (ret) + return ret; + + /* + * 8-bit resolution at +/- 0.5g, that is 4x accel data scale + * factor at full resolution + */ + *val = sign_extend32(regval, 7) * 4; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = regmap_read(data->regmap, ADXL313_REG_BW_RATE, ®val); + if (ret) + return ret; + + ret = FIELD_GET(ADXL313_RATE_MSK, regval) - ADXL313_RATE_BASE; + *val = adxl313_odr_freqs[ret][0]; + *val2 = adxl313_odr_freqs[ret][1]; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int adxl313_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct adxl313_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_CALIBBIAS: + /* + * 8-bit resolution at +/- 0.5g, that is 4x accel data scale + * factor at full resolution + */ + if (clamp_val(val, -128 * 4, 127 * 4) != val) + return -EINVAL; + + return regmap_write(data->regmap, + ADXL313_REG_OFS_AXIS(chan->address), + val / 4); + case IIO_CHAN_INFO_SAMP_FREQ: + return adxl313_set_odr(data, val, val2); + default: + return -EINVAL; + } +} + +static const struct iio_info adxl313_info = { + .read_raw = adxl313_read_raw, + .write_raw = adxl313_write_raw, + .read_avail = adxl313_read_freq_avail, +}; + +static int adxl313_setup(struct device *dev, struct adxl313_data *data, + int (*setup)(struct device *, struct regmap *)) +{ + unsigned int regval; + int ret; + + /* Ensures the device is in a consistent state after start up */ + ret = regmap_write(data->regmap, ADXL313_REG_SOFT_RESET, + ADXL313_SOFT_RESET); + if (ret) + return ret; + + if (setup) { + ret = setup(dev, data->regmap); + if (ret) + return ret; + } + + ret = regmap_read(data->regmap, ADXL313_REG_DEVID0, ®val); + if (ret) + return ret; + + if (regval != ADXL313_DEVID0) { + dev_err(dev, "Invalid manufacturer ID: 0x%02x\n", regval); + return -ENODEV; + } + + ret = regmap_read(data->regmap, ADXL313_REG_DEVID1, ®val); + if (ret) + return ret; + + if (regval != ADXL313_DEVID1) { + dev_err(dev, "Invalid mems ID: 0x%02x\n", regval); + return -ENODEV; + } + + ret = regmap_read(data->regmap, ADXL313_REG_PARTID, ®val); + if (ret) + return ret; + + if (regval != ADXL313_PARTID) { + dev_err(dev, "Invalid device ID: 0x%02x\n", regval); + return -ENODEV; + } + + /* Sets the range to +/- 4g */ + ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT, + ADXL313_RANGE_MSK, + FIELD_PREP(ADXL313_RANGE_MSK, ADXL313_RANGE_4G)); + if (ret) + return ret; + + /* Enables full resolution */ + ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT, + ADXL313_FULL_RES, ADXL313_FULL_RES); + if (ret) + return ret; + + /* Enables measurement mode */ + return regmap_update_bits(data->regmap, ADXL313_REG_POWER_CTL, + ADXL313_POWER_CTL_MSK, + ADXL313_MEASUREMENT_MODE); +} + +/** + * adxl313_core_probe() - probe and setup for adxl313 accelerometer + * @dev: Driver model representation of the device + * @regmap: Register map of the device + * @name: Device name buffer reference + * @setup: Setup routine to be executed right before the standard device + * setup, can also be set to NULL if not required + * + * Return: 0 on success, negative errno on error cases + */ +int adxl313_core_probe(struct device *dev, + struct regmap *regmap, + const char *name, + int (*setup)(struct device *, struct regmap *)) +{ + struct adxl313_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->regmap = regmap; + mutex_init(&data->lock); + + indio_dev->name = name; + indio_dev->info = &adxl313_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = adxl313_channels; + indio_dev->num_channels = ARRAY_SIZE(adxl313_channels); + + ret = adxl313_setup(dev, data, setup); + if (ret) { + dev_err(dev, "ADXL313 setup failed\n"); + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_GPL(adxl313_core_probe); + +MODULE_AUTHOR("Lucas Stankus "); +MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer core driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/adxl313_i2c.c b/drivers/iio/accel/adxl313_i2c.c new file mode 100644 index 000000000000..82e9fb2db1e6 --- /dev/null +++ b/drivers/iio/accel/adxl313_i2c.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADXL313 3-Axis Digital Accelerometer + * + * Copyright (c) 2021 Lucas Stankus + * + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL313.pdf + */ + +#include +#include +#include +#include + +#include "adxl313.h" + +static const struct regmap_config adxl313_i2c_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .rd_table = &adxl313_readable_regs_table, + .wr_table = &adxl313_writable_regs_table, + .max_register = 0x39, +}; + +static int adxl313_i2c_probe(struct i2c_client *client) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, &adxl313_i2c_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "Error initializing i2c regmap: %ld\n", + PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + return adxl313_core_probe(&client->dev, regmap, client->name, NULL); +} + +static const struct i2c_device_id adxl313_i2c_id[] = { + { "adxl313" }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, adxl313_i2c_id); + +static const struct of_device_id adxl313_of_match[] = { + { .compatible = "adi,adxl313" }, + { } +}; + +MODULE_DEVICE_TABLE(of, adxl313_of_match); + +static struct i2c_driver adxl313_i2c_driver = { + .driver = { + .name = "adxl313_i2c", + .of_match_table = adxl313_of_match, + }, + .probe_new = adxl313_i2c_probe, + .id_table = adxl313_i2c_id, +}; + +module_i2c_driver(adxl313_i2c_driver); + +MODULE_AUTHOR("Lucas Stankus "); +MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer I2C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/adxl313_spi.c b/drivers/iio/accel/adxl313_spi.c new file mode 100644 index 000000000000..a6162f36ef52 --- /dev/null +++ b/drivers/iio/accel/adxl313_spi.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADXL313 3-Axis Digital Accelerometer + * + * Copyright (c) 2021 Lucas Stankus + * + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL313.pdf + */ + +#include +#include +#include +#include + +#include "adxl313.h" + +static const struct regmap_config adxl313_spi_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .rd_table = &adxl313_readable_regs_table, + .wr_table = &adxl313_writable_regs_table, + .max_register = 0x39, + /* Setting bits 7 and 6 enables multiple-byte read */ + .read_flag_mask = BIT(7) | BIT(6), +}; + +static int adxl313_spi_setup(struct device *dev, struct regmap *regmap) +{ + struct spi_device *spi = container_of(dev, struct spi_device, dev); + int ret; + + if (spi->mode & SPI_3WIRE) { + ret = regmap_write(regmap, ADXL313_REG_DATA_FORMAT, + ADXL313_SPI_3WIRE); + if (ret) + return ret; + } + + return regmap_update_bits(regmap, ADXL313_REG_POWER_CTL, + ADXL313_I2C_DISABLE, ADXL313_I2C_DISABLE); +} + +static int adxl313_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct regmap *regmap; + int ret; + + spi->mode |= SPI_MODE_3; + ret = spi_setup(spi); + if (ret) + return ret; + + regmap = devm_regmap_init_spi(spi, &adxl313_spi_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "Error initializing spi regmap: %ld\n", + PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + return adxl313_core_probe(&spi->dev, regmap, id->name, + &adxl313_spi_setup); +} + +static const struct spi_device_id adxl313_spi_id[] = { + { "adxl313" }, + { } +}; + +MODULE_DEVICE_TABLE(spi, adxl313_spi_id); + +static const struct of_device_id adxl313_of_match[] = { + { .compatible = "adi,adxl313" }, + { } +}; + +MODULE_DEVICE_TABLE(of, adxl313_of_match); + +static struct spi_driver adxl313_spi_driver = { + .driver = { + .name = "adxl313_spi", + .of_match_table = adxl313_of_match, + }, + .probe = adxl313_spi_probe, + .id_table = adxl313_spi_id, +}; + +module_spi_driver(adxl313_spi_driver); + +MODULE_AUTHOR("Lucas Stankus "); +MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer SPI driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-70-g09d2 From 86ff6cb15f46310a8f88f36f4c5e3b6a9ca32efd Mon Sep 17 00:00:00 2001 From: Puranjay Mohan Date: Sat, 4 Sep 2021 00:13:11 +0530 Subject: iio: accel: adxl355: use if(ret) in place of ret < 0 Replace if(ret < 0) with if(ret) for consistency. Signed-off-by: Puranjay Mohan Link: https://lore.kernel.org/r/20210903184312.21009-2-puranjay12@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl355_core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c index f71f64b32a01..d3ed7eec9485 100644 --- a/drivers/iio/accel/adxl355_core.c +++ b/drivers/iio/accel/adxl355_core.c @@ -262,7 +262,7 @@ static int adxl355_read_axis(struct adxl355_data *data, u8 addr) ret = regmap_bulk_read(data->regmap, addr, data->transf_buf, ARRAY_SIZE(data->transf_buf)); - if (ret < 0) + if (ret) return ret; return get_unaligned_be24(data->transf_buf); @@ -294,13 +294,13 @@ static int adxl355_set_odr(struct adxl355_data *data, } ret = adxl355_set_op_mode(data, ADXL355_STANDBY); - if (ret < 0) + if (ret) goto err_unlock; ret = regmap_update_bits(data->regmap, ADXL355_FILTER_REG, ADXL355_FILTER_ODR_MSK, FIELD_PREP(ADXL355_FILTER_ODR_MSK, odr)); - if (ret < 0) + if (ret) goto err_set_opmode; data->odr = odr; @@ -333,7 +333,7 @@ static int adxl355_set_hpf_3db(struct adxl355_data *data, } ret = adxl355_set_op_mode(data, ADXL355_STANDBY); - if (ret < 0) + if (ret) goto err_unlock; ret = regmap_update_bits(data->regmap, ADXL355_FILTER_REG, @@ -366,7 +366,7 @@ static int adxl355_set_calibbias(struct adxl355_data *data, mutex_lock(&data->lock); ret = adxl355_set_op_mode(data, ADXL355_STANDBY); - if (ret < 0) + if (ret) goto err_unlock; put_unaligned_be16(calibbias, data->transf_buf); @@ -598,7 +598,7 @@ int adxl355_core_probe(struct device *dev, struct regmap *regmap, indio_dev->num_channels = ARRAY_SIZE(adxl355_channels); ret = adxl355_setup(data); - if (ret < 0) { + if (ret) { dev_err(dev, "ADXL355 setup failed\n"); return ret; } -- cgit v1.2.3-70-g09d2 From 327a0eaf19d53efc77f7073a43b2b0712bc6364d Mon Sep 17 00:00:00 2001 From: Puranjay Mohan Date: Sat, 4 Sep 2021 00:13:12 +0530 Subject: iio: accel: adxl355: Add triggered buffer support Provide a way for continuous data capture by setting up buffer support. The data ready signal exposed at the DRDY pin of the ADXL355 is exploited as a hardware interrupt which triggers to fill the buffer. Signed-off-by: Puranjay Mohan Link: https://lore.kernel.org/r/20210903184312.21009-3-puranjay12@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/Kconfig | 4 + drivers/iio/accel/adxl355_core.c | 155 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 158 insertions(+), 1 deletion(-) diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index f1bc18f19342..49587c992a6d 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -98,6 +98,8 @@ config ADXL355_I2C depends on I2C select ADXL355 select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER help Say Y here if you want to build i2c support for the Analog Devices ADXL355 3-axis digital accelerometer. @@ -111,6 +113,8 @@ config ADXL355_SPI depends on SPI select ADXL355 select REGMAP_SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER help Say Y here if you want to build spi support for the Analog Devices ADXL355 3-axis digital accelerometer. diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c index d3ed7eec9485..4f485909f459 100644 --- a/drivers/iio/accel/adxl355_core.c +++ b/drivers/iio/accel/adxl355_core.c @@ -9,11 +9,16 @@ #include #include +#include #include +#include +#include +#include #include #include #include #include +#include #include #include @@ -46,6 +51,7 @@ #define ADXL355_RANGE_REG 0x2C #define ADXL355_POWER_CTL_REG 0x2D #define ADXL355_POWER_CTL_MODE_MSK GENMASK(1, 0) +#define ADXL355_POWER_CTL_DRDY_MSK BIT(2) #define ADXL355_SELF_TEST_REG 0x2E #define ADXL355_RESET_REG 0x2F @@ -165,7 +171,14 @@ struct adxl355_data { enum adxl355_hpf_3db hpf_3db; int calibbias[3]; int adxl355_hpf_3db_table[7][2]; - u8 transf_buf[3] ____cacheline_aligned; + struct iio_trigger *dready_trig; + union { + u8 transf_buf[3]; + struct { + u8 buf[14]; + s64 ts; + } buffer; + } ____cacheline_aligned; }; static int adxl355_set_op_mode(struct adxl355_data *data, @@ -186,6 +199,23 @@ static int adxl355_set_op_mode(struct adxl355_data *data, return ret; } +static int adxl355_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct adxl355_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->lock); + ret = regmap_update_bits(data->regmap, ADXL355_POWER_CTL_REG, + ADXL355_POWER_CTL_DRDY_MSK, + FIELD_PREP(ADXL355_POWER_CTL_DRDY_MSK, + state ? 0 : 1)); + mutex_unlock(&data->lock); + + return ret; +} + static void adxl355_fill_3db_frequency_table(struct adxl355_data *data) { u32 multiplier; @@ -246,6 +276,12 @@ static int adxl355_setup(struct adxl355_data *data) if (ret) return ret; + ret = regmap_update_bits(data->regmap, ADXL355_POWER_CTL_REG, + ADXL355_POWER_CTL_DRDY_MSK, + FIELD_PREP(ADXL355_POWER_CTL_DRDY_MSK, 1)); + if (ret) + return ret; + adxl355_fill_3db_frequency_table(data); return adxl355_set_op_mode(data, ADXL355_MEASUREMENT); @@ -527,12 +563,74 @@ static int adxl355_read_avail(struct iio_dev *indio_dev, } } +static const unsigned long adxl355_avail_scan_masks[] = { + GENMASK(3, 0), + 0 +}; + static const struct iio_info adxl355_info = { .read_raw = adxl355_read_raw, .write_raw = adxl355_write_raw, .read_avail = &adxl355_read_avail, }; +static const struct iio_trigger_ops adxl355_trigger_ops = { + .set_trigger_state = &adxl355_data_rdy_trigger_set_state, + .validate_device = &iio_trigger_validate_own_device, +}; + +static irqreturn_t adxl355_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct adxl355_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->lock); + + /* + * data->buffer is used both for triggered buffer support + * and read/write_raw(), hence, it has to be zeroed here before usage. + */ + data->buffer.buf[0] = 0; + + /* + * The acceleration data is 24 bits and big endian. It has to be saved + * in 32 bits, hence, it is saved in the 2nd byte of the 4 byte buffer. + * The buf array is 14 bytes as it includes 3x4=12 bytes for + * accelaration data of x, y, and z axis. It also includes 2 bytes for + * temperature data. + */ + ret = regmap_bulk_read(data->regmap, ADXL355_XDATA3_REG, + &data->buffer.buf[1], 3); + if (ret) + goto out_unlock_notify; + + ret = regmap_bulk_read(data->regmap, ADXL355_YDATA3_REG, + &data->buffer.buf[5], 3); + if (ret) + goto out_unlock_notify; + + ret = regmap_bulk_read(data->regmap, ADXL355_ZDATA3_REG, + &data->buffer.buf[9], 3); + if (ret) + goto out_unlock_notify; + + ret = regmap_bulk_read(data->regmap, ADXL355_TEMP2_REG, + &data->buffer.buf[12], 2); + if (ret) + goto out_unlock_notify; + + iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer, + pf->timestamp); + +out_unlock_notify: + mutex_unlock(&data->lock); + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + #define ADXL355_ACCEL_CHANNEL(index, reg, axis) { \ .type = IIO_ACCEL, \ .address = reg, \ @@ -546,6 +644,7 @@ static const struct iio_info adxl355_info = { .info_mask_shared_by_type_available = \ BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \ + .scan_index = index, \ .scan_type = { \ .sign = 's', \ .realbits = 20, \ @@ -565,6 +664,7 @@ static const struct iio_chan_spec adxl355_channels[] = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), + .scan_index = 3, .scan_type = { .sign = 's', .realbits = 12, @@ -572,14 +672,48 @@ static const struct iio_chan_spec adxl355_channels[] = { .endianness = IIO_BE, }, }, + IIO_CHAN_SOFT_TIMESTAMP(4), }; +static int adxl355_probe_trigger(struct iio_dev *indio_dev, int irq) +{ + struct adxl355_data *data = iio_priv(indio_dev); + int ret; + + data->dready_trig = devm_iio_trigger_alloc(data->dev, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!data->dready_trig) + return -ENOMEM; + + data->dready_trig->ops = &adxl355_trigger_ops; + iio_trigger_set_drvdata(data->dready_trig, indio_dev); + + ret = devm_request_irq(data->dev, irq, + &iio_trigger_generic_data_rdy_poll, + IRQF_ONESHOT, "adxl355_irq", data->dready_trig); + if (ret) + return dev_err_probe(data->dev, ret, "request irq %d failed\n", + irq); + + ret = devm_iio_trigger_register(data->dev, data->dready_trig); + if (ret) { + dev_err(data->dev, "iio trigger register failed\n"); + return ret; + } + + indio_dev->trig = iio_trigger_get(data->dready_trig); + + return 0; +} + int adxl355_core_probe(struct device *dev, struct regmap *regmap, const char *name) { struct adxl355_data *data; struct iio_dev *indio_dev; int ret; + int irq; indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) @@ -596,6 +730,7 @@ int adxl355_core_probe(struct device *dev, struct regmap *regmap, indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = adxl355_channels; indio_dev->num_channels = ARRAY_SIZE(adxl355_channels); + indio_dev->available_scan_masks = adxl355_avail_scan_masks; ret = adxl355_setup(data); if (ret) { @@ -603,6 +738,24 @@ int adxl355_core_probe(struct device *dev, struct regmap *regmap, return ret; } + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + &iio_pollfunc_store_time, + &adxl355_trigger_handler, NULL); + if (ret) { + dev_err(dev, "iio triggered buffer setup failed\n"); + return ret; + } + + /* + * TODO: Would be good to move it to the generic version. + */ + irq = of_irq_get_byname(dev->of_node, "DRDY"); + if (irq > 0) { + ret = adxl355_probe_trigger(indio_dev, irq); + if (ret) + return ret; + } + return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL_GPL(adxl355_core_probe); -- cgit v1.2.3-70-g09d2 From 5e87622c4bf3614155781daf5439cf4909adae9a Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 18 Sep 2021 07:20:28 +0200 Subject: misc: genwqe: Remove usage of the deprecated "pci-dma-compat.h" API In [1], Christoph Hellwig has proposed to remove the wrappers in include/linux/pci-dma-compat.h. Some reasons why this API should be removed have been given by Julia Lawall in [2]. Finally, Arnd Bergmann reminded that the documentation was updated 11 years ago to only describe the modern linux/dma-mapping.h interfaces and mark the old bus-specific ones as no longer recommended, see commit 216bf58f4092 ("Documentation: convert PCI-DMA-mapping.txt to use the generic DMA API"). A coccinelle script has been used to perform the needed transformation Only relevant parts are given below. @@ @@ - PCI_DMA_BIDIRECTIONAL + DMA_BIDIRECTIONAL@@ @@ expression e1, e2, e3, e4, e5; @@ - pci_map_page(e1, e2, e3, e4, e5) + dma_map_page(&e1->dev, e2, e3, e4, e5) @@ expression e1, e2, e3, e4; @@ - pci_unmap_page(e1, e2, e3, e4) + dma_unmap_page(&e1->dev, e2, e3, e4) @@ expression e1, e2; @@ - pci_dma_mapping_error(e1, e2) + dma_mapping_error(&e1->dev, e2) [1]: https://lore.kernel.org/kernel-janitors/20200421081257.GA131897@infradead.org/ [2]: https://lore.kernel.org/kernel-janitors/alpine.DEB.2.22.394.2007120902170.2424@hadrien/ Reviewed-by: Arnd Bergmann Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/590154f2ab113088346ae76c3f13f8b1cbebccbb.1631942274.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman --- drivers/misc/genwqe/card_utils.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c index 039b923d1d60..1167463f26fb 100644 --- a/drivers/misc/genwqe/card_utils.c +++ b/drivers/misc/genwqe/card_utils.c @@ -233,8 +233,8 @@ static void genwqe_unmap_pages(struct genwqe_dev *cd, dma_addr_t *dma_list, struct pci_dev *pci_dev = cd->pci_dev; for (i = 0; (i < num_pages) && (dma_list[i] != 0x0); i++) { - pci_unmap_page(pci_dev, dma_list[i], - PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); + dma_unmap_page(&pci_dev->dev, dma_list[i], PAGE_SIZE, + DMA_BIDIRECTIONAL); dma_list[i] = 0x0; } } @@ -251,12 +251,12 @@ static int genwqe_map_pages(struct genwqe_dev *cd, dma_addr_t daddr; dma_list[i] = 0x0; - daddr = pci_map_page(pci_dev, page_list[i], + daddr = dma_map_page(&pci_dev->dev, page_list[i], 0, /* map_offs */ PAGE_SIZE, - PCI_DMA_BIDIRECTIONAL); /* FIXME rd/rw */ + DMA_BIDIRECTIONAL); /* FIXME rd/rw */ - if (pci_dma_mapping_error(pci_dev, daddr)) { + if (dma_mapping_error(&pci_dev->dev, daddr)) { dev_err(&pci_dev->dev, "[%s] err: no dma addr daddr=%016llx!\n", __func__, (long long)daddr); -- cgit v1.2.3-70-g09d2 From 639fd77e2f696269b3a5c7a1eb547e044e4dbc2b Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 18 Sep 2021 07:32:26 +0200 Subject: tifm: Remove usage of the deprecated "pci-dma-compat.h" API In [1], Christoph Hellwig has proposed to remove the wrappers in include/linux/pci-dma-compat.h. Some reasons why this API should be removed have been given by Julia Lawall in [2]. Finally, Arnd Bergmann reminded that the documentation was updated 11 years ago to only describe the modern linux/dma-mapping.h interfaces and mark the old bus-specific ones as no longer recommended, see commit 216bf58f4092 ("Documentation: convert PCI-DMA-mapping.txt to use the generic DMA API"). A coccinelle script has been used to perform the needed transformation Only relevant parts are given below. @@ expression e1, e2, e3, e4; @@ - pci_map_sg(e1, e2, e3, e4) + dma_map_sg(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_unmap_sg(e1, e2, e3, e4) + dma_unmap_sg(&e1->dev, e2, e3, e4) @@ expression e1, e2; @@ - pci_set_dma_mask(e1, e2) + dma_set_mask(&e1->dev, e2) [1]: https://lore.kernel.org/kernel-janitors/20200421081257.GA131897@infradead.org/ [2]: https://lore.kernel.org/kernel-janitors/alpine.DEB.2.22.394.2007120902170.2424@hadrien/ Reviewed-by: Arnd Bergmann Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/b5296677f92f7bace957e66479b3d57a5a824ca1.1631942796.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman --- drivers/misc/tifm_7xx1.c | 2 +- drivers/misc/tifm_core.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index 228f2eb1d476..017c2f7d6287 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -311,7 +311,7 @@ static int tifm_7xx1_probe(struct pci_dev *dev, int pci_dev_busy = 0; int rc; - rc = pci_set_dma_mask(dev, DMA_BIT_MASK(32)); + rc = dma_set_mask(&dev->dev, DMA_BIT_MASK(32)); if (rc) return rc; diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c index 2bb46b1f4be3..a3098fea3bf7 100644 --- a/drivers/misc/tifm_core.c +++ b/drivers/misc/tifm_core.c @@ -292,14 +292,15 @@ EXPORT_SYMBOL(tifm_has_ms_pif); int tifm_map_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents, int direction) { - return pci_map_sg(to_pci_dev(sock->dev.parent), sg, nents, direction); + return dma_map_sg(&to_pci_dev(sock->dev.parent)->dev, sg, nents, + direction); } EXPORT_SYMBOL(tifm_map_sg); void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents, int direction) { - pci_unmap_sg(to_pci_dev(sock->dev.parent), sg, nents, direction); + dma_unmap_sg(&to_pci_dev(sock->dev.parent)->dev, sg, nents, direction); } EXPORT_SYMBOL(tifm_unmap_sg); -- cgit v1.2.3-70-g09d2 From 54fa156bb33ad06077ec709dc2331a4d5ee446f3 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 18 Sep 2021 07:36:42 +0200 Subject: mei: Remove usage of the deprecated "pci-dma-compat.h" API In [1], Christoph Hellwig has proposed to remove the wrappers in include/linux/pci-dma-compat.h. Some reasons why this API should be removed have been given by Julia Lawall in [2]. Finally, Arnd Bergmann reminded that the documentation was updated 11 years ago to only describe the modern linux/dma-mapping.h interfaces and mark the old bus-specific ones as no longer recommended, see commit 216bf58f4092 ("Documentation: convert PCI-DMA-mapping.txt to use the generic DMA API"). A coccinelle script has been used to perform the needed transformation Only relevant parts are given below. @@ expression e1, e2; @@ - pci_set_dma_mask(e1, e2) + dma_set_mask(&e1->dev, e2) [1]: https://lore.kernel.org/kernel-janitors/20200421081257.GA131897@infradead.org/ [2]: https://lore.kernel.org/kernel-janitors/alpine.DEB.2.22.394.2007120902170.2424@hadrien/ Reviewed-by: Arnd Bergmann Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/c6f280927835c5677cc0367fccdc0ef54b307bd8.1631943364.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/pci-txe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c index aec0483b8e72..fa20d9a27813 100644 --- a/drivers/misc/mei/pci-txe.c +++ b/drivers/misc/mei/pci-txe.c @@ -69,9 +69,9 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto end; } - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36)); + err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(36)); if (err) { - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); if (err) { dev_err(&pdev->dev, "No suitable DMA available.\n"); goto end; -- cgit v1.2.3-70-g09d2 From 75eac387a2539aa6c6bbee3affa23435f2096396 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 7 Sep 2021 11:53:32 +0100 Subject: soundwire: debugfs: use controller id and link_id for debugfs link_id can be zero and if we have multiple controller instances in a system like Qualcomm debugfs will end-up with duplicate namespace resulting in incorrect debugfs entries. Using bus-id and link-id combination should give a unique debugfs directory entry and should fix below warning too. "debugfs: Directory 'master-0' with parent 'soundwire' already present!" Fixes: bf03473d5bcc ("soundwire: add debugfs support") Signed-off-by: Srinivas Kandagatla Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20210907105332.1257-1-srinivas.kandagatla@linaro.org Signed-off-by: Vinod Koul --- drivers/soundwire/debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soundwire/debugfs.c b/drivers/soundwire/debugfs.c index b6cad0d59b7b..49900cd207bc 100644 --- a/drivers/soundwire/debugfs.c +++ b/drivers/soundwire/debugfs.c @@ -19,7 +19,7 @@ void sdw_bus_debugfs_init(struct sdw_bus *bus) return; /* create the debugfs master-N */ - snprintf(name, sizeof(name), "master-%d", bus->link_id); + snprintf(name, sizeof(name), "master-%d-%d", bus->id, bus->link_id); bus->debugfs = debugfs_create_dir(name, sdw_debugfs_root); } -- cgit v1.2.3-70-g09d2 From 3733c12ef4b55097efeeee4c7daf380b0810606f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 16 Sep 2021 10:59:42 +0200 Subject: ABI: sysfs-bus-soundwire-master: use wildcards on What definitions An "N" upper letter is not a wildcard, nor can easily be identified by script, specially since the USB sysfs define things like. bNumInterfaces. Use, instead, , in order to let script/get_abi.pl to convert it into a Regex. Signed-off-by: Mauro Carvalho Chehab Link: https://lore.kernel.org/r/eb62c19ce92c0dc1a50eb57c1052866256250644.1631782432.git.mchehab+huawei@kernel.org Signed-off-by: Vinod Koul --- Documentation/ABI/testing/sysfs-bus-soundwire-master | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-soundwire-master b/Documentation/ABI/testing/sysfs-bus-soundwire-master index 46ef038d8722..d2342911ffbb 100644 --- a/Documentation/ABI/testing/sysfs-bus-soundwire-master +++ b/Documentation/ABI/testing/sysfs-bus-soundwire-master @@ -1,13 +1,13 @@ -What: /sys/bus/soundwire/devices/sdw-master-N/revision - /sys/bus/soundwire/devices/sdw-master-N/clk_stop_modes - /sys/bus/soundwire/devices/sdw-master-N/clk_freq - /sys/bus/soundwire/devices/sdw-master-N/clk_gears - /sys/bus/soundwire/devices/sdw-master-N/default_col - /sys/bus/soundwire/devices/sdw-master-N/default_frame_rate - /sys/bus/soundwire/devices/sdw-master-N/default_row - /sys/bus/soundwire/devices/sdw-master-N/dynamic_shape - /sys/bus/soundwire/devices/sdw-master-N/err_threshold - /sys/bus/soundwire/devices/sdw-master-N/max_clk_freq +What: /sys/bus/soundwire/devices/sdw-master-/revision + /sys/bus/soundwire/devices/sdw-master-/clk_stop_modes + /sys/bus/soundwire/devices/sdw-master-/clk_freq + /sys/bus/soundwire/devices/sdw-master-/clk_gears + /sys/bus/soundwire/devices/sdw-master-/default_col + /sys/bus/soundwire/devices/sdw-master-/default_frame_rate + /sys/bus/soundwire/devices/sdw-master-/default_row + /sys/bus/soundwire/devices/sdw-master-/dynamic_shape + /sys/bus/soundwire/devices/sdw-master-/err_threshold + /sys/bus/soundwire/devices/sdw-master-/max_clk_freq Date: April 2020 -- cgit v1.2.3-70-g09d2 From ccfdcb325f2a9fd188b89bcd43bb82680823201d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 16 Sep 2021 10:59:43 +0200 Subject: ABI: sysfs-bus-soundwire-slave: use wildcards on What definitions An "N" upper letter is not a wildcard, nor can easily be identified by script, specially since the USB sysfs define things like. bNumInterfaces. Use, instead, , in order to let script/get_abi.pl to convert it into a Regex. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/416f4a746c116147abb08fb0155a6a4ed065dfd7.1631782432.git.mchehab+huawei@kernel.org Signed-off-by: Vinod Koul --- .../ABI/testing/sysfs-bus-soundwire-slave | 62 +++++++++++----------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-soundwire-slave b/Documentation/ABI/testing/sysfs-bus-soundwire-slave index d324aa0b678f..fbf55834dfee 100644 --- a/Documentation/ABI/testing/sysfs-bus-soundwire-slave +++ b/Documentation/ABI/testing/sysfs-bus-soundwire-slave @@ -64,37 +64,37 @@ Description: SoundWire Slave Data Port-0 DisCo properties. Data port 0 are used by the bus to configure the Data Port 0. -What: /sys/bus/soundwire/devices/sdw:.../dpN_src/max_word - /sys/bus/soundwire/devices/sdw:.../dpN_src/min_word - /sys/bus/soundwire/devices/sdw:.../dpN_src/words - /sys/bus/soundwire/devices/sdw:.../dpN_src/type - /sys/bus/soundwire/devices/sdw:.../dpN_src/max_grouping - /sys/bus/soundwire/devices/sdw:.../dpN_src/simple_ch_prep_sm - /sys/bus/soundwire/devices/sdw:.../dpN_src/ch_prep_timeout - /sys/bus/soundwire/devices/sdw:.../dpN_src/imp_def_interrupts - /sys/bus/soundwire/devices/sdw:.../dpN_src/min_ch - /sys/bus/soundwire/devices/sdw:.../dpN_src/max_ch - /sys/bus/soundwire/devices/sdw:.../dpN_src/channels - /sys/bus/soundwire/devices/sdw:.../dpN_src/ch_combinations - /sys/bus/soundwire/devices/sdw:.../dpN_src/max_async_buffer - /sys/bus/soundwire/devices/sdw:.../dpN_src/block_pack_mode - /sys/bus/soundwire/devices/sdw:.../dpN_src/port_encoding - - /sys/bus/soundwire/devices/sdw:.../dpN_sink/max_word - /sys/bus/soundwire/devices/sdw:.../dpN_sink/min_word - /sys/bus/soundwire/devices/sdw:.../dpN_sink/words - /sys/bus/soundwire/devices/sdw:.../dpN_sink/type - /sys/bus/soundwire/devices/sdw:.../dpN_sink/max_grouping - /sys/bus/soundwire/devices/sdw:.../dpN_sink/simple_ch_prep_sm - /sys/bus/soundwire/devices/sdw:.../dpN_sink/ch_prep_timeout - /sys/bus/soundwire/devices/sdw:.../dpN_sink/imp_def_interrupts - /sys/bus/soundwire/devices/sdw:.../dpN_sink/min_ch - /sys/bus/soundwire/devices/sdw:.../dpN_sink/max_ch - /sys/bus/soundwire/devices/sdw:.../dpN_sink/channels - /sys/bus/soundwire/devices/sdw:.../dpN_sink/ch_combinations - /sys/bus/soundwire/devices/sdw:.../dpN_sink/max_async_buffer - /sys/bus/soundwire/devices/sdw:.../dpN_sink/block_pack_mode - /sys/bus/soundwire/devices/sdw:.../dpN_sink/port_encoding +What: /sys/bus/soundwire/devices/sdw:.../dp_src/max_word + /sys/bus/soundwire/devices/sdw:.../dp_src/min_word + /sys/bus/soundwire/devices/sdw:.../dp_src/words + /sys/bus/soundwire/devices/sdw:.../dp_src/type + /sys/bus/soundwire/devices/sdw:.../dp_src/max_grouping + /sys/bus/soundwire/devices/sdw:.../dp_src/simple_ch_prep_sm + /sys/bus/soundwire/devices/sdw:.../dp_src/ch_prep_timeout + /sys/bus/soundwire/devices/sdw:.../dp_src/imp_def_interrupts + /sys/bus/soundwire/devices/sdw:.../dp_src/min_ch + /sys/bus/soundwire/devices/sdw:.../dp_src/max_ch + /sys/bus/soundwire/devices/sdw:.../dp_src/channels + /sys/bus/soundwire/devices/sdw:.../dp_src/ch_combinations + /sys/bus/soundwire/devices/sdw:.../dp_src/max_async_buffer + /sys/bus/soundwire/devices/sdw:.../dp_src/block_pack_mode + /sys/bus/soundwire/devices/sdw:.../dp_src/port_encoding + + /sys/bus/soundwire/devices/sdw:.../dp_sink/max_word + /sys/bus/soundwire/devices/sdw:.../dp_sink/min_word + /sys/bus/soundwire/devices/sdw:.../dp_sink/words + /sys/bus/soundwire/devices/sdw:.../dp_sink/type + /sys/bus/soundwire/devices/sdw:.../dp_sink/max_grouping + /sys/bus/soundwire/devices/sdw:.../dp_sink/simple_ch_prep_sm + /sys/bus/soundwire/devices/sdw:.../dp_sink/ch_prep_timeout + /sys/bus/soundwire/devices/sdw:.../dp_sink/imp_def_interrupts + /sys/bus/soundwire/devices/sdw:.../dp_sink/min_ch + /sys/bus/soundwire/devices/sdw:.../dp_sink/max_ch + /sys/bus/soundwire/devices/sdw:.../dp_sink/channels + /sys/bus/soundwire/devices/sdw:.../dp_sink/ch_combinations + /sys/bus/soundwire/devices/sdw:.../dp_sink/max_async_buffer + /sys/bus/soundwire/devices/sdw:.../dp_sink/block_pack_mode + /sys/bus/soundwire/devices/sdw:.../dp_sink/port_encoding Date: May 2020 -- cgit v1.2.3-70-g09d2 From 9b870e8c04ec0c079440d8320d9c728901517052 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 24 Sep 2021 15:26:57 +0200 Subject: phy: samsung: unify naming and describe driver in KConfig We use everywhere "Samsung" and "Exynos", not the uppercase versions. Describe better which driver applies to which SoC, to make configuring kernel for Samsung SoC easier. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20210924132658.109814-1-krzysztof.kozlowski@canonical.com Signed-off-by: Vinod Koul --- drivers/phy/samsung/Kconfig | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/phy/samsung/Kconfig b/drivers/phy/samsung/Kconfig index e20d2fcc9fe7..3ccaabf2850a 100644 --- a/drivers/phy/samsung/Kconfig +++ b/drivers/phy/samsung/Kconfig @@ -30,16 +30,16 @@ config PHY_EXYNOS_PCIE This driver provides PHY interface for Exynos PCIe controller. config PHY_SAMSUNG_UFS - tristate "SAMSUNG SoC series UFS PHY driver" + tristate "Exynos SoC series UFS PHY driver" depends on OF && (ARCH_EXYNOS || COMPILE_TEST) select GENERIC_PHY help - Enable this to support the Samsung UFS PHY driver for - Samsung SoCs. This driver provides the interface for UFS - host controller to do PHY related programming. + Enable this to support the Samsung Exynos SoC UFS PHY driver for + Samsung Exynos SoCs. This driver provides the interface for UFS host + controller to do PHY related programming. config PHY_SAMSUNG_USB2 - tristate "Samsung USB 2.0 PHY driver" + tristate "S5P/Exynos SoC series USB 2.0 PHY driver" depends on HAS_IOMEM depends on USB_EHCI_EXYNOS || USB_OHCI_EXYNOS || USB_DWC2 || COMPILE_TEST select GENERIC_PHY @@ -47,9 +47,9 @@ config PHY_SAMSUNG_USB2 default ARCH_EXYNOS help Enable this to support the Samsung USB 2.0 PHY driver for Samsung - SoCs. This driver provides the interface for USB 2.0 PHY. Support - for particular PHYs will be enabled based on the SoC type in addition - to this driver. + S5Pv210 and Exynos SoCs. This driver provides the interface for USB + 2.0 PHY. Support for particular PHYs will be enabled based on the SoC + type in addition to this driver. config PHY_EXYNOS4210_USB2 bool -- cgit v1.2.3-70-g09d2 From 73075011ffff876de8516a1e583dc41869293da9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 28 Sep 2021 09:34:11 +0200 Subject: phy: HiSilicon: Add driver for Kirin 970 PCIe PHY The Kirin 970 PHY is somewhat similar to the Kirin 960, but it does a lot more. Add the needed bits for PCIe to start working on HiKey 970 boards. Co-developed-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Signed-off-by: Mauro Carvalho Chehab Link: https://lore.kernel.org/r/b7a4ff41b57d861b003f1a00cae81f3d226fbe18.1632814194.git.mchehab+huawei@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/hisilicon/Kconfig | 10 + drivers/phy/hisilicon/Makefile | 1 + drivers/phy/hisilicon/phy-hi3670-pcie.c | 845 ++++++++++++++++++++++++++++++++ 3 files changed, 856 insertions(+) create mode 100644 drivers/phy/hisilicon/phy-hi3670-pcie.c diff --git a/drivers/phy/hisilicon/Kconfig b/drivers/phy/hisilicon/Kconfig index 4d008cfc279c..d3b92c288554 100644 --- a/drivers/phy/hisilicon/Kconfig +++ b/drivers/phy/hisilicon/Kconfig @@ -33,6 +33,16 @@ config PHY_HI3670_USB To compile this driver as a module, choose M here. +config PHY_HI3670_PCIE + tristate "hi3670 PCIe PHY support" + depends on (ARCH_HISI && ARM64) || COMPILE_TEST + select GENERIC_PHY + select MFD_SYSCON + help + Enable this to support the HiSilicon hi3670 PCIe PHY. + + To compile this driver as a module, choose M here. + config PHY_HISTB_COMBPHY tristate "HiSilicon STB SoCs COMBPHY support" depends on (ARCH_HISI && ARM64) || COMPILE_TEST diff --git a/drivers/phy/hisilicon/Makefile b/drivers/phy/hisilicon/Makefile index 51729868145b..4029d3813b1e 100644 --- a/drivers/phy/hisilicon/Makefile +++ b/drivers/phy/hisilicon/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_PHY_HI6220_USB) += phy-hi6220-usb.o obj-$(CONFIG_PHY_HI3660_USB) += phy-hi3660-usb3.o obj-$(CONFIG_PHY_HI3670_USB) += phy-hi3670-usb3.o +obj-$(CONFIG_PHY_HI3670_PCIE) += phy-hi3670-pcie.o obj-$(CONFIG_PHY_HISTB_COMBPHY) += phy-histb-combphy.o obj-$(CONFIG_PHY_HISI_INNO_USB2) += phy-hisi-inno-usb2.o obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o diff --git a/drivers/phy/hisilicon/phy-hi3670-pcie.c b/drivers/phy/hisilicon/phy-hi3670-pcie.c new file mode 100644 index 000000000000..c64c6679b1b9 --- /dev/null +++ b/drivers/phy/hisilicon/phy-hi3670-pcie.c @@ -0,0 +1,845 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe phy driver for Kirin 970 + * + * Copyright (C) 2017 HiSilicon Electronics Co., Ltd. + * https://www.huawei.com + * Copyright (C) 2021 Huawei Technologies Co., Ltd. + * https://www.huawei.com + * + * Authors: + * Mauro Carvalho Chehab + * Manivannan Sadhasivam + * + * Based on: + * https://lore.kernel.org/lkml/4c9d6581478aa966698758c0420933f5defab4dd.1612335031.git.mchehab+huawei@kernel.org/ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AXI_CLK_FREQ 207500000 +#define REF_CLK_FREQ 100000000 + +/* PCIe CTRL registers */ +#define SOC_PCIECTRL_CTRL7_ADDR 0x01c +#define SOC_PCIECTRL_CTRL12_ADDR 0x030 +#define SOC_PCIECTRL_CTRL20_ADDR 0x050 +#define SOC_PCIECTRL_CTRL21_ADDR 0x054 + +#define PCIE_OUTPUT_PULL_BITS GENMASK(3, 0) +#define SOC_PCIECTRL_CTRL20_2P_MEM_CTRL 0x02605550 +#define SOC_PCIECTRL_CTRL21_DEFAULT 0x20000070 +#define PCIE_PULL_UP_SYS_AUX_PWR_DET BIT(10) +#define PCIE_OUTPUT_PULL_DOWN BIT(1) + +/* PCIe PHY registers */ +#define SOC_PCIEPHY_CTRL0_ADDR 0x000 +#define SOC_PCIEPHY_CTRL1_ADDR 0x004 +#define SOC_PCIEPHY_CTRL38_ADDR 0x0098 +#define SOC_PCIEPHY_STATE0_ADDR 0x400 + +#define RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1 0xc004 +#define SUP_DIG_LVL_OVRD_IN 0x003c +#define LANEN_DIG_ASIC_TX_OVRD_IN_1 0x4008 +#define LANEN_DIG_ASIC_TX_OVRD_IN_2 0x400c + +#define PCIEPHY_RESET_BIT BIT(17) +#define PCIEPHY_PIPE_LINE0_RESET_BIT BIT(19) +#define PCIE_TXDETECT_RX_FAIL BIT(2) +#define PCIE_CLK_SOURCE BIT(8) +#define PCIE_IS_CLOCK_STABLE BIT(19) +#define PCIE_PULL_DOWN_PHY_TEST_POWERDOWN BIT(22) +#define PCIE_DEASSERT_CONTROLLER_PERST BIT(2) + +#define EYEPARAM_NOCFG 0xffffffff +#define EYE_PARM0_MASK GENMASK(8, 6) +#define EYE_PARM1_MASK GENMASK(11, 8) +#define EYE_PARM2_MASK GENMASK(5, 0) +#define EYE_PARM3_MASK GENMASK(12, 7) +#define EYE_PARM4_MASK GENMASK(14, 9) +#define EYE_PARM0_EN BIT(9) +#define EYE_PARM1_EN BIT(12) +#define EYE_PARM2_EN BIT(6) +#define EYE_PARM3_EN BIT(13) +#define EYE_PARM4_EN BIT(15) + +/* hi3670 pciephy register */ +#define APB_PHY_START_ADDR 0x40000 +#define SOC_PCIEPHY_MMC1PLL_CTRL1 0xc04 +#define SOC_PCIEPHY_MMC1PLL_CTRL16 0xC40 +#define SOC_PCIEPHY_MMC1PLL_CTRL17 0xC44 +#define SOC_PCIEPHY_MMC1PLL_CTRL20 0xC50 +#define SOC_PCIEPHY_MMC1PLL_CTRL21 0xC54 +#define SOC_PCIEPHY_MMC1PLL_STAT0 0xE00 + +#define CRGPERIPH_PEREN12 0x470 +#define CRGPERIPH_PERDIS12 0x474 +#define CRGPERIPH_PCIECTRL0 0x800 + +#define PCIE_FNPLL_FBDIV_MASK GENMASK(27, 16) +#define PCIE_FNPLL_FRACDIV_MASK GENMASK(23, 0) +#define PCIE_FNPLL_POSTDIV1_MASK GENMASK(10, 8) +#define PCIE_FNPLL_POSTDIV2_MASK GENMASK(14, 12) +#define PCIE_FNPLL_PLL_MODE_MASK BIT(25) + +#define PCIE_FNPLL_DLL_EN BIT(27) +#define PCIE_FNPLL_FBDIV 0xd0 +#define PCIE_FNPLL_FRACDIV 0x555555 +#define PCIE_FNPLL_POSTDIV1 0x5 +#define PCIE_FNPLL_POSTDIV2 0x4 +#define PCIE_FNPLL_PLL_MODE 0x0 + +#define PCIE_PHY_MMC1PLL 0x20 +#define PCIE_PHY_CHOOSE_FNPLL BIT(27) +#define PCIE_PHY_MMC1PLL_DISABLE BIT(0) +#define PCIE_PHY_PCIEPL_BP BIT(16) + +/* define ie,oe cfg */ +#define IO_OE_HARD_GT_MODE BIT(1) +#define IO_IE_EN_HARD_BYPASS BIT(27) +#define IO_OE_EN_HARD_BYPASS BIT(11) +#define IO_HARD_CTRL_DEBOUNCE_BYPASS BIT(10) +#define IO_OE_GT_MODE BIT(8) +#define DEBOUNCE_WAITCFG_IN GENMASK(23, 20) +#define DEBOUNCE_WAITCFG_OUT GENMASK(16, 13) + +#define IO_HP_DEBOUNCE_GT (BIT(12) | BIT(15)) +#define IO_PHYREF_SOFT_GT_MODE BIT(14) +#define IO_REF_SOFT_GT_MODE BIT(13) +#define IO_REF_HARD_GT_MODE BIT(0) + +/* noc power domain */ +#define NOC_POWER_IDLEREQ_1 0x38c +#define NOC_POWER_IDLE_1 0x394 +#define NOC_PW_MASK 0x10000 +#define NOC_PW_SET_BIT 0x1 + +#define NUM_EYEPARAM 5 + +/* info located in sysctrl */ +#define SCTRL_PCIE_CMOS_OFFSET 0x60 +#define SCTRL_PCIE_CMOS_BIT 0x10 +#define SCTRL_PCIE_ISO_OFFSET 0x44 +#define SCTRL_PCIE_ISO_BIT 0x30 +#define SCTRL_PCIE_HPCLK_OFFSET 0x190 +#define SCTRL_PCIE_HPCLK_BIT 0x184000 +#define SCTRL_PCIE_OE_OFFSET 0x14a +#define PCIE_DEBOUNCE_PARAM 0xf0f400 +#define PCIE_OE_BYPASS GENMASK(29, 28) + +/* peri_crg ctrl */ +#define CRGCTRL_PCIE_ASSERT_OFFSET 0x88 +#define CRGCTRL_PCIE_ASSERT_BIT 0x8c000000 + +#define FNPLL_HAS_LOCKED BIT(4) + +/* Time for delay */ +#define TIME_CMOS_MIN 100 +#define TIME_CMOS_MAX 105 +#define PIPE_CLK_STABLE_TIME 100 +#define PLL_CTRL_WAIT_TIME 200 +#define NOC_POWER_TIME 100 + +struct hi3670_pcie_phy { + struct device *dev; + void __iomem *base; + struct regmap *apb; + struct regmap *crgctrl; + struct regmap *sysctrl; + struct regmap *pmctrl; + struct clk *apb_sys_clk; + struct clk *apb_phy_clk; + struct clk *phy_ref_clk; + struct clk *aclk; + struct clk *aux_clk; + u32 eye_param[NUM_EYEPARAM]; +}; + +/* Registers in PCIePHY */ +static inline void hi3670_apb_phy_writel(struct hi3670_pcie_phy *phy, u32 val, + u32 reg) +{ + writel(val, phy->base + APB_PHY_START_ADDR + reg); +} + +static inline u32 hi3670_apb_phy_readl(struct hi3670_pcie_phy *phy, u32 reg) +{ + return readl(phy->base + APB_PHY_START_ADDR + reg); +} + +static inline void hi3670_apb_phy_updatel(struct hi3670_pcie_phy *phy, + u32 val, u32 mask, u32 reg) +{ + u32 regval; + + regval = hi3670_apb_phy_readl(phy, reg); + regval &= ~mask; + regval |= val; + hi3670_apb_phy_writel(phy, regval, reg); +} + +static inline void kirin_apb_natural_phy_writel(struct hi3670_pcie_phy *phy, + u32 val, u32 reg) +{ + writel(val, phy->base + reg); +} + +static inline u32 kirin_apb_natural_phy_readl(struct hi3670_pcie_phy *phy, + u32 reg) +{ + return readl(phy->base + reg); +} + +static void hi3670_pcie_phy_oe_enable(struct hi3670_pcie_phy *phy, bool enable) +{ + u32 val; + + regmap_read(phy->sysctrl, SCTRL_PCIE_OE_OFFSET, &val); + val |= PCIE_DEBOUNCE_PARAM; + if (enable) + val &= ~PCIE_OE_BYPASS; + else + val |= PCIE_OE_BYPASS; + regmap_write(phy->sysctrl, SCTRL_PCIE_OE_OFFSET, val); +} + +static void hi3670_pcie_get_eyeparam(struct hi3670_pcie_phy *phy) +{ + struct device *dev = phy->dev; + struct device_node *np; + int ret, i; + + np = dev->of_node; + + ret = of_property_read_u32_array(np, "hisilicon,eye-diagram-param", + phy->eye_param, NUM_EYEPARAM); + if (!ret) + return; + + /* There's no optional eye_param property. Set array to default */ + for (i = 0; i < NUM_EYEPARAM; i++) + phy->eye_param[i] = EYEPARAM_NOCFG; +} + +static void hi3670_pcie_set_eyeparam(struct hi3670_pcie_phy *phy) +{ + u32 val; + + val = kirin_apb_natural_phy_readl(phy, RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1); + + if (phy->eye_param[1] != EYEPARAM_NOCFG) { + val &= ~EYE_PARM1_MASK; + val |= FIELD_PREP(EYE_PARM1_MASK, phy->eye_param[1]); + val |= EYE_PARM1_EN; + } + kirin_apb_natural_phy_writel(phy, val, + RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1); + + val = kirin_apb_natural_phy_readl(phy, LANEN_DIG_ASIC_TX_OVRD_IN_2); + val &= ~(EYE_PARM2_MASK | EYE_PARM3_MASK); + if (phy->eye_param[2] != EYEPARAM_NOCFG) { + val |= FIELD_PREP(EYE_PARM2_MASK, phy->eye_param[2]); + val |= EYE_PARM2_EN; + } + + if (phy->eye_param[3] != EYEPARAM_NOCFG) { + val |= FIELD_PREP(EYE_PARM3_MASK, phy->eye_param[3]); + val |= EYE_PARM3_EN; + } + + kirin_apb_natural_phy_writel(phy, val, LANEN_DIG_ASIC_TX_OVRD_IN_2); + + val = kirin_apb_natural_phy_readl(phy, SUP_DIG_LVL_OVRD_IN); + if (phy->eye_param[0] != EYEPARAM_NOCFG) { + val &= ~EYE_PARM0_MASK; + val |= FIELD_PREP(EYE_PARM0_MASK, phy->eye_param[0]); + val |= EYE_PARM0_EN; + } + kirin_apb_natural_phy_writel(phy, val, SUP_DIG_LVL_OVRD_IN); + + val = kirin_apb_natural_phy_readl(phy, LANEN_DIG_ASIC_TX_OVRD_IN_1); + if (phy->eye_param[4] != EYEPARAM_NOCFG) { + val &= ~EYE_PARM4_MASK; + val |= FIELD_PREP(EYE_PARM4_MASK, phy->eye_param[4]); + val |= EYE_PARM4_EN; + } + kirin_apb_natural_phy_writel(phy, val, LANEN_DIG_ASIC_TX_OVRD_IN_1); +} + +static void hi3670_pcie_natural_cfg(struct hi3670_pcie_phy *phy) +{ + u32 val; + + /* change 2p mem_ctrl */ + regmap_write(phy->apb, SOC_PCIECTRL_CTRL20_ADDR, + SOC_PCIECTRL_CTRL20_2P_MEM_CTRL); + + regmap_read(phy->apb, SOC_PCIECTRL_CTRL7_ADDR, &val); + val |= PCIE_PULL_UP_SYS_AUX_PWR_DET; + regmap_write(phy->apb, SOC_PCIECTRL_CTRL7_ADDR, val); + + /* output, pull down */ + regmap_read(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, &val); + val &= ~PCIE_OUTPUT_PULL_BITS; + val |= PCIE_OUTPUT_PULL_DOWN; + regmap_write(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, val); + + /* Handle phy_reset and lane0_reset to HW */ + hi3670_apb_phy_updatel(phy, PCIEPHY_RESET_BIT, + PCIEPHY_PIPE_LINE0_RESET_BIT | PCIEPHY_RESET_BIT, + SOC_PCIEPHY_CTRL1_ADDR); + + /* fix chip bug: TxDetectRx fail */ + hi3670_apb_phy_updatel(phy, PCIE_TXDETECT_RX_FAIL, PCIE_TXDETECT_RX_FAIL, + SOC_PCIEPHY_CTRL38_ADDR); +} + +static void hi3670_pcie_pll_init(struct hi3670_pcie_phy *phy) +{ + hi3670_apb_phy_updatel(phy, PCIE_PHY_CHOOSE_FNPLL, PCIE_PHY_CHOOSE_FNPLL, + SOC_PCIEPHY_MMC1PLL_CTRL1); + + hi3670_apb_phy_updatel(phy, + FIELD_PREP(PCIE_FNPLL_FBDIV_MASK, PCIE_FNPLL_FBDIV), + PCIE_FNPLL_FBDIV_MASK, + SOC_PCIEPHY_MMC1PLL_CTRL16); + + hi3670_apb_phy_updatel(phy, + FIELD_PREP(PCIE_FNPLL_FRACDIV_MASK, PCIE_FNPLL_FRACDIV), + PCIE_FNPLL_FRACDIV_MASK, SOC_PCIEPHY_MMC1PLL_CTRL17); + + hi3670_apb_phy_updatel(phy, + PCIE_FNPLL_DLL_EN | + FIELD_PREP(PCIE_FNPLL_POSTDIV1_MASK, PCIE_FNPLL_POSTDIV1) | + FIELD_PREP(PCIE_FNPLL_POSTDIV2_MASK, PCIE_FNPLL_POSTDIV2) | + FIELD_PREP(PCIE_FNPLL_PLL_MODE_MASK, PCIE_FNPLL_PLL_MODE), + PCIE_FNPLL_POSTDIV1_MASK | + PCIE_FNPLL_POSTDIV2_MASK | + PCIE_FNPLL_PLL_MODE_MASK | PCIE_FNPLL_DLL_EN, + SOC_PCIEPHY_MMC1PLL_CTRL20); + + hi3670_apb_phy_writel(phy, PCIE_PHY_MMC1PLL, + SOC_PCIEPHY_MMC1PLL_CTRL21); +} + +static int hi3670_pcie_pll_ctrl(struct hi3670_pcie_phy *phy, bool enable) +{ + struct device *dev = phy->dev; + u32 val; + int time = PLL_CTRL_WAIT_TIME; + + if (enable) { + /* pd = 0 */ + hi3670_apb_phy_updatel(phy, 0, PCIE_PHY_MMC1PLL_DISABLE, + SOC_PCIEPHY_MMC1PLL_CTRL16); + + /* choose FNPLL */ + val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_MMC1PLL_STAT0); + while (!(val & FNPLL_HAS_LOCKED)) { + if (!time) { + dev_err(dev, "wait for pll_lock timeout\n"); + return -EINVAL; + } + time--; + udelay(1); + val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_MMC1PLL_STAT0); + } + + hi3670_apb_phy_updatel(phy, 0, PCIE_PHY_PCIEPL_BP, + SOC_PCIEPHY_MMC1PLL_CTRL20); + + } else { + hi3670_apb_phy_updatel(phy, + PCIE_PHY_MMC1PLL_DISABLE, + PCIE_PHY_MMC1PLL_DISABLE, + SOC_PCIEPHY_MMC1PLL_CTRL16); + + hi3670_apb_phy_updatel(phy, PCIE_PHY_PCIEPL_BP, + PCIE_PHY_PCIEPL_BP, + SOC_PCIEPHY_MMC1PLL_CTRL20); + } + + return 0; +} + +static void hi3670_pcie_hp_debounce_gt(struct hi3670_pcie_phy *phy, bool open) +{ + if (open) + /* gt_clk_pcie_hp/gt_clk_pcie_debounce open */ + regmap_write(phy->crgctrl, CRGPERIPH_PEREN12, + IO_HP_DEBOUNCE_GT); + else + /* gt_clk_pcie_hp/gt_clk_pcie_debounce close */ + regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12, + IO_HP_DEBOUNCE_GT); +} + +static void hi3670_pcie_phyref_gt(struct hi3670_pcie_phy *phy, bool open) +{ + unsigned int val; + + regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val); + + if (open) + val &= ~IO_OE_HARD_GT_MODE; /* enable hard gt mode */ + else + val |= IO_OE_HARD_GT_MODE; /* disable hard gt mode */ + + regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val); + + /* disable soft gt mode */ + regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12, IO_PHYREF_SOFT_GT_MODE); +} + +static void hi3670_pcie_oe_ctrl(struct hi3670_pcie_phy *phy, bool en_flag) +{ + unsigned int val; + + regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val); + + /* set ie cfg */ + val |= IO_IE_EN_HARD_BYPASS; + + /* set oe cfg */ + val &= ~IO_HARD_CTRL_DEBOUNCE_BYPASS; + + /* set phy_debounce in&out time */ + val |= (DEBOUNCE_WAITCFG_IN | DEBOUNCE_WAITCFG_OUT); + + /* select oe_gt_mode */ + val |= IO_OE_GT_MODE; + + if (en_flag) + val &= ~IO_OE_EN_HARD_BYPASS; + else + val |= IO_OE_EN_HARD_BYPASS; + + regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val); +} + +static void hi3670_pcie_ioref_gt(struct hi3670_pcie_phy *phy, bool open) +{ + unsigned int val; + + if (open) { + regmap_write(phy->apb, SOC_PCIECTRL_CTRL21_ADDR, + SOC_PCIECTRL_CTRL21_DEFAULT); + + hi3670_pcie_oe_ctrl(phy, true); + + /* en hard gt mode */ + regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val); + val &= ~IO_REF_HARD_GT_MODE; + regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val); + + /* disable soft gt mode */ + regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12, + IO_REF_SOFT_GT_MODE); + + } else { + /* disable hard gt mode */ + regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val); + val |= IO_REF_HARD_GT_MODE; + regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val); + + /* disable soft gt mode */ + regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12, + IO_REF_SOFT_GT_MODE); + + hi3670_pcie_oe_ctrl(phy, false); + } +} + +static int hi3670_pcie_allclk_ctrl(struct hi3670_pcie_phy *phy, bool clk_on) +{ + struct device *dev = phy->dev; + int ret = 0; + + if (!clk_on) + goto close_clocks; + + /* choose 100MHz clk src: Bit[8]==1 pad, Bit[8]==0 pll */ + hi3670_apb_phy_updatel(phy, 0, PCIE_CLK_SOURCE, + SOC_PCIEPHY_CTRL1_ADDR); + + hi3670_pcie_pll_init(phy); + + ret = hi3670_pcie_pll_ctrl(phy, true); + if (ret) { + dev_err(dev, "Failed to enable pll\n"); + return -EINVAL; + } + hi3670_pcie_hp_debounce_gt(phy, true); + hi3670_pcie_phyref_gt(phy, true); + hi3670_pcie_ioref_gt(phy, true); + + ret = clk_set_rate(phy->aclk, AXI_CLK_FREQ); + if (ret) { + dev_err(dev, "Failed to set rate\n"); + goto close_clocks; + } + + return 0; + +close_clocks: + hi3670_pcie_ioref_gt(phy, false); + hi3670_pcie_phyref_gt(phy, false); + hi3670_pcie_hp_debounce_gt(phy, false); + + hi3670_pcie_pll_ctrl(phy, false); + + return ret; +} + +static bool is_pipe_clk_stable(struct hi3670_pcie_phy *phy) +{ + struct device *dev = phy->dev; + u32 val; + u32 time = PIPE_CLK_STABLE_TIME; + u32 pipe_clk_stable = PCIE_IS_CLOCK_STABLE; + + val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_STATE0_ADDR); + while (val & pipe_clk_stable) { + mdelay(1); + if (!time) { + dev_err(dev, "PIPE clk is not stable\n"); + return false; + } + time--; + val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_STATE0_ADDR); + } + + return true; +} + +static int hi3670_pcie_noc_power(struct hi3670_pcie_phy *phy, bool enable) +{ + struct device *dev = phy->dev; + u32 time = NOC_POWER_TIME; + unsigned int val = NOC_PW_MASK; + int rst; + + if (enable) + val = NOC_PW_MASK | NOC_PW_SET_BIT; + else + val = NOC_PW_MASK; + rst = enable ? 1 : 0; + + regmap_write(phy->pmctrl, NOC_POWER_IDLEREQ_1, val); + + time = NOC_POWER_TIME; + regmap_read(phy->pmctrl, NOC_POWER_IDLE_1, &val); + while ((val & NOC_PW_SET_BIT) != rst) { + udelay(10); + if (!time) { + dev_err(dev, "Failed to reverse noc power-status\n"); + return -EINVAL; + } + time--; + regmap_read(phy->pmctrl, NOC_POWER_IDLE_1, &val); + } + + return 0; +} + +static int hi3670_pcie_get_resources_from_pcie(struct hi3670_pcie_phy *phy) +{ + struct device_node *pcie_port; + struct device *dev = phy->dev; + struct device *pcie_dev; + + pcie_port = of_get_child_by_name(dev->parent->of_node, "pcie"); + if (!pcie_port) { + dev_err(dev, "no pcie node found in %s\n", + dev->parent->of_node->full_name); + return -ENODEV; + } + + pcie_dev = bus_find_device_by_of_node(&platform_bus_type, pcie_port); + if (!pcie_dev) { + dev_err(dev, "Didn't find pcie device\n"); + return -ENODEV; + } + + /* + * We might just use NULL instead of the APB name, as the + * pcie-kirin currently registers directly just one regmap (although + * the DWC driver register other regmaps). + * + * Yet, it sounds safer to warrant that it will be accessing the + * right regmap. So, let's use the named version. + */ + phy->apb = dev_get_regmap(pcie_dev, "kirin_pcie_apb"); + if (!phy->apb) { + dev_err(dev, "Failed to get APB regmap\n"); + return -ENODEV; + } + + return 0; +} + +static int kirin_pcie_clk_ctrl(struct hi3670_pcie_phy *phy, bool enable) +{ + int ret = 0; + + if (!enable) + goto close_clk; + + ret = clk_set_rate(phy->phy_ref_clk, REF_CLK_FREQ); + if (ret) + return ret; + + ret = clk_prepare_enable(phy->phy_ref_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(phy->apb_sys_clk); + if (ret) + goto apb_sys_fail; + + ret = clk_prepare_enable(phy->apb_phy_clk); + if (ret) + goto apb_phy_fail; + + ret = clk_prepare_enable(phy->aclk); + if (ret) + goto aclk_fail; + + ret = clk_prepare_enable(phy->aux_clk); + if (ret) + goto aux_clk_fail; + + return 0; + +close_clk: + clk_disable_unprepare(phy->aux_clk); +aux_clk_fail: + clk_disable_unprepare(phy->aclk); +aclk_fail: + clk_disable_unprepare(phy->apb_phy_clk); +apb_phy_fail: + clk_disable_unprepare(phy->apb_sys_clk); +apb_sys_fail: + clk_disable_unprepare(phy->phy_ref_clk); + + return ret; +} + +static int hi3670_pcie_phy_init(struct phy *generic_phy) +{ + struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy); + int ret; + + /* + * The code under hi3670_pcie_get_resources_from_pcie() need to + * access the reset-gpios and the APB registers, both from the + * pcie-kirin driver. + * + * The APB is obtained via the pcie driver's regmap + * Such kind of resource can only be obtained during the PCIe + * power_on sequence, as the code inside pcie-kirin needs to + * be already probed, as it needs to register the APB regmap. + */ + + ret = hi3670_pcie_get_resources_from_pcie(phy); + if (ret) + return ret; + + return 0; +} + +static int hi3670_pcie_phy_power_on(struct phy *generic_phy) +{ + struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy); + int val, ret; + + /* Power supply for Host */ + regmap_write(phy->sysctrl, SCTRL_PCIE_CMOS_OFFSET, SCTRL_PCIE_CMOS_BIT); + usleep_range(TIME_CMOS_MIN, TIME_CMOS_MAX); + + hi3670_pcie_phy_oe_enable(phy, true); + + ret = kirin_pcie_clk_ctrl(phy, true); + if (ret) + return ret; + + /* ISO disable, PCIeCtrl, PHY assert and clk gate clear */ + regmap_write(phy->sysctrl, SCTRL_PCIE_ISO_OFFSET, SCTRL_PCIE_ISO_BIT); + regmap_write(phy->crgctrl, CRGCTRL_PCIE_ASSERT_OFFSET, + CRGCTRL_PCIE_ASSERT_BIT); + regmap_write(phy->sysctrl, SCTRL_PCIE_HPCLK_OFFSET, + SCTRL_PCIE_HPCLK_BIT); + + hi3670_pcie_natural_cfg(phy); + + ret = hi3670_pcie_allclk_ctrl(phy, true); + if (ret) + goto disable_clks; + + /* pull down phy_test_powerdown signal */ + hi3670_apb_phy_updatel(phy, 0, PCIE_PULL_DOWN_PHY_TEST_POWERDOWN, + SOC_PCIEPHY_CTRL0_ADDR); + + /* deassert controller perst_n */ + regmap_read(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, &val); + val |= PCIE_DEASSERT_CONTROLLER_PERST; + regmap_write(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, val); + udelay(10); + + ret = is_pipe_clk_stable(phy); + if (!ret) + goto disable_clks; + + hi3670_pcie_set_eyeparam(phy); + + ret = hi3670_pcie_noc_power(phy, false); + if (ret) + goto disable_clks; + + return 0; + +disable_clks: + kirin_pcie_clk_ctrl(phy, false); + return ret; +} + +static int hi3670_pcie_phy_power_off(struct phy *generic_phy) +{ + struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy); + + hi3670_pcie_phy_oe_enable(phy, false); + + hi3670_pcie_allclk_ctrl(phy, false); + + /* Drop power supply for Host */ + regmap_write(phy->sysctrl, SCTRL_PCIE_CMOS_OFFSET, 0); + + /* + * FIXME: The enabled clocks should be disabled here by calling + * kirin_pcie_clk_ctrl(phy, false); + * However, some clocks used at Kirin 970 should be marked as + * CLK_IS_CRITICAL at clk-hi3670 driver, as powering such clocks off + * cause an Asynchronous SError interrupt, which produces panic(). + * While clk-hi3670 is not fixed, we cannot risk disabling clocks here. + */ + + return 0; +} + +static const struct phy_ops hi3670_phy_ops = { + .init = hi3670_pcie_phy_init, + .power_on = hi3670_pcie_phy_power_on, + .power_off = hi3670_pcie_phy_power_off, + .owner = THIS_MODULE, +}; + +static int hi3670_pcie_phy_get_resources(struct hi3670_pcie_phy *phy, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + /* syscon */ + phy->crgctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-crgctrl"); + if (IS_ERR(phy->crgctrl)) + return PTR_ERR(phy->crgctrl); + + phy->sysctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-sctrl"); + if (IS_ERR(phy->sysctrl)) + return PTR_ERR(phy->sysctrl); + + phy->pmctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-pmctrl"); + if (IS_ERR(phy->sysctrl)) + return PTR_ERR(phy->sysctrl); + + /* clocks */ + phy->phy_ref_clk = devm_clk_get(dev, "phy_ref"); + if (IS_ERR(phy->phy_ref_clk)) + return PTR_ERR(phy->phy_ref_clk); + + phy->aux_clk = devm_clk_get(dev, "aux"); + if (IS_ERR(phy->aux_clk)) + return PTR_ERR(phy->aux_clk); + + phy->apb_phy_clk = devm_clk_get(dev, "apb_phy"); + if (IS_ERR(phy->apb_phy_clk)) + return PTR_ERR(phy->apb_phy_clk); + + phy->apb_sys_clk = devm_clk_get(dev, "apb_sys"); + if (IS_ERR(phy->apb_sys_clk)) + return PTR_ERR(phy->apb_sys_clk); + + phy->aclk = devm_clk_get(dev, "aclk"); + if (IS_ERR(phy->aclk)) + return PTR_ERR(phy->aclk); + + /* registers */ + phy->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(phy->base)) + return PTR_ERR(phy->base); + + hi3670_pcie_get_eyeparam(phy); + + return 0; +} + +static int hi3670_pcie_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct hi3670_pcie_phy *phy; + struct phy *generic_phy; + int ret; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->dev = dev; + + ret = hi3670_pcie_phy_get_resources(phy, pdev); + if (ret) + return ret; + + generic_phy = devm_phy_create(dev, dev->of_node, &hi3670_phy_ops); + if (IS_ERR(generic_phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(generic_phy); + } + + phy_set_drvdata(generic_phy, phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id hi3670_pcie_phy_match[] = { + { + .compatible = "hisilicon,hi970-pcie-phy", + }, + {}, +}; + +static struct platform_driver hi3670_pcie_phy_driver = { + .probe = hi3670_pcie_phy_probe, + .driver = { + .of_match_table = hi3670_pcie_phy_match, + .name = "hi3670_pcie_phy", + .suppress_bind_attrs = true, + } +}; +builtin_platform_driver(hi3670_pcie_phy_driver); + +MODULE_DEVICE_TABLE(of, hi3670_pcie_phy_match); +MODULE_DESCRIPTION("PCIe phy driver for Kirin 970"); +MODULE_AUTHOR("Mauro Carvalho Chehab "); +MODULE_AUTHOR("Manivannan Sadhasivam "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-70-g09d2 From c2aff14ea0d909a0132471ce2f79ffeb259cce08 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 27 Sep 2021 15:16:39 +0800 Subject: dt-bindings: phy: qcom,qmp: Update maintainer email Manu's codeaurora.org email address bounces. Before he comes back with his new email, fill Vinod's address in there. Signed-off-by: Shawn Guo Link: https://lore.kernel.org/r/20210927071639.6225-1-shawnguo@kernel.org Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml index 75be5650a198..a167b5c1ae17 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml @@ -8,7 +8,7 @@ $schema: "http://devicetree.org/meta-schemas/core.yaml#" title: Qualcomm QMP PHY controller maintainers: - - Manu Gautam + - Vinod Koul description: QMP phy controller supports physical layer functionality for a number of -- cgit v1.2.3-70-g09d2 From 34356d113bdc62ff68cf7b760ef98580aafdc60c Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Thu, 23 Sep 2021 10:50:13 +0800 Subject: phy: broadcom: Kconfig: Add configuration menu for Broadcom phy drivers Adding a configuration menu to hold many Broadcom phy drivers helps to make the menu display more concise. Signed-off-by: Cai Huoqing Acked-by: Florian Fainelli Link: https://lore.kernel.org/r/20210923025013.189-1-caihuoqing@baidu.com Signed-off-by: Vinod Koul --- drivers/phy/broadcom/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/phy/broadcom/Kconfig b/drivers/phy/broadcom/Kconfig index fd92b73b7109..f81e23742079 100644 --- a/drivers/phy/broadcom/Kconfig +++ b/drivers/phy/broadcom/Kconfig @@ -2,6 +2,8 @@ # # Phy drivers for Broadcom platforms # +menu "PHY drivers for Broadcom platforms" + config PHY_BCM63XX_USBH tristate "BCM63xx USBH PHY driver" depends on BMIPS_GENERIC || COMPILE_TEST @@ -112,3 +114,5 @@ config PHY_BCM_SR_PCIE help Enable this to support the Broadcom Stingray PCIe PHY If unsure, say N. + +endmenu -- cgit v1.2.3-70-g09d2 From 40683ee5ff040b979f02ce2adf02ea32b83cd006 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 19 Sep 2021 11:11:09 +0800 Subject: dt-bindings: phy: qcom,qusb2: Add compatible for QCM2290 Add compatible for QUSB2 PHY on QCM2290 platform. Signed-off-by: Shawn Guo Acked-by: Rob Herring Link: https://lore.kernel.org/r/20210919031110.25064-2-shawn.guo@linaro.org Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml index ec9ccaaba098..48ae604b2194 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml @@ -21,6 +21,7 @@ properties: - qcom,ipq8074-qusb2-phy - qcom,msm8996-qusb2-phy - qcom,msm8998-qusb2-phy + - qcom,qcm2290-qusb2-phy - qcom,sdm660-qusb2-phy - qcom,ipq6018-qusb2-phy - qcom,sm4250-qusb2-phy -- cgit v1.2.3-70-g09d2 From 0fd732f8246786164db7375f7ae7b8d1f4cdbc14 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 19 Sep 2021 11:11:10 +0800 Subject: phy: qcom-qusb2: Add compatible for QCM2290 Add compatible for QCM2290 QUSB2 device which reuses SM6115 configuration. Signed-off-by: Shawn Guo Link: https://lore.kernel.org/r/20210919031110.25064-3-shawn.guo@linaro.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qusb2.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c index 3c1d3b71c825..ae063a8b3e28 100644 --- a/drivers/phy/qualcomm/phy-qcom-qusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c @@ -913,6 +913,9 @@ static const struct of_device_id qusb2_phy_of_match_table[] = { }, { .compatible = "qcom,msm8998-qusb2-phy", .data = &msm8998_phy_cfg, + }, { + .compatible = "qcom,qcm2290-qusb2-phy", + .data = &sm6115_phy_cfg, }, { .compatible = "qcom,sdm660-qusb2-phy", .data = &sdm660_phy_cfg, -- cgit v1.2.3-70-g09d2 From 55b9b741712de84d144cfed6cb3987314002ea21 Mon Sep 17 00:00:00 2001 From: RafaÅ‚ MiÅ‚ecki Date: Mon, 13 Sep 2021 10:00:22 +0200 Subject: dt-bindings: phy: brcm,ns-usb2-phy: bind just a PHY block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old binding was covering the whole DMU block space (DMU block contains CRU block which contains USB PHY). It was a bad design, overkill and a non-generic solution. Northstar's USB 2.0 PHY is a small block (part of the CRU MFD) and binding should be designed to represent that properly. Rework the binding to map just PHY with the "reg" property and use syscon to reference shared register that controls block access. The old binding is deprecated now. Signed-off-by: RafaÅ‚ MiÅ‚ecki Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20210913080024.6951-2-zajec5@gmail.com Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/bcm-ns-usb2-phy.yaml | 25 ++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/bcm-ns-usb2-phy.yaml b/Documentation/devicetree/bindings/phy/bcm-ns-usb2-phy.yaml index 05b4dcd80019..426101530a21 100644 --- a/Documentation/devicetree/bindings/phy/bcm-ns-usb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/bcm-ns-usb2-phy.yaml @@ -18,13 +18,21 @@ properties: const: brcm,ns-usb2-phy reg: - items: - - description: iomem address range of DMU (Device Management Unit) + anyOf: + - maxItems: 1 + description: PHY control register + - maxItems: 1 + description: iomem address range of DMU (Device Management Unit) + deprecated: true reg-names: items: - const: dmu + brcm,syscon-clkset: + description: phandle to syscon for clkset register + $ref: /schemas/types.yaml#/definitions/phandle + clocks: items: - description: USB PHY reference clock @@ -39,20 +47,25 @@ properties: required: - compatible - reg - - reg-names - clocks - clock-names - "#phy-cells" +oneOf: + - required: + - brcm,syscon-clkset + - required: + - reg-names + additionalProperties: false examples: - | #include - phy@1800c000 { + phy@1800c164 { compatible = "brcm,ns-usb2-phy"; - reg = <0x1800c000 0x1000>; - reg-names = "dmu"; + reg = <0x1800c164 0x4>; + brcm,syscon-clkset = <&clkset>; clocks = <&genpll BCM_NSP_GENPLL_USB_PHY_REF_CLK>; clock-names = "phy-ref-clk"; #phy-cells = <0>; -- cgit v1.2.3-70-g09d2 From 6ae6942fe996a32f0ff471c6481b3a545e11e14b Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Wed, 22 Sep 2021 21:00:16 +0800 Subject: phy: qcom-qmp: Make use of the helper function devm_add_action_or_reset() The helper function devm_add_action_or_reset() will internally call devm_add_action(), and gif devm_add_action() fails then it will execute the action mentioned and return the error code. So use devm_add_action_or_reset() instead of devm_add_action() to simplify the error handling, reduce the code. Signed-off-by: Cai Huoqing Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20210922130017.692-1-caihuoqing@baidu.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index f14032170b1c..084e3d96264e 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -5154,11 +5154,7 @@ static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np) * Roll a devm action because the clock provider is the child node, but * the child node is not actually a device. */ - ret = devm_add_action(qmp->dev, phy_clk_release_provider, np); - if (ret) - phy_clk_release_provider(np); - - return ret; + return devm_add_action_or_reset(qmp->dev, phy_clk_release_provider, np); } /* @@ -5350,11 +5346,7 @@ static int phy_dp_clks_register(struct qcom_qmp *qmp, struct qmp_phy *qphy, * Roll a devm action because the clock provider is the child node, but * the child node is not actually a device. */ - ret = devm_add_action(qmp->dev, phy_clk_release_provider, np); - if (ret) - phy_clk_release_provider(np); - - return ret; + return devm_add_action_or_reset(qmp->dev, phy_clk_release_provider, np); } static const struct phy_ops qcom_qmp_phy_gen_ops = { -- cgit v1.2.3-70-g09d2 From 717e04fba4fa0e2f8e5a1fa810c5eff4d4d5e7db Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Wed, 22 Sep 2021 21:00:23 +0800 Subject: phy: rockchip-inno-usb2: Make use of the helper function devm_add_action_or_reset() The helper function devm_add_action_or_reset() will internally call devm_add_action(), and gif devm_add_action() fails then it will execute the action mentioned and return the error code. So use devm_add_action_or_reset() instead of devm_add_action() to simplify the error handling, reduce the code. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20210922130024.745-1-caihuoqing@baidu.com Signed-off-by: Vinod Koul --- drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index 4f569d9307b9..1938365abbb3 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -321,7 +321,7 @@ rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy) struct device_node *node = rphy->dev->of_node; struct clk_init_data init; const char *clk_name; - int ret; + int ret = 0; init.flags = 0; init.name = "clk_usbphy_480m"; @@ -352,15 +352,8 @@ rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy) if (ret < 0) goto err_clk_provider; - ret = devm_add_action(rphy->dev, rockchip_usb2phy_clk480m_unregister, - rphy); - if (ret < 0) - goto err_unreg_action; - - return 0; + return devm_add_action_or_reset(rphy->dev, rockchip_usb2phy_clk480m_unregister, rphy); -err_unreg_action: - of_clk_del_provider(node); err_clk_provider: clk_unregister(rphy->clk480m); err_ret: -- cgit v1.2.3-70-g09d2 From 349f2fe48dfefa9ca6938675e20d90a762a18078 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 17 Sep 2021 13:46:22 +0200 Subject: ipack: ipoctal: rename tty-driver pointer The name "tty" is typically used for pointers to struct tty_struct. Rename the tty-driver pointer used during registration to something more apt to improve readability. Acked-by: Samuel Iglesias Gonsalvez Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20210917114622.5412-7-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/ipack/devices/ipoctal.c | 48 ++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/drivers/ipack/devices/ipoctal.c b/drivers/ipack/devices/ipoctal.c index c709861198e5..20d2b9ec1227 100644 --- a/drivers/ipack/devices/ipoctal.c +++ b/drivers/ipack/devices/ipoctal.c @@ -276,7 +276,7 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr, { int res; int i; - struct tty_driver *tty; + struct tty_driver *drv; struct ipoctal_channel *channel; struct ipack_region *region; void __iomem *addr; @@ -359,38 +359,38 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr, /* Register the TTY device */ /* Each IP-OCTAL channel is a TTY port */ - tty = tty_alloc_driver(NR_CHANNELS, TTY_DRIVER_REAL_RAW | + drv = tty_alloc_driver(NR_CHANNELS, TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV); - if (IS_ERR(tty)) - return PTR_ERR(tty); + if (IS_ERR(drv)) + return PTR_ERR(drv); /* Fill struct tty_driver with ipoctal data */ - tty->owner = THIS_MODULE; - tty->driver_name = KBUILD_MODNAME; - tty->name = kasprintf(GFP_KERNEL, KBUILD_MODNAME ".%d.%d.", bus_nr, slot); - if (!tty->name) { + drv->owner = THIS_MODULE; + drv->driver_name = KBUILD_MODNAME; + drv->name = kasprintf(GFP_KERNEL, KBUILD_MODNAME ".%d.%d.", bus_nr, slot); + if (!drv->name) { res = -ENOMEM; goto err_put_driver; } - tty->major = 0; - - tty->minor_start = 0; - tty->type = TTY_DRIVER_TYPE_SERIAL; - tty->subtype = SERIAL_TYPE_NORMAL; - tty->init_termios = tty_std_termios; - tty->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - tty->init_termios.c_ispeed = 9600; - tty->init_termios.c_ospeed = 9600; - - tty_set_operations(tty, &ipoctal_fops); - res = tty_register_driver(tty); + drv->major = 0; + + drv->minor_start = 0; + drv->type = TTY_DRIVER_TYPE_SERIAL; + drv->subtype = SERIAL_TYPE_NORMAL; + drv->init_termios = tty_std_termios; + drv->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + drv->init_termios.c_ispeed = 9600; + drv->init_termios.c_ospeed = 9600; + + tty_set_operations(drv, &ipoctal_fops); + res = tty_register_driver(drv); if (res) { dev_err(&ipoctal->dev->dev, "Can't register tty driver.\n"); goto err_free_name; } /* Save struct tty_driver for use it when uninstalling the device */ - ipoctal->tty_drv = tty; + ipoctal->tty_drv = drv; for (i = 0; i < NR_CHANNELS; i++) { struct device *tty_dev; @@ -407,7 +407,7 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr, spin_lock_init(&channel->lock); channel->pointer_read = 0; channel->pointer_write = 0; - tty_dev = tty_port_register_device_attr(&channel->tty_port, tty, + tty_dev = tty_port_register_device_attr(&channel->tty_port, drv, i, NULL, channel, NULL); if (IS_ERR(tty_dev)) { dev_err(&ipoctal->dev->dev, "Failed to register tty device.\n"); @@ -429,9 +429,9 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr, return 0; err_free_name: - kfree(tty->name); + kfree(drv->name); err_put_driver: - tty_driver_kref_put(tty); + tty_driver_kref_put(drv); return res; } -- cgit v1.2.3-70-g09d2 From 63e8ab610d8ae8413d59bbcd4301af40a2a4f95b Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 4 Sep 2021 02:24:11 +0300 Subject: interconnect: icc-rpm: move bus clocks handling into qnoc_probe All icc-rpm drivers use the same set of bus clocks. Move handling of bus clocks to qnoc_probe. This both simplifies the code and allows using qnoc_probe as device's probe function. Signed-off-by: Dmitry Baryshkov Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Marijn Suijten Tested-by: Shawn Guo Link: https://lore.kernel.org/r/20210903232421.1384199-2-dmitry.baryshkov@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 22 ++++++++++++++-------- drivers/interconnect/qcom/icc-rpm.h | 5 ++--- drivers/interconnect/qcom/msm8916.c | 13 +------------ drivers/interconnect/qcom/msm8939.c | 13 +------------ drivers/interconnect/qcom/qcs404.c | 13 +------------ 5 files changed, 19 insertions(+), 47 deletions(-) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index 54de49ca7808..3049454685dc 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -86,8 +86,11 @@ static int qcom_icc_set(struct icc_node *src, struct icc_node *dst) return 0; } -int qnoc_probe(struct platform_device *pdev, size_t cd_size, int cd_num, - const struct clk_bulk_data *cd) +static const char * const bus_clocks[] = { + "bus", "bus_a", +}; + +int qnoc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; const struct qcom_icc_desc *desc; @@ -97,6 +100,8 @@ int qnoc_probe(struct platform_device *pdev, size_t cd_size, int cd_num, struct qcom_icc_provider *qp; struct icc_node *node; size_t num_nodes, i; + const char * const *cds; + int cd_num; int ret; /* wait for the RPM proxy */ @@ -110,7 +115,10 @@ int qnoc_probe(struct platform_device *pdev, size_t cd_size, int cd_num, qnodes = desc->nodes; num_nodes = desc->num_nodes; - qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL); + cds = bus_clocks; + cd_num = ARRAY_SIZE(bus_clocks); + + qp = devm_kzalloc(dev, struct_size(qp, bus_clks, cd_num), GFP_KERNEL); if (!qp) return -ENOMEM; @@ -119,12 +127,10 @@ int qnoc_probe(struct platform_device *pdev, size_t cd_size, int cd_num, if (!data) return -ENOMEM; - qp->bus_clks = devm_kmemdup(dev, cd, cd_size, - GFP_KERNEL); - if (!qp->bus_clks) - return -ENOMEM; - + for (i = 0; i < cd_num; i++) + qp->bus_clks[i].id = cds[i]; qp->num_clks = cd_num; + ret = devm_clk_bulk_get(dev, qp->num_clks, qp->bus_clks); if (ret) return ret; diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index 79a6f68249c1..f4b05c20c097 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -22,8 +22,8 @@ */ struct qcom_icc_provider { struct icc_provider provider; - struct clk_bulk_data *bus_clks; int num_clks; + struct clk_bulk_data bus_clks[]; }; /** @@ -66,8 +66,7 @@ struct qcom_icc_desc { } -int qnoc_probe(struct platform_device *pdev, size_t cd_size, int cd_num, - const struct clk_bulk_data *cd); +int qnoc_probe(struct platform_device *pdev); int qnoc_remove(struct platform_device *pdev); #endif diff --git a/drivers/interconnect/qcom/msm8916.c b/drivers/interconnect/qcom/msm8916.c index fc3689c8947a..fc0d48d2997a 100644 --- a/drivers/interconnect/qcom/msm8916.c +++ b/drivers/interconnect/qcom/msm8916.c @@ -105,11 +105,6 @@ enum { MSM8916_SNOC_PNOC_SLV, }; -static const struct clk_bulk_data msm8916_bus_clocks[] = { - { .id = "bus" }, - { .id = "bus_a" }, -}; - DEFINE_QNODE(bimc_snoc_mas, MSM8916_BIMC_SNOC_MAS, 8, -1, -1, MSM8916_BIMC_SNOC_SLV); DEFINE_QNODE(bimc_snoc_slv, MSM8916_BIMC_SNOC_SLV, 8, -1, -1, MSM8916_SNOC_INT_0, MSM8916_SNOC_INT_1); DEFINE_QNODE(mas_apss, MSM8916_MASTER_AMPSS_M0, 8, -1, -1, MSM8916_SLAVE_EBI_CH0, MSM8916_BIMC_SNOC_MAS, MSM8916_SLAVE_AMPSS_L2); @@ -305,12 +300,6 @@ static struct qcom_icc_desc msm8916_pcnoc = { .num_nodes = ARRAY_SIZE(msm8916_pcnoc_nodes), }; -static int msm8916_qnoc_probe(struct platform_device *pdev) -{ - return qnoc_probe(pdev, sizeof(msm8916_bus_clocks), - ARRAY_SIZE(msm8916_bus_clocks), msm8916_bus_clocks); -} - static const struct of_device_id msm8916_noc_of_match[] = { { .compatible = "qcom,msm8916-bimc", .data = &msm8916_bimc }, { .compatible = "qcom,msm8916-pcnoc", .data = &msm8916_pcnoc }, @@ -320,7 +309,7 @@ static const struct of_device_id msm8916_noc_of_match[] = { MODULE_DEVICE_TABLE(of, msm8916_noc_of_match); static struct platform_driver msm8916_noc_driver = { - .probe = msm8916_qnoc_probe, + .probe = qnoc_probe, .remove = qnoc_remove, .driver = { .name = "qnoc-msm8916", diff --git a/drivers/interconnect/qcom/msm8939.c b/drivers/interconnect/qcom/msm8939.c index 20f31a1b4192..4a5a2ec64960 100644 --- a/drivers/interconnect/qcom/msm8939.c +++ b/drivers/interconnect/qcom/msm8939.c @@ -110,11 +110,6 @@ enum { MSM8939_SNOC_PNOC_SLV, }; -static const struct clk_bulk_data msm8939_bus_clocks[] = { - { .id = "bus" }, - { .id = "bus_a" }, -}; - DEFINE_QNODE(bimc_snoc_mas, MSM8939_BIMC_SNOC_MAS, 8, -1, -1, MSM8939_BIMC_SNOC_SLV); DEFINE_QNODE(bimc_snoc_slv, MSM8939_BIMC_SNOC_SLV, 16, -1, 2, MSM8939_SNOC_INT_0, MSM8939_SNOC_INT_1); DEFINE_QNODE(mas_apss, MSM8939_MASTER_AMPSS_M0, 16, -1, -1, MSM8939_SLAVE_EBI_CH0, MSM8939_BIMC_SNOC_MAS, MSM8939_SLAVE_AMPSS_L2); @@ -326,12 +321,6 @@ static struct qcom_icc_desc msm8939_pcnoc = { .num_nodes = ARRAY_SIZE(msm8939_pcnoc_nodes), }; -static int msm8939_qnoc_probe(struct platform_device *pdev) -{ - return qnoc_probe(pdev, sizeof(msm8939_bus_clocks), - ARRAY_SIZE(msm8939_bus_clocks), msm8939_bus_clocks); -} - static const struct of_device_id msm8939_noc_of_match[] = { { .compatible = "qcom,msm8939-bimc", .data = &msm8939_bimc }, { .compatible = "qcom,msm8939-pcnoc", .data = &msm8939_pcnoc }, @@ -342,7 +331,7 @@ static const struct of_device_id msm8939_noc_of_match[] = { MODULE_DEVICE_TABLE(of, msm8939_noc_of_match); static struct platform_driver msm8939_noc_driver = { - .probe = msm8939_qnoc_probe, + .probe = qnoc_probe, .remove = qnoc_remove, .driver = { .name = "qnoc-msm8939", diff --git a/drivers/interconnect/qcom/qcs404.c b/drivers/interconnect/qcom/qcs404.c index 36a7e30a00be..0f2fff230b13 100644 --- a/drivers/interconnect/qcom/qcs404.c +++ b/drivers/interconnect/qcom/qcs404.c @@ -92,11 +92,6 @@ enum { QCS404_SLAVE_LPASS, }; -static const struct clk_bulk_data qcs404_bus_clocks[] = { - { .id = "bus" }, - { .id = "bus_a" }, -}; - DEFINE_QNODE(mas_apps_proc, QCS404_MASTER_AMPSS_M0, 8, 0, -1, QCS404_SLAVE_EBI_CH0, QCS404_BIMC_SNOC_SLV); DEFINE_QNODE(mas_oxili, QCS404_MASTER_GRAPHICS_3D, 8, -1, -1, QCS404_SLAVE_EBI_CH0, QCS404_BIMC_SNOC_SLV); DEFINE_QNODE(mas_mdp, QCS404_MASTER_MDP_PORT0, 8, -1, -1, QCS404_SLAVE_EBI_CH0, QCS404_BIMC_SNOC_SLV); @@ -269,12 +264,6 @@ static struct qcom_icc_desc qcs404_snoc = { }; -static int qcs404_qnoc_probe(struct platform_device *pdev) -{ - return qnoc_probe(pdev, sizeof(qcs404_bus_clocks), - ARRAY_SIZE(qcs404_bus_clocks), qcs404_bus_clocks); -} - static const struct of_device_id qcs404_noc_of_match[] = { { .compatible = "qcom,qcs404-bimc", .data = &qcs404_bimc }, { .compatible = "qcom,qcs404-pcnoc", .data = &qcs404_pcnoc }, @@ -284,7 +273,7 @@ static const struct of_device_id qcs404_noc_of_match[] = { MODULE_DEVICE_TABLE(of, qcs404_noc_of_match); static struct platform_driver qcs404_noc_driver = { - .probe = qcs404_qnoc_probe, + .probe = qnoc_probe, .remove = qnoc_remove, .driver = { .name = "qnoc-qcs404", -- cgit v1.2.3-70-g09d2 From 7ae77e60abef3c4f7e7061e02c90dcd469ec881d Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 4 Sep 2021 02:24:12 +0300 Subject: interconnect: sdm660: expand DEFINE_QNODE macros Expand DEFINE_QNODE macros, which with an addition of QoS become an ugly beast with tons of different arguments. While we are at it also move links lists to separate arrays. Suggested-by: Bjorn Andersson Signed-off-by: Dmitry Baryshkov Reviewed-by: AngeloGioacchino Del Regno Tested-by: Marijn Suijten Tested-by: Shawn Guo Link: https://lore.kernel.org/r/20210903232421.1384199-3-dmitry.baryshkov@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/sdm660.c | 1742 +++++++++++++++++++++++++++++++++--- 1 file changed, 1626 insertions(+), 116 deletions(-) diff --git a/drivers/interconnect/qcom/sdm660.c b/drivers/interconnect/qcom/sdm660.c index fb23a5b780a4..652f7cc9cad6 100644 --- a/drivers/interconnect/qcom/sdm660.c +++ b/drivers/interconnect/qcom/sdm660.c @@ -201,8 +201,6 @@ struct qcom_icc_provider { void __iomem *mmio; }; -#define SDM660_MAX_LINKS 34 - /** * struct qcom_icc_qos - Qualcomm specific interconnect QoS parameters * @areq_prio: node requests priority @@ -236,7 +234,7 @@ struct qcom_icc_qos { struct qcom_icc_node { unsigned char *name; u16 id; - u16 links[SDM660_MAX_LINKS]; + const u16 *links; u16 num_links; u16 buswidth; int mas_rpm_id; @@ -251,120 +249,1632 @@ struct qcom_icc_desc { const struct regmap_config *regmap_cfg; }; -#define DEFINE_QNODE(_name, _id, _buswidth, _mas_rpm_id, _slv_rpm_id, \ - _ap_owned, _qos_mode, _qos_prio, _qos_port, ...) \ - static struct qcom_icc_node _name = { \ - .name = #_name, \ - .id = _id, \ - .buswidth = _buswidth, \ - .mas_rpm_id = _mas_rpm_id, \ - .slv_rpm_id = _slv_rpm_id, \ - .qos.ap_owned = _ap_owned, \ - .qos.qos_mode = _qos_mode, \ - .qos.areq_prio = _qos_prio, \ - .qos.prio_level = _qos_prio, \ - .qos.qos_port = _qos_port, \ - .num_links = ARRAY_SIZE(((int[]){ __VA_ARGS__ })), \ - .links = { __VA_ARGS__ }, \ - } +static const u16 mas_ipa_links[] = { + SDM660_SLAVE_A2NOC_SNOC +}; + +static struct qcom_icc_node mas_ipa = { + .name = "mas_ipa", + .id = SDM660_MASTER_IPA, + .buswidth = 8, + .mas_rpm_id = 59, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 3, + .num_links = ARRAY_SIZE(mas_ipa_links), + .links = mas_ipa_links, +}; + +static const u16 mas_cnoc_a2noc_links[] = { + SDM660_SLAVE_A2NOC_SNOC +}; + +static struct qcom_icc_node mas_cnoc_a2noc = { + .name = "mas_cnoc_a2noc", + .id = SDM660_MASTER_CNOC_A2NOC, + .buswidth = 8, + .mas_rpm_id = 146, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(mas_cnoc_a2noc_links), + .links = mas_cnoc_a2noc_links, +}; + +static const u16 mas_sdcc_1_links[] = { + SDM660_SLAVE_A2NOC_SNOC +}; + +static struct qcom_icc_node mas_sdcc_1 = { + .name = "mas_sdcc_1", + .id = SDM660_MASTER_SDCC_1, + .buswidth = 8, + .mas_rpm_id = 33, + .slv_rpm_id = -1, + .qos.ap_owned = false, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(mas_sdcc_1_links), + .links = mas_sdcc_1_links, +}; + +static const u16 mas_sdcc_2_links[] = { + SDM660_SLAVE_A2NOC_SNOC +}; + +static struct qcom_icc_node mas_sdcc_2 = { + .name = "mas_sdcc_2", + .id = SDM660_MASTER_SDCC_2, + .buswidth = 8, + .mas_rpm_id = 35, + .slv_rpm_id = -1, + .qos.ap_owned = false, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(mas_sdcc_2_links), + .links = mas_sdcc_2_links, +}; + +static const u16 mas_blsp_1_links[] = { + SDM660_SLAVE_A2NOC_SNOC +}; + +static struct qcom_icc_node mas_blsp_1 = { + .name = "mas_blsp_1", + .id = SDM660_MASTER_BLSP_1, + .buswidth = 4, + .mas_rpm_id = 41, + .slv_rpm_id = -1, + .qos.ap_owned = false, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(mas_blsp_1_links), + .links = mas_blsp_1_links, +}; + +static const u16 mas_blsp_2_links[] = { + SDM660_SLAVE_A2NOC_SNOC +}; + +static struct qcom_icc_node mas_blsp_2 = { + .name = "mas_blsp_2", + .id = SDM660_MASTER_BLSP_2, + .buswidth = 4, + .mas_rpm_id = 39, + .slv_rpm_id = -1, + .qos.ap_owned = false, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(mas_blsp_2_links), + .links = mas_blsp_2_links, +}; + +static const u16 mas_ufs_links[] = { + SDM660_SLAVE_A2NOC_SNOC +}; + +static struct qcom_icc_node mas_ufs = { + .name = "mas_ufs", + .id = SDM660_MASTER_UFS, + .buswidth = 8, + .mas_rpm_id = 68, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 4, + .num_links = ARRAY_SIZE(mas_ufs_links), + .links = mas_ufs_links, +}; + +static const u16 mas_usb_hs_links[] = { + SDM660_SLAVE_A2NOC_SNOC +}; + +static struct qcom_icc_node mas_usb_hs = { + .name = "mas_usb_hs", + .id = SDM660_MASTER_USB_HS, + .buswidth = 8, + .mas_rpm_id = 42, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 1, + .num_links = ARRAY_SIZE(mas_usb_hs_links), + .links = mas_usb_hs_links, +}; + +static const u16 mas_usb3_links[] = { + SDM660_SLAVE_A2NOC_SNOC +}; + +static struct qcom_icc_node mas_usb3 = { + .name = "mas_usb3", + .id = SDM660_MASTER_USB3, + .buswidth = 8, + .mas_rpm_id = 32, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 2, + .num_links = ARRAY_SIZE(mas_usb3_links), + .links = mas_usb3_links, +}; + +static const u16 mas_crypto_links[] = { + SDM660_SLAVE_A2NOC_SNOC +}; + +static struct qcom_icc_node mas_crypto = { + .name = "mas_crypto", + .id = SDM660_MASTER_CRYPTO_C0, + .buswidth = 8, + .mas_rpm_id = 23, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 11, + .num_links = ARRAY_SIZE(mas_crypto_links), + .links = mas_crypto_links, +}; + +static const u16 mas_gnoc_bimc_links[] = { + SDM660_SLAVE_EBI +}; + +static struct qcom_icc_node mas_gnoc_bimc = { + .name = "mas_gnoc_bimc", + .id = SDM660_MASTER_GNOC_BIMC, + .buswidth = 4, + .mas_rpm_id = 144, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 0, + .num_links = ARRAY_SIZE(mas_gnoc_bimc_links), + .links = mas_gnoc_bimc_links, +}; + +static const u16 mas_oxili_links[] = { + SDM660_SLAVE_HMSS_L3, + SDM660_SLAVE_EBI, + SDM660_SLAVE_BIMC_SNOC +}; + +static struct qcom_icc_node mas_oxili = { + .name = "mas_oxili", + .id = SDM660_MASTER_OXILI, + .buswidth = 4, + .mas_rpm_id = 6, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 1, + .num_links = ARRAY_SIZE(mas_oxili_links), + .links = mas_oxili_links, +}; + +static const u16 mas_mnoc_bimc_links[] = { + SDM660_SLAVE_HMSS_L3, + SDM660_SLAVE_EBI, + SDM660_SLAVE_BIMC_SNOC +}; + +static struct qcom_icc_node mas_mnoc_bimc = { + .name = "mas_mnoc_bimc", + .id = SDM660_MASTER_MNOC_BIMC, + .buswidth = 4, + .mas_rpm_id = 2, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 2, + .num_links = ARRAY_SIZE(mas_mnoc_bimc_links), + .links = mas_mnoc_bimc_links, +}; + +static const u16 mas_snoc_bimc_links[] = { + SDM660_SLAVE_HMSS_L3, + SDM660_SLAVE_EBI +}; + +static struct qcom_icc_node mas_snoc_bimc = { + .name = "mas_snoc_bimc", + .id = SDM660_MASTER_SNOC_BIMC, + .buswidth = 4, + .mas_rpm_id = 3, + .slv_rpm_id = -1, + .qos.ap_owned = false, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(mas_snoc_bimc_links), + .links = mas_snoc_bimc_links, +}; + +static const u16 mas_pimem_links[] = { + SDM660_SLAVE_HMSS_L3, + SDM660_SLAVE_EBI +}; + +static struct qcom_icc_node mas_pimem = { + .name = "mas_pimem", + .id = SDM660_MASTER_PIMEM, + .buswidth = 4, + .mas_rpm_id = 113, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 4, + .num_links = ARRAY_SIZE(mas_pimem_links), + .links = mas_pimem_links, +}; + +static const u16 mas_snoc_cnoc_links[] = { + SDM660_SLAVE_CLK_CTL, + SDM660_SLAVE_QDSS_CFG, + SDM660_SLAVE_QM_CFG, + SDM660_SLAVE_SRVC_CNOC, + SDM660_SLAVE_UFS_CFG, + SDM660_SLAVE_TCSR, + SDM660_SLAVE_A2NOC_SMMU_CFG, + SDM660_SLAVE_SNOC_CFG, + SDM660_SLAVE_TLMM_SOUTH, + SDM660_SLAVE_MPM, + SDM660_SLAVE_CNOC_MNOC_MMSS_CFG, + SDM660_SLAVE_SDCC_2, + SDM660_SLAVE_SDCC_1, + SDM660_SLAVE_SPDM, + SDM660_SLAVE_PMIC_ARB, + SDM660_SLAVE_PRNG, + SDM660_SLAVE_MSS_CFG, + SDM660_SLAVE_GPUSS_CFG, + SDM660_SLAVE_IMEM_CFG, + SDM660_SLAVE_USB3_0, + SDM660_SLAVE_A2NOC_CFG, + SDM660_SLAVE_TLMM_NORTH, + SDM660_SLAVE_USB_HS, + SDM660_SLAVE_PDM, + SDM660_SLAVE_TLMM_CENTER, + SDM660_SLAVE_AHB2PHY, + SDM660_SLAVE_BLSP_2, + SDM660_SLAVE_BLSP_1, + SDM660_SLAVE_PIMEM_CFG, + SDM660_SLAVE_GLM, + SDM660_SLAVE_MESSAGE_RAM, + SDM660_SLAVE_BIMC_CFG, + SDM660_SLAVE_CNOC_MNOC_CFG +}; + +static struct qcom_icc_node mas_snoc_cnoc = { + .name = "mas_snoc_cnoc", + .id = SDM660_MASTER_SNOC_CNOC, + .buswidth = 8, + .mas_rpm_id = 52, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(mas_snoc_cnoc_links), + .links = mas_snoc_cnoc_links, +}; + +static const u16 mas_qdss_dap_links[] = { + SDM660_SLAVE_CLK_CTL, + SDM660_SLAVE_QDSS_CFG, + SDM660_SLAVE_QM_CFG, + SDM660_SLAVE_SRVC_CNOC, + SDM660_SLAVE_UFS_CFG, + SDM660_SLAVE_TCSR, + SDM660_SLAVE_A2NOC_SMMU_CFG, + SDM660_SLAVE_SNOC_CFG, + SDM660_SLAVE_TLMM_SOUTH, + SDM660_SLAVE_MPM, + SDM660_SLAVE_CNOC_MNOC_MMSS_CFG, + SDM660_SLAVE_SDCC_2, + SDM660_SLAVE_SDCC_1, + SDM660_SLAVE_SPDM, + SDM660_SLAVE_PMIC_ARB, + SDM660_SLAVE_PRNG, + SDM660_SLAVE_MSS_CFG, + SDM660_SLAVE_GPUSS_CFG, + SDM660_SLAVE_IMEM_CFG, + SDM660_SLAVE_USB3_0, + SDM660_SLAVE_A2NOC_CFG, + SDM660_SLAVE_TLMM_NORTH, + SDM660_SLAVE_USB_HS, + SDM660_SLAVE_PDM, + SDM660_SLAVE_TLMM_CENTER, + SDM660_SLAVE_AHB2PHY, + SDM660_SLAVE_BLSP_2, + SDM660_SLAVE_BLSP_1, + SDM660_SLAVE_PIMEM_CFG, + SDM660_SLAVE_GLM, + SDM660_SLAVE_MESSAGE_RAM, + SDM660_SLAVE_CNOC_A2NOC, + SDM660_SLAVE_BIMC_CFG, + SDM660_SLAVE_CNOC_MNOC_CFG +}; + +static struct qcom_icc_node mas_qdss_dap = { + .name = "mas_qdss_dap", + .id = SDM660_MASTER_QDSS_DAP, + .buswidth = 8, + .mas_rpm_id = 49, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(mas_qdss_dap_links), + .links = mas_qdss_dap_links, +}; + +static const u16 mas_apss_proc_links[] = { + SDM660_SLAVE_GNOC_SNOC, + SDM660_SLAVE_GNOC_BIMC +}; + +static struct qcom_icc_node mas_apss_proc = { + .name = "mas_apss_proc", + .id = SDM660_MASTER_APPS_PROC, + .buswidth = 16, + .mas_rpm_id = 0, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(mas_apss_proc_links), + .links = mas_apss_proc_links, +}; + +static const u16 mas_cnoc_mnoc_mmss_cfg_links[] = { + SDM660_SLAVE_VENUS_THROTTLE_CFG, + SDM660_SLAVE_VENUS_CFG, + SDM660_SLAVE_CAMERA_THROTTLE_CFG, + SDM660_SLAVE_SMMU_CFG, + SDM660_SLAVE_CAMERA_CFG, + SDM660_SLAVE_CSI_PHY_CFG, + SDM660_SLAVE_DISPLAY_THROTTLE_CFG, + SDM660_SLAVE_DISPLAY_CFG, + SDM660_SLAVE_MMSS_CLK_CFG, + SDM660_SLAVE_MNOC_MPU_CFG, + SDM660_SLAVE_MISC_CFG, + SDM660_SLAVE_MMSS_CLK_XPU_CFG +}; + +static struct qcom_icc_node mas_cnoc_mnoc_mmss_cfg = { + .name = "mas_cnoc_mnoc_mmss_cfg", + .id = SDM660_MASTER_CNOC_MNOC_MMSS_CFG, + .buswidth = 8, + .mas_rpm_id = 4, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(mas_cnoc_mnoc_mmss_cfg_links), + .links = mas_cnoc_mnoc_mmss_cfg_links, +}; + +static const u16 mas_cnoc_mnoc_cfg_links[] = { + SDM660_SLAVE_SRVC_MNOC +}; + +static struct qcom_icc_node mas_cnoc_mnoc_cfg = { + .name = "mas_cnoc_mnoc_cfg", + .id = SDM660_MASTER_CNOC_MNOC_CFG, + .buswidth = 4, + .mas_rpm_id = 5, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(mas_cnoc_mnoc_cfg_links), + .links = mas_cnoc_mnoc_cfg_links, +}; + +static const u16 mas_cpp_links[] = { + SDM660_SLAVE_MNOC_BIMC +}; + +static struct qcom_icc_node mas_cpp = { + .name = "mas_cpp", + .id = SDM660_MASTER_CPP, + .buswidth = 16, + .mas_rpm_id = 115, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 4, + .num_links = ARRAY_SIZE(mas_cpp_links), + .links = mas_cpp_links, +}; + +static const u16 mas_jpeg_links[] = { + SDM660_SLAVE_MNOC_BIMC +}; + +static struct qcom_icc_node mas_jpeg = { + .name = "mas_jpeg", + .id = SDM660_MASTER_JPEG, + .buswidth = 16, + .mas_rpm_id = 7, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 6, + .num_links = ARRAY_SIZE(mas_jpeg_links), + .links = mas_jpeg_links, +}; + +static const u16 mas_mdp_p0_links[] = { + SDM660_SLAVE_MNOC_BIMC +}; + +static struct qcom_icc_node mas_mdp_p0 = { + .name = "mas_mdp_p0", + .id = SDM660_MASTER_MDP_P0, + .buswidth = 16, + .mas_rpm_id = 8, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 0, + .num_links = ARRAY_SIZE(mas_mdp_p0_links), + .links = mas_mdp_p0_links, +}; + +static const u16 mas_mdp_p1_links[] = { + SDM660_SLAVE_MNOC_BIMC +}; + +static struct qcom_icc_node mas_mdp_p1 = { + .name = "mas_mdp_p1", + .id = SDM660_MASTER_MDP_P1, + .buswidth = 16, + .mas_rpm_id = 61, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 1, + .num_links = ARRAY_SIZE(mas_mdp_p1_links), + .links = mas_mdp_p1_links, +}; + +static const u16 mas_venus_links[] = { + SDM660_SLAVE_MNOC_BIMC +}; + +static struct qcom_icc_node mas_venus = { + .name = "mas_venus", + .id = SDM660_MASTER_VENUS, + .buswidth = 16, + .mas_rpm_id = 9, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 1, + .num_links = ARRAY_SIZE(mas_venus_links), + .links = mas_venus_links, +}; + +static const u16 mas_vfe_links[] = { + SDM660_SLAVE_MNOC_BIMC +}; + +static struct qcom_icc_node mas_vfe = { + .name = "mas_vfe", + .id = SDM660_MASTER_VFE, + .buswidth = 16, + .mas_rpm_id = 11, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 5, + .num_links = ARRAY_SIZE(mas_vfe_links), + .links = mas_vfe_links, +}; + +static const u16 mas_qdss_etr_links[] = { + SDM660_SLAVE_PIMEM, + SDM660_SLAVE_IMEM, + SDM660_SLAVE_SNOC_CNOC, + SDM660_SLAVE_SNOC_BIMC +}; + +static struct qcom_icc_node mas_qdss_etr = { + .name = "mas_qdss_etr", + .id = SDM660_MASTER_QDSS_ETR, + .buswidth = 8, + .mas_rpm_id = 31, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 1, + .num_links = ARRAY_SIZE(mas_qdss_etr_links), + .links = mas_qdss_etr_links, +}; + +static const u16 mas_qdss_bam_links[] = { + SDM660_SLAVE_PIMEM, + SDM660_SLAVE_IMEM, + SDM660_SLAVE_SNOC_CNOC, + SDM660_SLAVE_SNOC_BIMC +}; + +static struct qcom_icc_node mas_qdss_bam = { + .name = "mas_qdss_bam", + .id = SDM660_MASTER_QDSS_BAM, + .buswidth = 4, + .mas_rpm_id = 19, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 0, + .num_links = ARRAY_SIZE(mas_qdss_bam_links), + .links = mas_qdss_bam_links, +}; + +static const u16 mas_snoc_cfg_links[] = { + SDM660_SLAVE_SRVC_SNOC +}; + +static struct qcom_icc_node mas_snoc_cfg = { + .name = "mas_snoc_cfg", + .id = SDM660_MASTER_SNOC_CFG, + .buswidth = 4, + .mas_rpm_id = 20, + .slv_rpm_id = -1, + .qos.ap_owned = false, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(mas_snoc_cfg_links), + .links = mas_snoc_cfg_links, +}; + +static const u16 mas_bimc_snoc_links[] = { + SDM660_SLAVE_PIMEM, + SDM660_SLAVE_IPA, + SDM660_SLAVE_QDSS_STM, + SDM660_SLAVE_LPASS, + SDM660_SLAVE_HMSS, + SDM660_SLAVE_CDSP, + SDM660_SLAVE_SNOC_CNOC, + SDM660_SLAVE_WLAN, + SDM660_SLAVE_IMEM +}; + +static struct qcom_icc_node mas_bimc_snoc = { + .name = "mas_bimc_snoc", + .id = SDM660_MASTER_BIMC_SNOC, + .buswidth = 8, + .mas_rpm_id = 21, + .slv_rpm_id = -1, + .qos.ap_owned = false, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(mas_bimc_snoc_links), + .links = mas_bimc_snoc_links, +}; + +static const u16 mas_gnoc_snoc_links[] = { + SDM660_SLAVE_PIMEM, + SDM660_SLAVE_IPA, + SDM660_SLAVE_QDSS_STM, + SDM660_SLAVE_LPASS, + SDM660_SLAVE_HMSS, + SDM660_SLAVE_CDSP, + SDM660_SLAVE_SNOC_CNOC, + SDM660_SLAVE_WLAN, + SDM660_SLAVE_IMEM +}; + +static struct qcom_icc_node mas_gnoc_snoc = { + .name = "mas_gnoc_snoc", + .id = SDM660_MASTER_GNOC_SNOC, + .buswidth = 8, + .mas_rpm_id = 150, + .slv_rpm_id = -1, + .qos.ap_owned = false, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(mas_gnoc_snoc_links), + .links = mas_gnoc_snoc_links, +}; + +static const u16 mas_a2noc_snoc_links[] = { + SDM660_SLAVE_PIMEM, + SDM660_SLAVE_IPA, + SDM660_SLAVE_QDSS_STM, + SDM660_SLAVE_LPASS, + SDM660_SLAVE_HMSS, + SDM660_SLAVE_SNOC_BIMC, + SDM660_SLAVE_CDSP, + SDM660_SLAVE_SNOC_CNOC, + SDM660_SLAVE_WLAN, + SDM660_SLAVE_IMEM +}; + +static struct qcom_icc_node mas_a2noc_snoc = { + .name = "mas_a2noc_snoc", + .id = SDM660_MASTER_A2NOC_SNOC, + .buswidth = 16, + .mas_rpm_id = 112, + .slv_rpm_id = -1, + .qos.ap_owned = false, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(mas_a2noc_snoc_links), + .links = mas_a2noc_snoc_links, +}; + +static const u16 slv_a2noc_snoc_links[] = { + SDM660_MASTER_A2NOC_SNOC +}; + +static struct qcom_icc_node slv_a2noc_snoc = { + .name = "slv_a2noc_snoc", + .id = SDM660_SLAVE_A2NOC_SNOC, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 143, + .qos.ap_owned = false, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(slv_a2noc_snoc_links), + .links = slv_a2noc_snoc_links, +}; + +static struct qcom_icc_node slv_ebi = { + .name = "slv_ebi", + .id = SDM660_SLAVE_EBI, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 0, + .qos.ap_owned = false, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_hmss_l3 = { + .name = "slv_hmss_l3", + .id = SDM660_SLAVE_HMSS_L3, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 160, + .qos.ap_owned = false, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static const u16 slv_bimc_snoc_links[] = { + SDM660_MASTER_BIMC_SNOC +}; + +static struct qcom_icc_node slv_bimc_snoc = { + .name = "slv_bimc_snoc", + .id = SDM660_SLAVE_BIMC_SNOC, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 2, + .qos.ap_owned = false, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(slv_bimc_snoc_links), + .links = slv_bimc_snoc_links, +}; + +static const u16 slv_cnoc_a2noc_links[] = { + SDM660_MASTER_CNOC_A2NOC +}; + +static struct qcom_icc_node slv_cnoc_a2noc = { + .name = "slv_cnoc_a2noc", + .id = SDM660_SLAVE_CNOC_A2NOC, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 208, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(slv_cnoc_a2noc_links), + .links = slv_cnoc_a2noc_links, +}; + +static struct qcom_icc_node slv_mpm = { + .name = "slv_mpm", + .id = SDM660_SLAVE_MPM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 62, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_pmic_arb = { + .name = "slv_pmic_arb", + .id = SDM660_SLAVE_PMIC_ARB, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 59, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_tlmm_north = { + .name = "slv_tlmm_north", + .id = SDM660_SLAVE_TLMM_NORTH, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 214, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_tcsr = { + .name = "slv_tcsr", + .id = SDM660_SLAVE_TCSR, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 50, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_pimem_cfg = { + .name = "slv_pimem_cfg", + .id = SDM660_SLAVE_PIMEM_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 167, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_imem_cfg = { + .name = "slv_imem_cfg", + .id = SDM660_SLAVE_IMEM_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 54, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_message_ram = { + .name = "slv_message_ram", + .id = SDM660_SLAVE_MESSAGE_RAM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 55, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_glm = { + .name = "slv_glm", + .id = SDM660_SLAVE_GLM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 209, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_bimc_cfg = { + .name = "slv_bimc_cfg", + .id = SDM660_SLAVE_BIMC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 56, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_prng = { + .name = "slv_prng", + .id = SDM660_SLAVE_PRNG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 44, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_spdm = { + .name = "slv_spdm", + .id = SDM660_SLAVE_SPDM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 60, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_qdss_cfg = { + .name = "slv_qdss_cfg", + .id = SDM660_SLAVE_QDSS_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 63, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static const u16 slv_cnoc_mnoc_cfg_links[] = { + SDM660_MASTER_CNOC_MNOC_CFG +}; + +static struct qcom_icc_node slv_cnoc_mnoc_cfg = { + .name = "slv_cnoc_mnoc_cfg", + .id = SDM660_SLAVE_CNOC_MNOC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 66, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(slv_cnoc_mnoc_cfg_links), + .links = slv_cnoc_mnoc_cfg_links, +}; + +static struct qcom_icc_node slv_snoc_cfg = { + .name = "slv_snoc_cfg", + .id = SDM660_SLAVE_SNOC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 70, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_qm_cfg = { + .name = "slv_qm_cfg", + .id = SDM660_SLAVE_QM_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 212, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_clk_ctl = { + .name = "slv_clk_ctl", + .id = SDM660_SLAVE_CLK_CTL, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 47, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_mss_cfg = { + .name = "slv_mss_cfg", + .id = SDM660_SLAVE_MSS_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 48, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_tlmm_south = { + .name = "slv_tlmm_south", + .id = SDM660_SLAVE_TLMM_SOUTH, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 217, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_ufs_cfg = { + .name = "slv_ufs_cfg", + .id = SDM660_SLAVE_UFS_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 92, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_a2noc_cfg = { + .name = "slv_a2noc_cfg", + .id = SDM660_SLAVE_A2NOC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 150, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_a2noc_smmu_cfg = { + .name = "slv_a2noc_smmu_cfg", + .id = SDM660_SLAVE_A2NOC_SMMU_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 152, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_gpuss_cfg = { + .name = "slv_gpuss_cfg", + .id = SDM660_SLAVE_GPUSS_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 11, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_ahb2phy = { + .name = "slv_ahb2phy", + .id = SDM660_SLAVE_AHB2PHY, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 163, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_blsp_1 = { + .name = "slv_blsp_1", + .id = SDM660_SLAVE_BLSP_1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 39, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_sdcc_1 = { + .name = "slv_sdcc_1", + .id = SDM660_SLAVE_SDCC_1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 31, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_sdcc_2 = { + .name = "slv_sdcc_2", + .id = SDM660_SLAVE_SDCC_2, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 33, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_tlmm_center = { + .name = "slv_tlmm_center", + .id = SDM660_SLAVE_TLMM_CENTER, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 218, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_blsp_2 = { + .name = "slv_blsp_2", + .id = SDM660_SLAVE_BLSP_2, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 37, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_pdm = { + .name = "slv_pdm", + .id = SDM660_SLAVE_PDM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 41, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static const u16 slv_cnoc_mnoc_mmss_cfg_links[] = { + SDM660_MASTER_CNOC_MNOC_MMSS_CFG +}; + +static struct qcom_icc_node slv_cnoc_mnoc_mmss_cfg = { + .name = "slv_cnoc_mnoc_mmss_cfg", + .id = SDM660_SLAVE_CNOC_MNOC_MMSS_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 58, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(slv_cnoc_mnoc_mmss_cfg_links), + .links = slv_cnoc_mnoc_mmss_cfg_links, +}; + +static struct qcom_icc_node slv_usb_hs = { + .name = "slv_usb_hs", + .id = SDM660_SLAVE_USB_HS, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 40, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_usb3_0 = { + .name = "slv_usb3_0", + .id = SDM660_SLAVE_USB3_0, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 22, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_srvc_cnoc = { + .name = "slv_srvc_cnoc", + .id = SDM660_SLAVE_SRVC_CNOC, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 76, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static const u16 slv_gnoc_bimc_links[] = { + SDM660_MASTER_GNOC_BIMC +}; + +static struct qcom_icc_node slv_gnoc_bimc = { + .name = "slv_gnoc_bimc", + .id = SDM660_SLAVE_GNOC_BIMC, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 210, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(slv_gnoc_bimc_links), + .links = slv_gnoc_bimc_links, +}; + +static const u16 slv_gnoc_snoc_links[] = { + SDM660_MASTER_GNOC_SNOC +}; + +static struct qcom_icc_node slv_gnoc_snoc = { + .name = "slv_gnoc_snoc", + .id = SDM660_SLAVE_GNOC_SNOC, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 211, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(slv_gnoc_snoc_links), + .links = slv_gnoc_snoc_links, +}; + +static struct qcom_icc_node slv_camera_cfg = { + .name = "slv_camera_cfg", + .id = SDM660_SLAVE_CAMERA_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 3, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_camera_throttle_cfg = { + .name = "slv_camera_throttle_cfg", + .id = SDM660_SLAVE_CAMERA_THROTTLE_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 154, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_misc_cfg = { + .name = "slv_misc_cfg", + .id = SDM660_SLAVE_MISC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 8, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; -DEFINE_QNODE(mas_ipa, SDM660_MASTER_IPA, 8, 59, -1, true, NOC_QOS_MODE_FIXED, 1, 3, SDM660_SLAVE_A2NOC_SNOC); -DEFINE_QNODE(mas_cnoc_a2noc, SDM660_MASTER_CNOC_A2NOC, 8, 146, -1, true, -1, 0, -1, SDM660_SLAVE_A2NOC_SNOC); -DEFINE_QNODE(mas_sdcc_1, SDM660_MASTER_SDCC_1, 8, 33, -1, false, -1, 0, -1, SDM660_SLAVE_A2NOC_SNOC); -DEFINE_QNODE(mas_sdcc_2, SDM660_MASTER_SDCC_2, 8, 35, -1, false, -1, 0, -1, SDM660_SLAVE_A2NOC_SNOC); -DEFINE_QNODE(mas_blsp_1, SDM660_MASTER_BLSP_1, 4, 41, -1, false, -1, 0, -1, SDM660_SLAVE_A2NOC_SNOC); -DEFINE_QNODE(mas_blsp_2, SDM660_MASTER_BLSP_2, 4, 39, -1, false, -1, 0, -1, SDM660_SLAVE_A2NOC_SNOC); -DEFINE_QNODE(mas_ufs, SDM660_MASTER_UFS, 8, 68, -1, true, NOC_QOS_MODE_FIXED, 1, 4, SDM660_SLAVE_A2NOC_SNOC); -DEFINE_QNODE(mas_usb_hs, SDM660_MASTER_USB_HS, 8, 42, -1, true, NOC_QOS_MODE_FIXED, 1, 1, SDM660_SLAVE_A2NOC_SNOC); -DEFINE_QNODE(mas_usb3, SDM660_MASTER_USB3, 8, 32, -1, true, NOC_QOS_MODE_FIXED, 1, 2, SDM660_SLAVE_A2NOC_SNOC); -DEFINE_QNODE(mas_crypto, SDM660_MASTER_CRYPTO_C0, 8, 23, -1, true, NOC_QOS_MODE_FIXED, 1, 11, SDM660_SLAVE_A2NOC_SNOC); -DEFINE_QNODE(mas_gnoc_bimc, SDM660_MASTER_GNOC_BIMC, 4, 144, -1, true, NOC_QOS_MODE_FIXED, 0, 0, SDM660_SLAVE_EBI); -DEFINE_QNODE(mas_oxili, SDM660_MASTER_OXILI, 4, 6, -1, true, NOC_QOS_MODE_BYPASS, 0, 1, SDM660_SLAVE_HMSS_L3, SDM660_SLAVE_EBI, SDM660_SLAVE_BIMC_SNOC); -DEFINE_QNODE(mas_mnoc_bimc, SDM660_MASTER_MNOC_BIMC, 4, 2, -1, true, NOC_QOS_MODE_BYPASS, 0, 2, SDM660_SLAVE_HMSS_L3, SDM660_SLAVE_EBI, SDM660_SLAVE_BIMC_SNOC); -DEFINE_QNODE(mas_snoc_bimc, SDM660_MASTER_SNOC_BIMC, 4, 3, -1, false, -1, 0, -1, SDM660_SLAVE_HMSS_L3, SDM660_SLAVE_EBI); -DEFINE_QNODE(mas_pimem, SDM660_MASTER_PIMEM, 4, 113, -1, true, NOC_QOS_MODE_FIXED, 1, 4, SDM660_SLAVE_HMSS_L3, SDM660_SLAVE_EBI); -DEFINE_QNODE(mas_snoc_cnoc, SDM660_MASTER_SNOC_CNOC, 8, 52, -1, true, -1, 0, -1, SDM660_SLAVE_CLK_CTL, SDM660_SLAVE_QDSS_CFG, SDM660_SLAVE_QM_CFG, SDM660_SLAVE_SRVC_CNOC, SDM660_SLAVE_UFS_CFG, SDM660_SLAVE_TCSR, SDM660_SLAVE_A2NOC_SMMU_CFG, SDM660_SLAVE_SNOC_CFG, SDM660_SLAVE_TLMM_SOUTH, SDM660_SLAVE_MPM, SDM660_SLAVE_CNOC_MNOC_MMSS_CFG, SDM660_SLAVE_SDCC_2, SDM660_SLAVE_SDCC_1, SDM660_SLAVE_SPDM, SDM660_SLAVE_PMIC_ARB, SDM660_SLAVE_PRNG, SDM660_SLAVE_MSS_CFG, SDM660_SLAVE_GPUSS_CFG, SDM660_SLAVE_IMEM_CFG, SDM660_SLAVE_USB3_0, SDM660_SLAVE_A2NOC_CFG, SDM660_SLAVE_TLMM_NORTH, SDM660_SLAVE_USB_HS, SDM660_SLAVE_PDM, SDM660_SLAVE_TLMM_CENTER, SDM660_SLAVE_AHB2PHY, SDM660_SLAVE_BLSP_2, SDM660_SLAVE_BLSP_1, SDM660_SLAVE_PIMEM_CFG, SDM660_SLAVE_GLM, SDM660_SLAVE_MESSAGE_RAM, SDM660_SLAVE_BIMC_CFG, SDM660_SLAVE_CNOC_MNOC_CFG); -DEFINE_QNODE(mas_qdss_dap, SDM660_MASTER_QDSS_DAP, 8, 49, -1, true, -1, 0, -1, SDM660_SLAVE_CLK_CTL, SDM660_SLAVE_QDSS_CFG, SDM660_SLAVE_QM_CFG, SDM660_SLAVE_SRVC_CNOC, SDM660_SLAVE_UFS_CFG, SDM660_SLAVE_TCSR, SDM660_SLAVE_A2NOC_SMMU_CFG, SDM660_SLAVE_SNOC_CFG, SDM660_SLAVE_TLMM_SOUTH, SDM660_SLAVE_MPM, SDM660_SLAVE_CNOC_MNOC_MMSS_CFG, SDM660_SLAVE_SDCC_2, SDM660_SLAVE_SDCC_1, SDM660_SLAVE_SPDM, SDM660_SLAVE_PMIC_ARB, SDM660_SLAVE_PRNG, SDM660_SLAVE_MSS_CFG, SDM660_SLAVE_GPUSS_CFG, SDM660_SLAVE_IMEM_CFG, SDM660_SLAVE_USB3_0, SDM660_SLAVE_A2NOC_CFG, SDM660_SLAVE_TLMM_NORTH, SDM660_SLAVE_USB_HS, SDM660_SLAVE_PDM, SDM660_SLAVE_TLMM_CENTER, SDM660_SLAVE_AHB2PHY, SDM660_SLAVE_BLSP_2, SDM660_SLAVE_BLSP_1, SDM660_SLAVE_PIMEM_CFG, SDM660_SLAVE_GLM, SDM660_SLAVE_MESSAGE_RAM, SDM660_SLAVE_CNOC_A2NOC, SDM660_SLAVE_BIMC_CFG, SDM660_SLAVE_CNOC_MNOC_CFG); -DEFINE_QNODE(mas_apss_proc, SDM660_MASTER_APPS_PROC, 16, 0, -1, true, -1, 0, -1, SDM660_SLAVE_GNOC_SNOC, SDM660_SLAVE_GNOC_BIMC); -DEFINE_QNODE(mas_cnoc_mnoc_mmss_cfg, SDM660_MASTER_CNOC_MNOC_MMSS_CFG, 8, 4, -1, true, -1, 0, -1, SDM660_SLAVE_VENUS_THROTTLE_CFG, SDM660_SLAVE_VENUS_CFG, SDM660_SLAVE_CAMERA_THROTTLE_CFG, SDM660_SLAVE_SMMU_CFG, SDM660_SLAVE_CAMERA_CFG, SDM660_SLAVE_CSI_PHY_CFG, SDM660_SLAVE_DISPLAY_THROTTLE_CFG, SDM660_SLAVE_DISPLAY_CFG, SDM660_SLAVE_MMSS_CLK_CFG, SDM660_SLAVE_MNOC_MPU_CFG, SDM660_SLAVE_MISC_CFG, SDM660_SLAVE_MMSS_CLK_XPU_CFG); -DEFINE_QNODE(mas_cnoc_mnoc_cfg, SDM660_MASTER_CNOC_MNOC_CFG, 4, 5, -1, true, -1, 0, -1, SDM660_SLAVE_SRVC_MNOC); -DEFINE_QNODE(mas_cpp, SDM660_MASTER_CPP, 16, 115, -1, true, NOC_QOS_MODE_BYPASS, 0, 4, SDM660_SLAVE_MNOC_BIMC); -DEFINE_QNODE(mas_jpeg, SDM660_MASTER_JPEG, 16, 7, -1, true, NOC_QOS_MODE_BYPASS, 0, 6, SDM660_SLAVE_MNOC_BIMC); -DEFINE_QNODE(mas_mdp_p0, SDM660_MASTER_MDP_P0, 16, 8, -1, true, NOC_QOS_MODE_BYPASS, 0, 0, SDM660_SLAVE_MNOC_BIMC); /* vrail-comp???? */ -DEFINE_QNODE(mas_mdp_p1, SDM660_MASTER_MDP_P1, 16, 61, -1, true, NOC_QOS_MODE_BYPASS, 0, 1, SDM660_SLAVE_MNOC_BIMC); /* vrail-comp??? */ -DEFINE_QNODE(mas_venus, SDM660_MASTER_VENUS, 16, 9, -1, true, NOC_QOS_MODE_BYPASS, 0, 1, SDM660_SLAVE_MNOC_BIMC); -DEFINE_QNODE(mas_vfe, SDM660_MASTER_VFE, 16, 11, -1, true, NOC_QOS_MODE_BYPASS, 0, 5, SDM660_SLAVE_MNOC_BIMC); -DEFINE_QNODE(mas_qdss_etr, SDM660_MASTER_QDSS_ETR, 8, 31, -1, true, NOC_QOS_MODE_FIXED, 1, 1, SDM660_SLAVE_PIMEM, SDM660_SLAVE_IMEM, SDM660_SLAVE_SNOC_CNOC, SDM660_SLAVE_SNOC_BIMC); -DEFINE_QNODE(mas_qdss_bam, SDM660_MASTER_QDSS_BAM, 4, 19, -1, true, NOC_QOS_MODE_FIXED, 1, 0, SDM660_SLAVE_PIMEM, SDM660_SLAVE_IMEM, SDM660_SLAVE_SNOC_CNOC, SDM660_SLAVE_SNOC_BIMC); -DEFINE_QNODE(mas_snoc_cfg, SDM660_MASTER_SNOC_CFG, 4, 20, -1, false, -1, 0, -1, SDM660_SLAVE_SRVC_SNOC); -DEFINE_QNODE(mas_bimc_snoc, SDM660_MASTER_BIMC_SNOC, 8, 21, -1, false, -1, 0, -1, SDM660_SLAVE_PIMEM, SDM660_SLAVE_IPA, SDM660_SLAVE_QDSS_STM, SDM660_SLAVE_LPASS, SDM660_SLAVE_HMSS, SDM660_SLAVE_CDSP, SDM660_SLAVE_SNOC_CNOC, SDM660_SLAVE_WLAN, SDM660_SLAVE_IMEM); -DEFINE_QNODE(mas_gnoc_snoc, SDM660_MASTER_GNOC_SNOC, 8, 150, -1, false, -1, 0, -1, SDM660_SLAVE_PIMEM, SDM660_SLAVE_IPA, SDM660_SLAVE_QDSS_STM, SDM660_SLAVE_LPASS, SDM660_SLAVE_HMSS, SDM660_SLAVE_CDSP, SDM660_SLAVE_SNOC_CNOC, SDM660_SLAVE_WLAN, SDM660_SLAVE_IMEM); -DEFINE_QNODE(mas_a2noc_snoc, SDM660_MASTER_A2NOC_SNOC, 16, 112, -1, false, -1, 0, -1, SDM660_SLAVE_PIMEM, SDM660_SLAVE_IPA, SDM660_SLAVE_QDSS_STM, SDM660_SLAVE_LPASS, SDM660_SLAVE_HMSS, SDM660_SLAVE_SNOC_BIMC, SDM660_SLAVE_CDSP, SDM660_SLAVE_SNOC_CNOC, SDM660_SLAVE_WLAN, SDM660_SLAVE_IMEM); -DEFINE_QNODE(slv_a2noc_snoc, SDM660_SLAVE_A2NOC_SNOC, 16, -1, 143, false, -1, 0, -1, SDM660_MASTER_A2NOC_SNOC); -DEFINE_QNODE(slv_ebi, SDM660_SLAVE_EBI, 4, -1, 0, false, -1, 0, -1, 0); -DEFINE_QNODE(slv_hmss_l3, SDM660_SLAVE_HMSS_L3, 4, -1, 160, false, -1, 0, -1, 0); -DEFINE_QNODE(slv_bimc_snoc, SDM660_SLAVE_BIMC_SNOC, 4, -1, 2, false, -1, 0, -1, SDM660_MASTER_BIMC_SNOC); -DEFINE_QNODE(slv_cnoc_a2noc, SDM660_SLAVE_CNOC_A2NOC, 8, -1, 208, true, -1, 0, -1, SDM660_MASTER_CNOC_A2NOC); -DEFINE_QNODE(slv_mpm, SDM660_SLAVE_MPM, 4, -1, 62, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_pmic_arb, SDM660_SLAVE_PMIC_ARB, 4, -1, 59, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_tlmm_north, SDM660_SLAVE_TLMM_NORTH, 8, -1, 214, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_tcsr, SDM660_SLAVE_TCSR, 4, -1, 50, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_pimem_cfg, SDM660_SLAVE_PIMEM_CFG, 4, -1, 167, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_imem_cfg, SDM660_SLAVE_IMEM_CFG, 4, -1, 54, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_message_ram, SDM660_SLAVE_MESSAGE_RAM, 4, -1, 55, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_glm, SDM660_SLAVE_GLM, 4, -1, 209, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_bimc_cfg, SDM660_SLAVE_BIMC_CFG, 4, -1, 56, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_prng, SDM660_SLAVE_PRNG, 4, -1, 44, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_spdm, SDM660_SLAVE_SPDM, 4, -1, 60, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_qdss_cfg, SDM660_SLAVE_QDSS_CFG, 4, -1, 63, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_cnoc_mnoc_cfg, SDM660_SLAVE_CNOC_MNOC_CFG, 4, -1, 66, true, -1, 0, -1, SDM660_MASTER_CNOC_MNOC_CFG); -DEFINE_QNODE(slv_snoc_cfg, SDM660_SLAVE_SNOC_CFG, 4, -1, 70, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_qm_cfg, SDM660_SLAVE_QM_CFG, 4, -1, 212, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_clk_ctl, SDM660_SLAVE_CLK_CTL, 4, -1, 47, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_mss_cfg, SDM660_SLAVE_MSS_CFG, 4, -1, 48, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_tlmm_south, SDM660_SLAVE_TLMM_SOUTH, 4, -1, 217, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_ufs_cfg, SDM660_SLAVE_UFS_CFG, 4, -1, 92, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_a2noc_cfg, SDM660_SLAVE_A2NOC_CFG, 4, -1, 150, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_a2noc_smmu_cfg, SDM660_SLAVE_A2NOC_SMMU_CFG, 8, -1, 152, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_gpuss_cfg, SDM660_SLAVE_GPUSS_CFG, 8, -1, 11, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_ahb2phy, SDM660_SLAVE_AHB2PHY, 4, -1, 163, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_blsp_1, SDM660_SLAVE_BLSP_1, 4, -1, 39, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_sdcc_1, SDM660_SLAVE_SDCC_1, 4, -1, 31, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_sdcc_2, SDM660_SLAVE_SDCC_2, 4, -1, 33, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_tlmm_center, SDM660_SLAVE_TLMM_CENTER, 4, -1, 218, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_blsp_2, SDM660_SLAVE_BLSP_2, 4, -1, 37, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_pdm, SDM660_SLAVE_PDM, 4, -1, 41, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_cnoc_mnoc_mmss_cfg, SDM660_SLAVE_CNOC_MNOC_MMSS_CFG, 8, -1, 58, true, -1, 0, -1, SDM660_MASTER_CNOC_MNOC_MMSS_CFG); -DEFINE_QNODE(slv_usb_hs, SDM660_SLAVE_USB_HS, 4, -1, 40, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_usb3_0, SDM660_SLAVE_USB3_0, 4, -1, 22, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_srvc_cnoc, SDM660_SLAVE_SRVC_CNOC, 4, -1, 76, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_gnoc_bimc, SDM660_SLAVE_GNOC_BIMC, 16, -1, 210, true, -1, 0, -1, SDM660_MASTER_GNOC_BIMC); -DEFINE_QNODE(slv_gnoc_snoc, SDM660_SLAVE_GNOC_SNOC, 8, -1, 211, true, -1, 0, -1, SDM660_MASTER_GNOC_SNOC); -DEFINE_QNODE(slv_camera_cfg, SDM660_SLAVE_CAMERA_CFG, 4, -1, 3, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_camera_throttle_cfg, SDM660_SLAVE_CAMERA_THROTTLE_CFG, 4, -1, 154, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_misc_cfg, SDM660_SLAVE_MISC_CFG, 4, -1, 8, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_venus_throttle_cfg, SDM660_SLAVE_VENUS_THROTTLE_CFG, 4, -1, 178, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_venus_cfg, SDM660_SLAVE_VENUS_CFG, 4, -1, 10, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_mmss_clk_xpu_cfg, SDM660_SLAVE_MMSS_CLK_XPU_CFG, 4, -1, 13, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_mmss_clk_cfg, SDM660_SLAVE_MMSS_CLK_CFG, 4, -1, 12, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_mnoc_mpu_cfg, SDM660_SLAVE_MNOC_MPU_CFG, 4, -1, 14, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_display_cfg, SDM660_SLAVE_DISPLAY_CFG, 4, -1, 4, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_csi_phy_cfg, SDM660_SLAVE_CSI_PHY_CFG, 4, -1, 224, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_display_throttle_cfg, SDM660_SLAVE_DISPLAY_THROTTLE_CFG, 4, -1, 156, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_smmu_cfg, SDM660_SLAVE_SMMU_CFG, 8, -1, 205, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_mnoc_bimc, SDM660_SLAVE_MNOC_BIMC, 16, -1, 16, true, -1, 0, -1, SDM660_MASTER_MNOC_BIMC); -DEFINE_QNODE(slv_srvc_mnoc, SDM660_SLAVE_SRVC_MNOC, 8, -1, 17, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_hmss, SDM660_SLAVE_HMSS, 8, -1, 20, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_lpass, SDM660_SLAVE_LPASS, 4, -1, 21, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_wlan, SDM660_SLAVE_WLAN, 4, -1, 206, false, -1, 0, -1, 0); -DEFINE_QNODE(slv_cdsp, SDM660_SLAVE_CDSP, 4, -1, 221, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_ipa, SDM660_SLAVE_IPA, 4, -1, 183, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_snoc_bimc, SDM660_SLAVE_SNOC_BIMC, 16, -1, 24, false, -1, 0, -1, SDM660_MASTER_SNOC_BIMC); -DEFINE_QNODE(slv_snoc_cnoc, SDM660_SLAVE_SNOC_CNOC, 8, -1, 25, false, -1, 0, -1, SDM660_MASTER_SNOC_CNOC); -DEFINE_QNODE(slv_imem, SDM660_SLAVE_IMEM, 8, -1, 26, false, -1, 0, -1, 0); -DEFINE_QNODE(slv_pimem, SDM660_SLAVE_PIMEM, 8, -1, 166, false, -1, 0, -1, 0); -DEFINE_QNODE(slv_qdss_stm, SDM660_SLAVE_QDSS_STM, 4, -1, 30, false, -1, 0, -1, 0); -DEFINE_QNODE(slv_srvc_snoc, SDM660_SLAVE_SRVC_SNOC, 16, -1, 29, false, -1, 0, -1, 0); +static struct qcom_icc_node slv_venus_throttle_cfg = { + .name = "slv_venus_throttle_cfg", + .id = SDM660_SLAVE_VENUS_THROTTLE_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 178, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_venus_cfg = { + .name = "slv_venus_cfg", + .id = SDM660_SLAVE_VENUS_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 10, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_mmss_clk_xpu_cfg = { + .name = "slv_mmss_clk_xpu_cfg", + .id = SDM660_SLAVE_MMSS_CLK_XPU_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 13, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_mmss_clk_cfg = { + .name = "slv_mmss_clk_cfg", + .id = SDM660_SLAVE_MMSS_CLK_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 12, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_mnoc_mpu_cfg = { + .name = "slv_mnoc_mpu_cfg", + .id = SDM660_SLAVE_MNOC_MPU_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 14, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_display_cfg = { + .name = "slv_display_cfg", + .id = SDM660_SLAVE_DISPLAY_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 4, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_csi_phy_cfg = { + .name = "slv_csi_phy_cfg", + .id = SDM660_SLAVE_CSI_PHY_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 224, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_display_throttle_cfg = { + .name = "slv_display_throttle_cfg", + .id = SDM660_SLAVE_DISPLAY_THROTTLE_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 156, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_smmu_cfg = { + .name = "slv_smmu_cfg", + .id = SDM660_SLAVE_SMMU_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 205, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static const u16 slv_mnoc_bimc_links[] = { + SDM660_MASTER_MNOC_BIMC +}; + +static struct qcom_icc_node slv_mnoc_bimc = { + .name = "slv_mnoc_bimc", + .id = SDM660_SLAVE_MNOC_BIMC, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 16, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(slv_mnoc_bimc_links), + .links = slv_mnoc_bimc_links, +}; + +static struct qcom_icc_node slv_srvc_mnoc = { + .name = "slv_srvc_mnoc", + .id = SDM660_SLAVE_SRVC_MNOC, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 17, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_hmss = { + .name = "slv_hmss", + .id = SDM660_SLAVE_HMSS, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 20, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_lpass = { + .name = "slv_lpass", + .id = SDM660_SLAVE_LPASS, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 21, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_wlan = { + .name = "slv_wlan", + .id = SDM660_SLAVE_WLAN, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 206, + .qos.ap_owned = false, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_cdsp = { + .name = "slv_cdsp", + .id = SDM660_SLAVE_CDSP, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 221, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_ipa = { + .name = "slv_ipa", + .id = SDM660_SLAVE_IPA, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 183, + .qos.ap_owned = true, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static const u16 slv_snoc_bimc_links[] = { + SDM660_MASTER_SNOC_BIMC +}; + +static struct qcom_icc_node slv_snoc_bimc = { + .name = "slv_snoc_bimc", + .id = SDM660_SLAVE_SNOC_BIMC, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 24, + .qos.ap_owned = false, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(slv_snoc_bimc_links), + .links = slv_snoc_bimc_links, +}; + +static const u16 slv_snoc_cnoc_links[] = { + SDM660_MASTER_SNOC_CNOC +}; + +static struct qcom_icc_node slv_snoc_cnoc = { + .name = "slv_snoc_cnoc", + .id = SDM660_SLAVE_SNOC_CNOC, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 25, + .qos.ap_owned = false, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(slv_snoc_cnoc_links), + .links = slv_snoc_cnoc_links, +}; + +static struct qcom_icc_node slv_imem = { + .name = "slv_imem", + .id = SDM660_SLAVE_IMEM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 26, + .qos.ap_owned = false, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_pimem = { + .name = "slv_pimem", + .id = SDM660_SLAVE_PIMEM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 166, + .qos.ap_owned = false, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_qdss_stm = { + .name = "slv_qdss_stm", + .id = SDM660_SLAVE_QDSS_STM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 30, + .qos.ap_owned = false, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; + +static struct qcom_icc_node slv_srvc_snoc = { + .name = "slv_srvc_snoc", + .id = SDM660_SLAVE_SRVC_SNOC, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 29, + .qos.ap_owned = false, + .qos.qos_mode = -1, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, +}; static struct qcom_icc_node *sdm660_a2noc_nodes[] = { [MASTER_IPA] = &mas_ipa, -- cgit v1.2.3-70-g09d2 From 656ba110e164e1aab2aa4bec9baac51008c5d12c Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 4 Sep 2021 02:24:13 +0300 Subject: interconnect: sdm660: drop default/unused values Simplify qnode setup by removing unused/default values. Signed-off-by: Dmitry Baryshkov Reviewed-by: AngeloGioacchino Del Regno Tested-by: Marijn Suijten Tested-by: Shawn Guo Link: https://lore.kernel.org/r/20210903232421.1384199-4-dmitry.baryshkov@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/sdm660.c | 407 ++++++------------------------------- 1 file changed, 64 insertions(+), 343 deletions(-) diff --git a/drivers/interconnect/qcom/sdm660.c b/drivers/interconnect/qcom/sdm660.c index 652f7cc9cad6..4a72f9677d4e 100644 --- a/drivers/interconnect/qcom/sdm660.c +++ b/drivers/interconnect/qcom/sdm660.c @@ -35,6 +35,7 @@ #define M_BKE_EN_EN_BMASK 0x1 /* Valid for both NoC and BIMC */ +#define NOC_QOS_MODE_INVALID -1 #define NOC_QOS_MODE_FIXED 0x0 #define NOC_QOS_MODE_LIMITER 0x1 #define NOC_QOS_MODE_BYPASS 0x2 @@ -279,10 +280,7 @@ static struct qcom_icc_node mas_cnoc_a2noc = { .mas_rpm_id = 146, .slv_rpm_id = -1, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(mas_cnoc_a2noc_links), .links = mas_cnoc_a2noc_links, }; @@ -297,11 +295,6 @@ static struct qcom_icc_node mas_sdcc_1 = { .buswidth = 8, .mas_rpm_id = 33, .slv_rpm_id = -1, - .qos.ap_owned = false, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, .num_links = ARRAY_SIZE(mas_sdcc_1_links), .links = mas_sdcc_1_links, }; @@ -316,11 +309,6 @@ static struct qcom_icc_node mas_sdcc_2 = { .buswidth = 8, .mas_rpm_id = 35, .slv_rpm_id = -1, - .qos.ap_owned = false, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, .num_links = ARRAY_SIZE(mas_sdcc_2_links), .links = mas_sdcc_2_links, }; @@ -335,11 +323,6 @@ static struct qcom_icc_node mas_blsp_1 = { .buswidth = 4, .mas_rpm_id = 41, .slv_rpm_id = -1, - .qos.ap_owned = false, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, .num_links = ARRAY_SIZE(mas_blsp_1_links), .links = mas_blsp_1_links, }; @@ -354,11 +337,6 @@ static struct qcom_icc_node mas_blsp_2 = { .buswidth = 4, .mas_rpm_id = 39, .slv_rpm_id = -1, - .qos.ap_owned = false, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, .num_links = ARRAY_SIZE(mas_blsp_2_links), .links = mas_blsp_2_links, }; @@ -511,11 +489,6 @@ static struct qcom_icc_node mas_snoc_bimc = { .buswidth = 4, .mas_rpm_id = 3, .slv_rpm_id = -1, - .qos.ap_owned = false, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, .num_links = ARRAY_SIZE(mas_snoc_bimc_links), .links = mas_snoc_bimc_links, }; @@ -583,10 +556,7 @@ static struct qcom_icc_node mas_snoc_cnoc = { .mas_rpm_id = 52, .slv_rpm_id = -1, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(mas_snoc_cnoc_links), .links = mas_snoc_cnoc_links, }; @@ -635,10 +605,7 @@ static struct qcom_icc_node mas_qdss_dap = { .mas_rpm_id = 49, .slv_rpm_id = -1, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(mas_qdss_dap_links), .links = mas_qdss_dap_links, }; @@ -655,10 +622,7 @@ static struct qcom_icc_node mas_apss_proc = { .mas_rpm_id = 0, .slv_rpm_id = -1, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(mas_apss_proc_links), .links = mas_apss_proc_links, }; @@ -685,10 +649,7 @@ static struct qcom_icc_node mas_cnoc_mnoc_mmss_cfg = { .mas_rpm_id = 4, .slv_rpm_id = -1, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(mas_cnoc_mnoc_mmss_cfg_links), .links = mas_cnoc_mnoc_mmss_cfg_links, }; @@ -704,10 +665,7 @@ static struct qcom_icc_node mas_cnoc_mnoc_cfg = { .mas_rpm_id = 5, .slv_rpm_id = -1, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(mas_cnoc_mnoc_cfg_links), .links = mas_cnoc_mnoc_cfg_links, }; @@ -880,11 +838,6 @@ static struct qcom_icc_node mas_snoc_cfg = { .buswidth = 4, .mas_rpm_id = 20, .slv_rpm_id = -1, - .qos.ap_owned = false, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, .num_links = ARRAY_SIZE(mas_snoc_cfg_links), .links = mas_snoc_cfg_links, }; @@ -907,11 +860,6 @@ static struct qcom_icc_node mas_bimc_snoc = { .buswidth = 8, .mas_rpm_id = 21, .slv_rpm_id = -1, - .qos.ap_owned = false, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, .num_links = ARRAY_SIZE(mas_bimc_snoc_links), .links = mas_bimc_snoc_links, }; @@ -934,11 +882,6 @@ static struct qcom_icc_node mas_gnoc_snoc = { .buswidth = 8, .mas_rpm_id = 150, .slv_rpm_id = -1, - .qos.ap_owned = false, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, .num_links = ARRAY_SIZE(mas_gnoc_snoc_links), .links = mas_gnoc_snoc_links, }; @@ -962,11 +905,6 @@ static struct qcom_icc_node mas_a2noc_snoc = { .buswidth = 16, .mas_rpm_id = 112, .slv_rpm_id = -1, - .qos.ap_owned = false, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, .num_links = ARRAY_SIZE(mas_a2noc_snoc_links), .links = mas_a2noc_snoc_links, }; @@ -981,11 +919,6 @@ static struct qcom_icc_node slv_a2noc_snoc = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = 143, - .qos.ap_owned = false, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, .num_links = ARRAY_SIZE(slv_a2noc_snoc_links), .links = slv_a2noc_snoc_links, }; @@ -996,11 +929,6 @@ static struct qcom_icc_node slv_ebi = { .buswidth = 4, .mas_rpm_id = -1, .slv_rpm_id = 0, - .qos.ap_owned = false, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, }; static struct qcom_icc_node slv_hmss_l3 = { @@ -1009,11 +937,6 @@ static struct qcom_icc_node slv_hmss_l3 = { .buswidth = 4, .mas_rpm_id = -1, .slv_rpm_id = 160, - .qos.ap_owned = false, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, }; static const u16 slv_bimc_snoc_links[] = { @@ -1026,11 +949,6 @@ static struct qcom_icc_node slv_bimc_snoc = { .buswidth = 4, .mas_rpm_id = -1, .slv_rpm_id = 2, - .qos.ap_owned = false, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, .num_links = ARRAY_SIZE(slv_bimc_snoc_links), .links = slv_bimc_snoc_links, }; @@ -1046,10 +964,7 @@ static struct qcom_icc_node slv_cnoc_a2noc = { .mas_rpm_id = -1, .slv_rpm_id = 208, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(slv_cnoc_a2noc_links), .links = slv_cnoc_a2noc_links, }; @@ -1061,10 +976,7 @@ static struct qcom_icc_node slv_mpm = { .mas_rpm_id = -1, .slv_rpm_id = 62, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_pmic_arb = { @@ -1074,10 +986,7 @@ static struct qcom_icc_node slv_pmic_arb = { .mas_rpm_id = -1, .slv_rpm_id = 59, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_tlmm_north = { @@ -1087,10 +996,7 @@ static struct qcom_icc_node slv_tlmm_north = { .mas_rpm_id = -1, .slv_rpm_id = 214, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_tcsr = { @@ -1100,10 +1006,7 @@ static struct qcom_icc_node slv_tcsr = { .mas_rpm_id = -1, .slv_rpm_id = 50, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_pimem_cfg = { @@ -1113,10 +1016,7 @@ static struct qcom_icc_node slv_pimem_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 167, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_imem_cfg = { @@ -1126,10 +1026,7 @@ static struct qcom_icc_node slv_imem_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 54, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_message_ram = { @@ -1139,10 +1036,7 @@ static struct qcom_icc_node slv_message_ram = { .mas_rpm_id = -1, .slv_rpm_id = 55, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_glm = { @@ -1152,10 +1046,7 @@ static struct qcom_icc_node slv_glm = { .mas_rpm_id = -1, .slv_rpm_id = 209, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_bimc_cfg = { @@ -1165,10 +1056,7 @@ static struct qcom_icc_node slv_bimc_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 56, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_prng = { @@ -1178,10 +1066,7 @@ static struct qcom_icc_node slv_prng = { .mas_rpm_id = -1, .slv_rpm_id = 44, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_spdm = { @@ -1191,10 +1076,7 @@ static struct qcom_icc_node slv_spdm = { .mas_rpm_id = -1, .slv_rpm_id = 60, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_qdss_cfg = { @@ -1204,10 +1086,7 @@ static struct qcom_icc_node slv_qdss_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 63, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static const u16 slv_cnoc_mnoc_cfg_links[] = { @@ -1221,10 +1100,7 @@ static struct qcom_icc_node slv_cnoc_mnoc_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 66, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(slv_cnoc_mnoc_cfg_links), .links = slv_cnoc_mnoc_cfg_links, }; @@ -1236,10 +1112,7 @@ static struct qcom_icc_node slv_snoc_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 70, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_qm_cfg = { @@ -1249,10 +1122,7 @@ static struct qcom_icc_node slv_qm_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 212, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_clk_ctl = { @@ -1262,10 +1132,7 @@ static struct qcom_icc_node slv_clk_ctl = { .mas_rpm_id = -1, .slv_rpm_id = 47, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_mss_cfg = { @@ -1275,10 +1142,7 @@ static struct qcom_icc_node slv_mss_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 48, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_tlmm_south = { @@ -1288,10 +1152,7 @@ static struct qcom_icc_node slv_tlmm_south = { .mas_rpm_id = -1, .slv_rpm_id = 217, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_ufs_cfg = { @@ -1301,10 +1162,7 @@ static struct qcom_icc_node slv_ufs_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 92, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_a2noc_cfg = { @@ -1314,10 +1172,7 @@ static struct qcom_icc_node slv_a2noc_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 150, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_a2noc_smmu_cfg = { @@ -1327,10 +1182,7 @@ static struct qcom_icc_node slv_a2noc_smmu_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 152, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_gpuss_cfg = { @@ -1340,10 +1192,7 @@ static struct qcom_icc_node slv_gpuss_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 11, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_ahb2phy = { @@ -1353,10 +1202,7 @@ static struct qcom_icc_node slv_ahb2phy = { .mas_rpm_id = -1, .slv_rpm_id = 163, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_blsp_1 = { @@ -1366,10 +1212,7 @@ static struct qcom_icc_node slv_blsp_1 = { .mas_rpm_id = -1, .slv_rpm_id = 39, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_sdcc_1 = { @@ -1379,10 +1222,7 @@ static struct qcom_icc_node slv_sdcc_1 = { .mas_rpm_id = -1, .slv_rpm_id = 31, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_sdcc_2 = { @@ -1392,10 +1232,7 @@ static struct qcom_icc_node slv_sdcc_2 = { .mas_rpm_id = -1, .slv_rpm_id = 33, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_tlmm_center = { @@ -1405,10 +1242,7 @@ static struct qcom_icc_node slv_tlmm_center = { .mas_rpm_id = -1, .slv_rpm_id = 218, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_blsp_2 = { @@ -1418,10 +1252,7 @@ static struct qcom_icc_node slv_blsp_2 = { .mas_rpm_id = -1, .slv_rpm_id = 37, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_pdm = { @@ -1431,10 +1262,7 @@ static struct qcom_icc_node slv_pdm = { .mas_rpm_id = -1, .slv_rpm_id = 41, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static const u16 slv_cnoc_mnoc_mmss_cfg_links[] = { @@ -1448,10 +1276,7 @@ static struct qcom_icc_node slv_cnoc_mnoc_mmss_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 58, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(slv_cnoc_mnoc_mmss_cfg_links), .links = slv_cnoc_mnoc_mmss_cfg_links, }; @@ -1463,10 +1288,7 @@ static struct qcom_icc_node slv_usb_hs = { .mas_rpm_id = -1, .slv_rpm_id = 40, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_usb3_0 = { @@ -1476,10 +1298,7 @@ static struct qcom_icc_node slv_usb3_0 = { .mas_rpm_id = -1, .slv_rpm_id = 22, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_srvc_cnoc = { @@ -1489,10 +1308,7 @@ static struct qcom_icc_node slv_srvc_cnoc = { .mas_rpm_id = -1, .slv_rpm_id = 76, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static const u16 slv_gnoc_bimc_links[] = { @@ -1506,10 +1322,7 @@ static struct qcom_icc_node slv_gnoc_bimc = { .mas_rpm_id = -1, .slv_rpm_id = 210, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(slv_gnoc_bimc_links), .links = slv_gnoc_bimc_links, }; @@ -1525,10 +1338,7 @@ static struct qcom_icc_node slv_gnoc_snoc = { .mas_rpm_id = -1, .slv_rpm_id = 211, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(slv_gnoc_snoc_links), .links = slv_gnoc_snoc_links, }; @@ -1540,10 +1350,7 @@ static struct qcom_icc_node slv_camera_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 3, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_camera_throttle_cfg = { @@ -1553,10 +1360,7 @@ static struct qcom_icc_node slv_camera_throttle_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 154, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_misc_cfg = { @@ -1566,10 +1370,7 @@ static struct qcom_icc_node slv_misc_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 8, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_venus_throttle_cfg = { @@ -1579,10 +1380,7 @@ static struct qcom_icc_node slv_venus_throttle_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 178, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_venus_cfg = { @@ -1592,10 +1390,7 @@ static struct qcom_icc_node slv_venus_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 10, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_mmss_clk_xpu_cfg = { @@ -1605,10 +1400,7 @@ static struct qcom_icc_node slv_mmss_clk_xpu_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 13, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_mmss_clk_cfg = { @@ -1618,10 +1410,7 @@ static struct qcom_icc_node slv_mmss_clk_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 12, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_mnoc_mpu_cfg = { @@ -1631,10 +1420,7 @@ static struct qcom_icc_node slv_mnoc_mpu_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 14, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_display_cfg = { @@ -1644,10 +1430,7 @@ static struct qcom_icc_node slv_display_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 4, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_csi_phy_cfg = { @@ -1657,10 +1440,7 @@ static struct qcom_icc_node slv_csi_phy_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 224, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_display_throttle_cfg = { @@ -1670,10 +1450,7 @@ static struct qcom_icc_node slv_display_throttle_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 156, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_smmu_cfg = { @@ -1683,10 +1460,7 @@ static struct qcom_icc_node slv_smmu_cfg = { .mas_rpm_id = -1, .slv_rpm_id = 205, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static const u16 slv_mnoc_bimc_links[] = { @@ -1700,10 +1474,7 @@ static struct qcom_icc_node slv_mnoc_bimc = { .mas_rpm_id = -1, .slv_rpm_id = 16, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(slv_mnoc_bimc_links), .links = slv_mnoc_bimc_links, }; @@ -1715,10 +1486,7 @@ static struct qcom_icc_node slv_srvc_mnoc = { .mas_rpm_id = -1, .slv_rpm_id = 17, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_hmss = { @@ -1728,10 +1496,7 @@ static struct qcom_icc_node slv_hmss = { .mas_rpm_id = -1, .slv_rpm_id = 20, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_lpass = { @@ -1741,10 +1506,7 @@ static struct qcom_icc_node slv_lpass = { .mas_rpm_id = -1, .slv_rpm_id = 21, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_wlan = { @@ -1753,11 +1515,6 @@ static struct qcom_icc_node slv_wlan = { .buswidth = 4, .mas_rpm_id = -1, .slv_rpm_id = 206, - .qos.ap_owned = false, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, }; static struct qcom_icc_node slv_cdsp = { @@ -1767,10 +1524,7 @@ static struct qcom_icc_node slv_cdsp = { .mas_rpm_id = -1, .slv_rpm_id = 221, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static struct qcom_icc_node slv_ipa = { @@ -1780,10 +1534,7 @@ static struct qcom_icc_node slv_ipa = { .mas_rpm_id = -1, .slv_rpm_id = 183, .qos.ap_owned = true, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, + .qos.qos_mode = NOC_QOS_MODE_INVALID, }; static const u16 slv_snoc_bimc_links[] = { @@ -1796,11 +1547,6 @@ static struct qcom_icc_node slv_snoc_bimc = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = 24, - .qos.ap_owned = false, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, .num_links = ARRAY_SIZE(slv_snoc_bimc_links), .links = slv_snoc_bimc_links, }; @@ -1815,11 +1561,6 @@ static struct qcom_icc_node slv_snoc_cnoc = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = 25, - .qos.ap_owned = false, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, .num_links = ARRAY_SIZE(slv_snoc_cnoc_links), .links = slv_snoc_cnoc_links, }; @@ -1830,11 +1571,6 @@ static struct qcom_icc_node slv_imem = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = 26, - .qos.ap_owned = false, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, }; static struct qcom_icc_node slv_pimem = { @@ -1843,11 +1579,6 @@ static struct qcom_icc_node slv_pimem = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = 166, - .qos.ap_owned = false, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, }; static struct qcom_icc_node slv_qdss_stm = { @@ -1856,11 +1587,6 @@ static struct qcom_icc_node slv_qdss_stm = { .buswidth = 4, .mas_rpm_id = -1, .slv_rpm_id = 30, - .qos.ap_owned = false, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, }; static struct qcom_icc_node slv_srvc_snoc = { @@ -1869,11 +1595,6 @@ static struct qcom_icc_node slv_srvc_snoc = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = 29, - .qos.ap_owned = false, - .qos.qos_mode = -1, - .qos.areq_prio = 0, - .qos.prio_level = 0, - .qos.qos_port = -1, }; static struct qcom_icc_node *sdm660_a2noc_nodes[] = { @@ -2113,7 +1834,7 @@ static int qcom_icc_set_bimc_qos(struct icc_node *src, u64 max_bw, provider = src->provider; qp = to_qcom_provider(provider); - if (qn->qos.qos_mode != -1) + if (qn->qos.qos_mode != NOC_QOS_MODE_INVALID) mode = qn->qos.qos_mode; /* QoS Priority: The QoS Health parameters are getting considered @@ -2171,7 +1892,7 @@ static int qcom_icc_set_noc_qos(struct icc_node *src, u64 max_bw) return 0; } - if (qn->qos.qos_mode != -1) + if (qn->qos.qos_mode != NOC_QOS_MODE_INVALID) mode = qn->qos.qos_mode; if (mode == NOC_QOS_MODE_FIXED) { @@ -2264,7 +1985,7 @@ static int qcom_icc_set(struct icc_node *src, struct icc_node *dst) ret = qcom_icc_rpm_set(qn->mas_rpm_id, qn->slv_rpm_id, sum_bw); if (ret) return ret; - } else if (qn->qos.qos_mode != -1) { + } else if (qn->qos.qos_mode != NOC_QOS_MODE_INVALID) { /* set bandwidth directly from the AP */ ret = qcom_icc_qos_set(src, sum_bw); if (ret) -- cgit v1.2.3-70-g09d2 From 2b6c7d645118cba7719f16f3b0e4d4a555776f48 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 4 Sep 2021 02:24:14 +0300 Subject: interconnect: sdm660: merge common code into icc-rpm Other RPM interconnect drivers might also use QoS support. Move AP-owned nodes support from SDM660 driver to common icc-rpm.c. Signed-off-by: Dmitry Baryshkov Reviewed-by: AngeloGioacchino Del Regno Tested-by: Marijn Suijten Tested-by: Shawn Guo Link: https://lore.kernel.org/r/20210903232421.1384199-5-dmitry.baryshkov@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 241 ++++++++++++++++-- drivers/interconnect/qcom/icc-rpm.h | 42 +++- drivers/interconnect/qcom/sdm660.c | 485 ++---------------------------------- 3 files changed, 274 insertions(+), 494 deletions(-) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index 3049454685dc..96a160edece9 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -11,60 +11,228 @@ #include #include #include +#include #include #include "smd-rpm.h" #include "icc-rpm.h" -static int qcom_icc_set(struct icc_node *src, struct icc_node *dst) +/* BIMC QoS */ +#define M_BKE_REG_BASE(n) (0x300 + (0x4000 * n)) +#define M_BKE_EN_ADDR(n) (M_BKE_REG_BASE(n)) +#define M_BKE_HEALTH_CFG_ADDR(i, n) (M_BKE_REG_BASE(n) + 0x40 + (0x4 * i)) + +#define M_BKE_HEALTH_CFG_LIMITCMDS_MASK 0x80000000 +#define M_BKE_HEALTH_CFG_AREQPRIO_MASK 0x300 +#define M_BKE_HEALTH_CFG_PRIOLVL_MASK 0x3 +#define M_BKE_HEALTH_CFG_AREQPRIO_SHIFT 0x8 +#define M_BKE_HEALTH_CFG_LIMITCMDS_SHIFT 0x1f + +#define M_BKE_EN_EN_BMASK 0x1 + +/* NoC QoS */ +#define NOC_QOS_PRIORITYn_ADDR(n) (0x8 + (n * 0x1000)) +#define NOC_QOS_PRIORITY_P1_MASK 0xc +#define NOC_QOS_PRIORITY_P0_MASK 0x3 +#define NOC_QOS_PRIORITY_P1_SHIFT 0x2 + +#define NOC_QOS_MODEn_ADDR(n) (0xc + (n * 0x1000)) +#define NOC_QOS_MODEn_MASK 0x3 + +static int qcom_icc_bimc_set_qos_health(struct regmap *rmap, + struct qcom_icc_qos *qos, + int regnum) +{ + u32 val; + u32 mask; + + val = qos->prio_level; + mask = M_BKE_HEALTH_CFG_PRIOLVL_MASK; + + val |= qos->areq_prio << M_BKE_HEALTH_CFG_AREQPRIO_SHIFT; + mask |= M_BKE_HEALTH_CFG_AREQPRIO_MASK; + + /* LIMITCMDS is not present on M_BKE_HEALTH_3 */ + if (regnum != 3) { + val |= qos->limit_commands << M_BKE_HEALTH_CFG_LIMITCMDS_SHIFT; + mask |= M_BKE_HEALTH_CFG_LIMITCMDS_MASK; + } + + return regmap_update_bits(rmap, + M_BKE_HEALTH_CFG_ADDR(regnum, qos->qos_port), + mask, val); +} + +static int qcom_icc_set_bimc_qos(struct icc_node *src, u64 max_bw) { struct qcom_icc_provider *qp; struct qcom_icc_node *qn; struct icc_provider *provider; - struct icc_node *n; - u64 sum_bw; - u64 max_peak_bw; - u64 rate; - u32 agg_avg = 0; - u32 agg_peak = 0; - int ret, i; + u32 mode = NOC_QOS_MODE_BYPASS; + u32 val = 0; + int i, rc = 0; qn = src->data; provider = src->provider; qp = to_qcom_provider(provider); - list_for_each_entry(n, &provider->nodes, node_list) - provider->aggregate(n, 0, n->avg_bw, n->peak_bw, - &agg_avg, &agg_peak); + if (qn->qos.qos_mode != -1) + mode = qn->qos.qos_mode; + + /* QoS Priority: The QoS Health parameters are getting considered + * only if we are NOT in Bypass Mode. + */ + if (mode != NOC_QOS_MODE_BYPASS) { + for (i = 3; i >= 0; i--) { + rc = qcom_icc_bimc_set_qos_health(qp->regmap, + &qn->qos, i); + if (rc) + return rc; + } - sum_bw = icc_units_to_bps(agg_avg); - max_peak_bw = icc_units_to_bps(agg_peak); + /* Set BKE_EN to 1 when Fixed, Regulator or Limiter Mode */ + val = 1; + } + + return regmap_update_bits(qp->regmap, M_BKE_EN_ADDR(qn->qos.qos_port), + M_BKE_EN_EN_BMASK, val); +} + +static int qcom_icc_noc_set_qos_priority(struct regmap *rmap, + struct qcom_icc_qos *qos) +{ + u32 val; + int rc; + + /* Must be updated one at a time, P1 first, P0 last */ + val = qos->areq_prio << NOC_QOS_PRIORITY_P1_SHIFT; + rc = regmap_update_bits(rmap, NOC_QOS_PRIORITYn_ADDR(qos->qos_port), + NOC_QOS_PRIORITY_P1_MASK, val); + if (rc) + return rc; + + return regmap_update_bits(rmap, NOC_QOS_PRIORITYn_ADDR(qos->qos_port), + NOC_QOS_PRIORITY_P0_MASK, qos->prio_level); +} + +static int qcom_icc_set_noc_qos(struct icc_node *src, u64 max_bw) +{ + struct qcom_icc_provider *qp; + struct qcom_icc_node *qn; + struct icc_provider *provider; + u32 mode = NOC_QOS_MODE_BYPASS; + int rc = 0; + + qn = src->data; + provider = src->provider; + qp = to_qcom_provider(provider); + + if (qn->qos.qos_port < 0) { + dev_dbg(src->provider->dev, + "NoC QoS: Skipping %s: vote aggregated on parent.\n", + qn->name); + return 0; + } + + if (qn->qos.qos_mode != -1) + mode = qn->qos.qos_mode; + + if (mode == NOC_QOS_MODE_FIXED) { + dev_dbg(src->provider->dev, "NoC QoS: %s: Set Fixed mode\n", + qn->name); + rc = qcom_icc_noc_set_qos_priority(qp->regmap, &qn->qos); + if (rc) + return rc; + } else if (mode == NOC_QOS_MODE_BYPASS) { + dev_dbg(src->provider->dev, "NoC QoS: %s: Set Bypass mode\n", + qn->name); + } + + return regmap_update_bits(qp->regmap, + NOC_QOS_MODEn_ADDR(qn->qos.qos_port), + NOC_QOS_MODEn_MASK, mode); +} - /* send bandwidth request message to the RPM processor */ - if (qn->mas_rpm_id != -1) { +static int qcom_icc_qos_set(struct icc_node *node, u64 sum_bw) +{ + struct qcom_icc_provider *qp = to_qcom_provider(node->provider); + struct qcom_icc_node *qn = node->data; + + dev_dbg(node->provider->dev, "Setting QoS for %s\n", qn->name); + + if (qp->is_bimc_node) + return qcom_icc_set_bimc_qos(node, sum_bw); + + return qcom_icc_set_noc_qos(node, sum_bw); +} + +static int qcom_icc_rpm_set(int mas_rpm_id, int slv_rpm_id, u64 sum_bw) +{ + int ret = 0; + + if (mas_rpm_id != -1) { ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE, RPM_BUS_MASTER_REQ, - qn->mas_rpm_id, + mas_rpm_id, sum_bw); if (ret) { pr_err("qcom_icc_rpm_smd_send mas %d error %d\n", - qn->mas_rpm_id, ret); + mas_rpm_id, ret); return ret; } } - if (qn->slv_rpm_id != -1) { + if (slv_rpm_id != -1) { ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE, RPM_BUS_SLAVE_REQ, - qn->slv_rpm_id, + slv_rpm_id, sum_bw); if (ret) { pr_err("qcom_icc_rpm_smd_send slv %d error %d\n", - qn->slv_rpm_id, ret); + slv_rpm_id, ret); return ret; } } + return ret; +} + +static int qcom_icc_set(struct icc_node *src, struct icc_node *dst) +{ + struct qcom_icc_provider *qp; + struct qcom_icc_node *qn; + struct icc_provider *provider; + struct icc_node *n; + u64 sum_bw; + u64 max_peak_bw; + u64 rate; + u32 agg_avg = 0; + u32 agg_peak = 0; + int ret, i; + + qn = src->data; + provider = src->provider; + qp = to_qcom_provider(provider); + + list_for_each_entry(n, &provider->nodes, node_list) + provider->aggregate(n, 0, n->avg_bw, n->peak_bw, + &agg_avg, &agg_peak); + + sum_bw = icc_units_to_bps(agg_avg); + max_peak_bw = icc_units_to_bps(agg_peak); + + if (!qn->qos.ap_owned) { + /* send bandwidth request message to the RPM processor */ + ret = qcom_icc_rpm_set(qn->mas_rpm_id, qn->slv_rpm_id, sum_bw); + if (ret) + return ret; + } else if (qn->qos.qos_mode != -1) { + /* set bandwidth directly from the AP */ + ret = qcom_icc_qos_set(src, sum_bw); + if (ret) + return ret; + } + rate = max(sum_bw, max_peak_bw); do_div(rate, qn->buswidth); @@ -115,8 +283,13 @@ int qnoc_probe(struct platform_device *pdev) qnodes = desc->nodes; num_nodes = desc->num_nodes; - cds = bus_clocks; - cd_num = ARRAY_SIZE(bus_clocks); + if (desc->num_clocks) { + cds = desc->clocks; + cd_num = desc->num_clocks; + } else { + cds = bus_clocks; + cd_num = ARRAY_SIZE(bus_clocks); + } qp = devm_kzalloc(dev, struct_size(qp, bus_clks, cd_num), GFP_KERNEL); if (!qp) @@ -131,6 +304,30 @@ int qnoc_probe(struct platform_device *pdev) qp->bus_clks[i].id = cds[i]; qp->num_clks = cd_num; + qp->is_bimc_node = desc->is_bimc_node; + + if (desc->regmap_cfg) { + struct resource *res; + void __iomem *mmio; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + mmio = devm_ioremap_resource(dev, res); + + if (IS_ERR(mmio)) { + dev_err(dev, "Cannot ioremap interconnect bus resource\n"); + return PTR_ERR(mmio); + } + + qp->regmap = devm_regmap_init_mmio(dev, mmio, desc->regmap_cfg); + if (IS_ERR(qp->regmap)) { + dev_err(dev, "Cannot regmap interconnect bus resource\n"); + return PTR_ERR(qp->regmap); + } + } + ret = devm_clk_bulk_get(dev, qp->num_clks, qp->bus_clks); if (ret) return ret; diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index f4b05c20c097..25d11c6a73d7 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -9,8 +9,6 @@ #define RPM_BUS_MASTER_REQ 0x73616d62 #define RPM_BUS_SLAVE_REQ 0x766c7362 -#define QCOM_MAX_LINKS 12 - #define to_qcom_provider(_provider) \ container_of(_provider, struct qcom_icc_provider, provider) @@ -19,13 +17,35 @@ * @provider: generic interconnect provider * @bus_clks: the clk_bulk_data table of bus clocks * @num_clks: the total number of clk_bulk_data entries + * @is_bimc_node: indicates whether to use bimc specific setting + * @regmap: regmap for QoS registers read/write access */ struct qcom_icc_provider { struct icc_provider provider; int num_clks; + bool is_bimc_node; + struct regmap *regmap; struct clk_bulk_data bus_clks[]; }; +/** + * struct qcom_icc_qos - Qualcomm specific interconnect QoS parameters + * @areq_prio: node requests priority + * @prio_level: priority level for bus communication + * @limit_commands: activate/deactivate limiter mode during runtime + * @ap_owned: indicates if the node is owned by the AP or by the RPM + * @qos_mode: default qos mode for this node + * @qos_port: qos port number for finding qos registers of this node + */ +struct qcom_icc_qos { + u32 areq_prio; + u32 prio_level; + bool limit_commands; + bool ap_owned; + int qos_mode; + int qos_port; +}; + /** * struct qcom_icc_node - Qualcomm specific interconnect nodes * @name: the node name used in debugfs @@ -35,36 +55,48 @@ struct qcom_icc_provider { * @buswidth: width of the interconnect between a node and the bus (bytes) * @mas_rpm_id: RPM id for devices that are bus masters * @slv_rpm_id: RPM id for devices that are bus slaves + * @qos: NoC QoS setting parameters * @rate: current bus clock rate in Hz */ struct qcom_icc_node { unsigned char *name; u16 id; - u16 links[QCOM_MAX_LINKS]; + const u16 *links; u16 num_links; u16 buswidth; int mas_rpm_id; int slv_rpm_id; + struct qcom_icc_qos qos; u64 rate; }; struct qcom_icc_desc { struct qcom_icc_node **nodes; size_t num_nodes; + const char * const *clocks; + size_t num_clocks; + bool is_bimc_node; + const struct regmap_config *regmap_cfg; }; #define DEFINE_QNODE(_name, _id, _buswidth, _mas_rpm_id, _slv_rpm_id, \ ...) \ + static const u16 _name ## _links[] = { __VA_ARGS__ }; \ + \ static struct qcom_icc_node _name = { \ .name = #_name, \ .id = _id, \ .buswidth = _buswidth, \ .mas_rpm_id = _mas_rpm_id, \ .slv_rpm_id = _slv_rpm_id, \ - .num_links = ARRAY_SIZE(((int[]){ __VA_ARGS__ })), \ - .links = { __VA_ARGS__ }, \ + .num_links = ARRAY_SIZE(_name ## _links), \ + .links = _name ## _links, \ } +/* Valid for both NoC and BIMC */ +#define NOC_QOS_MODE_INVALID -1 +#define NOC_QOS_MODE_FIXED 0x0 +#define NOC_QOS_MODE_BYPASS 0x2 int qnoc_probe(struct platform_device *pdev); int qnoc_remove(struct platform_device *pdev); diff --git a/drivers/interconnect/qcom/sdm660.c b/drivers/interconnect/qcom/sdm660.c index 4a72f9677d4e..471bb88f8828 100644 --- a/drivers/interconnect/qcom/sdm660.c +++ b/drivers/interconnect/qcom/sdm660.c @@ -16,42 +16,9 @@ #include #include +#include "icc-rpm.h" #include "smd-rpm.h" -#define RPM_BUS_MASTER_REQ 0x73616d62 -#define RPM_BUS_SLAVE_REQ 0x766c7362 - -/* BIMC QoS */ -#define M_BKE_REG_BASE(n) (0x300 + (0x4000 * n)) -#define M_BKE_EN_ADDR(n) (M_BKE_REG_BASE(n)) -#define M_BKE_HEALTH_CFG_ADDR(i, n) (M_BKE_REG_BASE(n) + 0x40 + (0x4 * i)) - -#define M_BKE_HEALTH_CFG_LIMITCMDS_MASK 0x80000000 -#define M_BKE_HEALTH_CFG_AREQPRIO_MASK 0x300 -#define M_BKE_HEALTH_CFG_PRIOLVL_MASK 0x3 -#define M_BKE_HEALTH_CFG_AREQPRIO_SHIFT 0x8 -#define M_BKE_HEALTH_CFG_LIMITCMDS_SHIFT 0x1f - -#define M_BKE_EN_EN_BMASK 0x1 - -/* Valid for both NoC and BIMC */ -#define NOC_QOS_MODE_INVALID -1 -#define NOC_QOS_MODE_FIXED 0x0 -#define NOC_QOS_MODE_LIMITER 0x1 -#define NOC_QOS_MODE_BYPASS 0x2 - -/* NoC QoS */ -#define NOC_PERM_MODE_FIXED 1 -#define NOC_PERM_MODE_BYPASS (1 << NOC_QOS_MODE_BYPASS) - -#define NOC_QOS_PRIORITYn_ADDR(n) (0x8 + (n * 0x1000)) -#define NOC_QOS_PRIORITY_P1_MASK 0xc -#define NOC_QOS_PRIORITY_P0_MASK 0x3 -#define NOC_QOS_PRIORITY_P1_SHIFT 0x2 - -#define NOC_QOS_MODEn_ADDR(n) (0xc + (n * 0x1000)) -#define NOC_QOS_MODEn_MASK 0x3 - enum { SDM660_MASTER_IPA = 1, SDM660_MASTER_CNOC_A2NOC, @@ -160,94 +127,20 @@ enum { SDM660_SNOC, }; -#define to_qcom_provider(_provider) \ - container_of(_provider, struct qcom_icc_provider, provider) - -static const struct clk_bulk_data bus_clocks[] = { - { .id = "bus" }, - { .id = "bus_a" }, +static const char * const bus_mm_clocks[] = { + "bus", + "bus_a", + "iface", }; -static const struct clk_bulk_data bus_mm_clocks[] = { - { .id = "bus" }, - { .id = "bus_a" }, - { .id = "iface" }, -}; - -static const struct clk_bulk_data bus_a2noc_clocks[] = { - { .id = "bus" }, - { .id = "bus_a" }, - { .id = "ipa" }, - { .id = "ufs_axi" }, - { .id = "aggre2_ufs_axi" }, - { .id = "aggre2_usb3_axi" }, - { .id = "cfg_noc_usb2_axi" }, -}; - -/** - * struct qcom_icc_provider - Qualcomm specific interconnect provider - * @provider: generic interconnect provider - * @bus_clks: the clk_bulk_data table of bus clocks - * @num_clks: the total number of clk_bulk_data entries - * @is_bimc_node: indicates whether to use bimc specific setting - * @regmap: regmap for QoS registers read/write access - * @mmio: NoC base iospace - */ -struct qcom_icc_provider { - struct icc_provider provider; - struct clk_bulk_data *bus_clks; - int num_clks; - bool is_bimc_node; - struct regmap *regmap; - void __iomem *mmio; -}; - -/** - * struct qcom_icc_qos - Qualcomm specific interconnect QoS parameters - * @areq_prio: node requests priority - * @prio_level: priority level for bus communication - * @limit_commands: activate/deactivate limiter mode during runtime - * @ap_owned: indicates if the node is owned by the AP or by the RPM - * @qos_mode: default qos mode for this node - * @qos_port: qos port number for finding qos registers of this node - */ -struct qcom_icc_qos { - u32 areq_prio; - u32 prio_level; - bool limit_commands; - bool ap_owned; - int qos_mode; - int qos_port; -}; - -/** - * struct qcom_icc_node - Qualcomm specific interconnect nodes - * @name: the node name used in debugfs - * @id: a unique node identifier - * @links: an array of nodes where we can go next while traversing - * @num_links: the total number of @links - * @buswidth: width of the interconnect between a node and the bus (bytes) - * @mas_rpm_id: RPM id for devices that are bus masters - * @slv_rpm_id: RPM id for devices that are bus slaves - * @qos: NoC QoS setting parameters - * @rate: current bus clock rate in Hz - */ -struct qcom_icc_node { - unsigned char *name; - u16 id; - const u16 *links; - u16 num_links; - u16 buswidth; - int mas_rpm_id; - int slv_rpm_id; - struct qcom_icc_qos qos; - u64 rate; -}; - -struct qcom_icc_desc { - struct qcom_icc_node **nodes; - size_t num_nodes; - const struct regmap_config *regmap_cfg; +static const char * const bus_a2noc_clocks[] = { + "bus", + "bus_a", + "ipa", + "ufs_axi", + "aggre2_ufs_axi", + "aggre2_usb3_axi", + "cfg_noc_usb2_axi", }; static const u16 mas_ipa_links[] = { @@ -1622,6 +1515,8 @@ static const struct regmap_config sdm660_a2noc_regmap_config = { static struct qcom_icc_desc sdm660_a2noc = { .nodes = sdm660_a2noc_nodes, .num_nodes = ARRAY_SIZE(sdm660_a2noc_nodes), + .clocks = bus_a2noc_clocks, + .num_clocks = ARRAY_SIZE(bus_a2noc_clocks), .regmap_cfg = &sdm660_a2noc_regmap_config, }; @@ -1647,6 +1542,7 @@ static const struct regmap_config sdm660_bimc_regmap_config = { static struct qcom_icc_desc sdm660_bimc = { .nodes = sdm660_bimc_nodes, .num_nodes = ARRAY_SIZE(sdm660_bimc_nodes), + .is_bimc_node = true, .regmap_cfg = &sdm660_bimc_regmap_config, }; @@ -1759,6 +1655,8 @@ static const struct regmap_config sdm660_mnoc_regmap_config = { static struct qcom_icc_desc sdm660_mnoc = { .nodes = sdm660_mnoc_nodes, .num_nodes = ARRAY_SIZE(sdm660_mnoc_nodes), + .clocks = bus_mm_clocks, + .num_clocks = ARRAY_SIZE(bus_mm_clocks), .regmap_cfg = &sdm660_mnoc_regmap_config, }; @@ -1796,353 +1694,6 @@ static struct qcom_icc_desc sdm660_snoc = { .regmap_cfg = &sdm660_snoc_regmap_config, }; -static int qcom_icc_bimc_set_qos_health(struct regmap *rmap, - struct qcom_icc_qos *qos, - int regnum) -{ - u32 val; - u32 mask; - - val = qos->prio_level; - mask = M_BKE_HEALTH_CFG_PRIOLVL_MASK; - - val |= qos->areq_prio << M_BKE_HEALTH_CFG_AREQPRIO_SHIFT; - mask |= M_BKE_HEALTH_CFG_AREQPRIO_MASK; - - /* LIMITCMDS is not present on M_BKE_HEALTH_3 */ - if (regnum != 3) { - val |= qos->limit_commands << M_BKE_HEALTH_CFG_LIMITCMDS_SHIFT; - mask |= M_BKE_HEALTH_CFG_LIMITCMDS_MASK; - } - - return regmap_update_bits(rmap, - M_BKE_HEALTH_CFG_ADDR(regnum, qos->qos_port), - mask, val); -} - -static int qcom_icc_set_bimc_qos(struct icc_node *src, u64 max_bw, - bool bypass_mode) -{ - struct qcom_icc_provider *qp; - struct qcom_icc_node *qn; - struct icc_provider *provider; - u32 mode = NOC_QOS_MODE_BYPASS; - u32 val = 0; - int i, rc = 0; - - qn = src->data; - provider = src->provider; - qp = to_qcom_provider(provider); - - if (qn->qos.qos_mode != NOC_QOS_MODE_INVALID) - mode = qn->qos.qos_mode; - - /* QoS Priority: The QoS Health parameters are getting considered - * only if we are NOT in Bypass Mode. - */ - if (mode != NOC_QOS_MODE_BYPASS) { - for (i = 3; i >= 0; i--) { - rc = qcom_icc_bimc_set_qos_health(qp->regmap, - &qn->qos, i); - if (rc) - return rc; - } - - /* Set BKE_EN to 1 when Fixed, Regulator or Limiter Mode */ - val = 1; - } - - return regmap_update_bits(qp->regmap, M_BKE_EN_ADDR(qn->qos.qos_port), - M_BKE_EN_EN_BMASK, val); -} - -static int qcom_icc_noc_set_qos_priority(struct regmap *rmap, - struct qcom_icc_qos *qos) -{ - u32 val; - int rc; - - /* Must be updated one at a time, P1 first, P0 last */ - val = qos->areq_prio << NOC_QOS_PRIORITY_P1_SHIFT; - rc = regmap_update_bits(rmap, NOC_QOS_PRIORITYn_ADDR(qos->qos_port), - NOC_QOS_PRIORITY_P1_MASK, val); - if (rc) - return rc; - - return regmap_update_bits(rmap, NOC_QOS_PRIORITYn_ADDR(qos->qos_port), - NOC_QOS_PRIORITY_P0_MASK, qos->prio_level); -} - -static int qcom_icc_set_noc_qos(struct icc_node *src, u64 max_bw) -{ - struct qcom_icc_provider *qp; - struct qcom_icc_node *qn; - struct icc_provider *provider; - u32 mode = NOC_QOS_MODE_BYPASS; - int rc = 0; - - qn = src->data; - provider = src->provider; - qp = to_qcom_provider(provider); - - if (qn->qos.qos_port < 0) { - dev_dbg(src->provider->dev, - "NoC QoS: Skipping %s: vote aggregated on parent.\n", - qn->name); - return 0; - } - - if (qn->qos.qos_mode != NOC_QOS_MODE_INVALID) - mode = qn->qos.qos_mode; - - if (mode == NOC_QOS_MODE_FIXED) { - dev_dbg(src->provider->dev, "NoC QoS: %s: Set Fixed mode\n", - qn->name); - rc = qcom_icc_noc_set_qos_priority(qp->regmap, &qn->qos); - if (rc) - return rc; - } else if (mode == NOC_QOS_MODE_BYPASS) { - dev_dbg(src->provider->dev, "NoC QoS: %s: Set Bypass mode\n", - qn->name); - } - - return regmap_update_bits(qp->regmap, - NOC_QOS_MODEn_ADDR(qn->qos.qos_port), - NOC_QOS_MODEn_MASK, mode); -} - -static int qcom_icc_qos_set(struct icc_node *node, u64 sum_bw) -{ - struct qcom_icc_provider *qp = to_qcom_provider(node->provider); - struct qcom_icc_node *qn = node->data; - - dev_dbg(node->provider->dev, "Setting QoS for %s\n", qn->name); - - if (qp->is_bimc_node) - return qcom_icc_set_bimc_qos(node, sum_bw, - (qn->qos.qos_mode == NOC_QOS_MODE_BYPASS)); - - return qcom_icc_set_noc_qos(node, sum_bw); -} - -static int qcom_icc_rpm_set(int mas_rpm_id, int slv_rpm_id, u64 sum_bw) -{ - int ret = 0; - - if (mas_rpm_id != -1) { - ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE, - RPM_BUS_MASTER_REQ, - mas_rpm_id, - sum_bw); - if (ret) { - pr_err("qcom_icc_rpm_smd_send mas %d error %d\n", - mas_rpm_id, ret); - return ret; - } - } - - if (slv_rpm_id != -1) { - ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE, - RPM_BUS_SLAVE_REQ, - slv_rpm_id, - sum_bw); - if (ret) { - pr_err("qcom_icc_rpm_smd_send slv %d error %d\n", - slv_rpm_id, ret); - return ret; - } - } - - return ret; -} - -static int qcom_icc_set(struct icc_node *src, struct icc_node *dst) -{ - struct qcom_icc_provider *qp; - struct qcom_icc_node *qn; - struct icc_provider *provider; - struct icc_node *n; - u64 sum_bw; - u64 max_peak_bw; - u64 rate; - u32 agg_avg = 0; - u32 agg_peak = 0; - int ret, i; - - qn = src->data; - provider = src->provider; - qp = to_qcom_provider(provider); - - list_for_each_entry(n, &provider->nodes, node_list) - provider->aggregate(n, 0, n->avg_bw, n->peak_bw, - &agg_avg, &agg_peak); - - sum_bw = icc_units_to_bps(agg_avg); - max_peak_bw = icc_units_to_bps(agg_peak); - - if (!qn->qos.ap_owned) { - /* send bandwidth request message to the RPM processor */ - ret = qcom_icc_rpm_set(qn->mas_rpm_id, qn->slv_rpm_id, sum_bw); - if (ret) - return ret; - } else if (qn->qos.qos_mode != NOC_QOS_MODE_INVALID) { - /* set bandwidth directly from the AP */ - ret = qcom_icc_qos_set(src, sum_bw); - if (ret) - return ret; - } - - rate = max(sum_bw, max_peak_bw); - - do_div(rate, qn->buswidth); - - if (qn->rate == rate) - return 0; - - for (i = 0; i < qp->num_clks; i++) { - ret = clk_set_rate(qp->bus_clks[i].clk, rate); - if (ret) { - pr_err("%s clk_set_rate error: %d\n", - qp->bus_clks[i].id, ret); - return ret; - } - } - - qn->rate = rate; - - return 0; -} - -static int qnoc_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - const struct qcom_icc_desc *desc; - struct icc_onecell_data *data; - struct icc_provider *provider; - struct qcom_icc_node **qnodes; - struct qcom_icc_provider *qp; - struct icc_node *node; - struct resource *res; - size_t num_nodes, i; - int ret; - - /* wait for the RPM proxy */ - if (!qcom_icc_rpm_smd_available()) - return -EPROBE_DEFER; - - desc = of_device_get_match_data(dev); - if (!desc) - return -EINVAL; - - qnodes = desc->nodes; - num_nodes = desc->num_nodes; - - qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL); - if (!qp) - return -ENOMEM; - - data = devm_kzalloc(dev, struct_size(data, nodes, num_nodes), - GFP_KERNEL); - if (!data) - return -ENOMEM; - - if (of_device_is_compatible(dev->of_node, "qcom,sdm660-mnoc")) { - qp->bus_clks = devm_kmemdup(dev, bus_mm_clocks, - sizeof(bus_mm_clocks), GFP_KERNEL); - qp->num_clks = ARRAY_SIZE(bus_mm_clocks); - } else if (of_device_is_compatible(dev->of_node, "qcom,sdm660-a2noc")) { - qp->bus_clks = devm_kmemdup(dev, bus_a2noc_clocks, - sizeof(bus_a2noc_clocks), GFP_KERNEL); - qp->num_clks = ARRAY_SIZE(bus_a2noc_clocks); - } else { - if (of_device_is_compatible(dev->of_node, "qcom,sdm660-bimc")) - qp->is_bimc_node = true; - - qp->bus_clks = devm_kmemdup(dev, bus_clocks, sizeof(bus_clocks), - GFP_KERNEL); - qp->num_clks = ARRAY_SIZE(bus_clocks); - } - if (!qp->bus_clks) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - - qp->mmio = devm_ioremap_resource(dev, res); - if (IS_ERR(qp->mmio)) { - dev_err(dev, "Cannot ioremap interconnect bus resource\n"); - return PTR_ERR(qp->mmio); - } - - qp->regmap = devm_regmap_init_mmio(dev, qp->mmio, desc->regmap_cfg); - if (IS_ERR(qp->regmap)) { - dev_err(dev, "Cannot regmap interconnect bus resource\n"); - return PTR_ERR(qp->regmap); - } - - ret = devm_clk_bulk_get(dev, qp->num_clks, qp->bus_clks); - if (ret) - return ret; - - ret = clk_bulk_prepare_enable(qp->num_clks, qp->bus_clks); - if (ret) - return ret; - - provider = &qp->provider; - INIT_LIST_HEAD(&provider->nodes); - provider->dev = dev; - provider->set = qcom_icc_set; - provider->aggregate = icc_std_aggregate; - provider->xlate = of_icc_xlate_onecell; - provider->data = data; - - ret = icc_provider_add(provider); - if (ret) { - dev_err(dev, "error adding interconnect provider: %d\n", ret); - clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); - return ret; - } - - for (i = 0; i < num_nodes; i++) { - size_t j; - - node = icc_node_create(qnodes[i]->id); - if (IS_ERR(node)) { - ret = PTR_ERR(node); - goto err; - } - - node->name = qnodes[i]->name; - node->data = qnodes[i]; - icc_node_add(node, provider); - - for (j = 0; j < qnodes[i]->num_links; j++) - icc_link_create(node, qnodes[i]->links[j]); - - data->nodes[i] = node; - } - data->num_nodes = num_nodes; - platform_set_drvdata(pdev, qp); - - return 0; -err: - icc_nodes_remove(provider); - clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); - icc_provider_del(provider); - - return ret; -} - -static int qnoc_remove(struct platform_device *pdev) -{ - struct qcom_icc_provider *qp = platform_get_drvdata(pdev); - - icc_nodes_remove(&qp->provider); - clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); - return icc_provider_del(&qp->provider); -} - static const struct of_device_id sdm660_noc_of_match[] = { { .compatible = "qcom,sdm660-a2noc", .data = &sdm660_a2noc }, { .compatible = "qcom,sdm660-bimc", .data = &sdm660_bimc }, -- cgit v1.2.3-70-g09d2 From 0788f4d575831e440eb5a5c04a4c126151c47ff7 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 4 Sep 2021 02:24:15 +0300 Subject: interconnect: icc-rpm: add support for QoS reg offset SDM660 driver expects to have QoS registers at the beginning of NoC address space (sdm660 platform shifts NoC base address). Add support for using QoS register offset, so that other platforms do not have to change existing device trees. Signed-off-by: Dmitry Baryshkov Reviewed-by: AngeloGioacchino Del Regno Tested-by: Shawn Guo Link: https://lore.kernel.org/r/20210903232421.1384199-6-dmitry.baryshkov@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 24 ++++++++++++++---------- drivers/interconnect/qcom/icc-rpm.h | 3 +++ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index 96a160edece9..ef7999a08c8b 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -39,7 +39,7 @@ #define NOC_QOS_MODEn_ADDR(n) (0xc + (n * 0x1000)) #define NOC_QOS_MODEn_MASK 0x3 -static int qcom_icc_bimc_set_qos_health(struct regmap *rmap, +static int qcom_icc_bimc_set_qos_health(struct qcom_icc_provider *qp, struct qcom_icc_qos *qos, int regnum) { @@ -58,8 +58,8 @@ static int qcom_icc_bimc_set_qos_health(struct regmap *rmap, mask |= M_BKE_HEALTH_CFG_LIMITCMDS_MASK; } - return regmap_update_bits(rmap, - M_BKE_HEALTH_CFG_ADDR(regnum, qos->qos_port), + return regmap_update_bits(qp->regmap, + qp->qos_offset + M_BKE_HEALTH_CFG_ADDR(regnum, qos->qos_port), mask, val); } @@ -84,7 +84,7 @@ static int qcom_icc_set_bimc_qos(struct icc_node *src, u64 max_bw) */ if (mode != NOC_QOS_MODE_BYPASS) { for (i = 3; i >= 0; i--) { - rc = qcom_icc_bimc_set_qos_health(qp->regmap, + rc = qcom_icc_bimc_set_qos_health(qp, &qn->qos, i); if (rc) return rc; @@ -94,11 +94,12 @@ static int qcom_icc_set_bimc_qos(struct icc_node *src, u64 max_bw) val = 1; } - return regmap_update_bits(qp->regmap, M_BKE_EN_ADDR(qn->qos.qos_port), + return regmap_update_bits(qp->regmap, + qp->qos_offset + M_BKE_EN_ADDR(qn->qos.qos_port), M_BKE_EN_EN_BMASK, val); } -static int qcom_icc_noc_set_qos_priority(struct regmap *rmap, +static int qcom_icc_noc_set_qos_priority(struct qcom_icc_provider *qp, struct qcom_icc_qos *qos) { u32 val; @@ -106,12 +107,14 @@ static int qcom_icc_noc_set_qos_priority(struct regmap *rmap, /* Must be updated one at a time, P1 first, P0 last */ val = qos->areq_prio << NOC_QOS_PRIORITY_P1_SHIFT; - rc = regmap_update_bits(rmap, NOC_QOS_PRIORITYn_ADDR(qos->qos_port), + rc = regmap_update_bits(qp->regmap, + qp->qos_offset + NOC_QOS_PRIORITYn_ADDR(qos->qos_port), NOC_QOS_PRIORITY_P1_MASK, val); if (rc) return rc; - return regmap_update_bits(rmap, NOC_QOS_PRIORITYn_ADDR(qos->qos_port), + return regmap_update_bits(qp->regmap, + qp->qos_offset + NOC_QOS_PRIORITYn_ADDR(qos->qos_port), NOC_QOS_PRIORITY_P0_MASK, qos->prio_level); } @@ -140,7 +143,7 @@ static int qcom_icc_set_noc_qos(struct icc_node *src, u64 max_bw) if (mode == NOC_QOS_MODE_FIXED) { dev_dbg(src->provider->dev, "NoC QoS: %s: Set Fixed mode\n", qn->name); - rc = qcom_icc_noc_set_qos_priority(qp->regmap, &qn->qos); + rc = qcom_icc_noc_set_qos_priority(qp, &qn->qos); if (rc) return rc; } else if (mode == NOC_QOS_MODE_BYPASS) { @@ -149,7 +152,7 @@ static int qcom_icc_set_noc_qos(struct icc_node *src, u64 max_bw) } return regmap_update_bits(qp->regmap, - NOC_QOS_MODEn_ADDR(qn->qos.qos_port), + qp->qos_offset + NOC_QOS_MODEn_ADDR(qn->qos.qos_port), NOC_QOS_MODEn_MASK, mode); } @@ -305,6 +308,7 @@ int qnoc_probe(struct platform_device *pdev) qp->num_clks = cd_num; qp->is_bimc_node = desc->is_bimc_node; + qp->qos_offset = desc->qos_offset; if (desc->regmap_cfg) { struct resource *res; diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index 25d11c6a73d7..0824ee34d816 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -18,6 +18,7 @@ * @bus_clks: the clk_bulk_data table of bus clocks * @num_clks: the total number of clk_bulk_data entries * @is_bimc_node: indicates whether to use bimc specific setting + * @qos_offset: offset to QoS registers * @regmap: regmap for QoS registers read/write access */ struct qcom_icc_provider { @@ -25,6 +26,7 @@ struct qcom_icc_provider { int num_clks; bool is_bimc_node; struct regmap *regmap; + unsigned int qos_offset; struct clk_bulk_data bus_clks[]; }; @@ -77,6 +79,7 @@ struct qcom_icc_desc { size_t num_clocks; bool is_bimc_node; const struct regmap_config *regmap_cfg; + unsigned int qos_offset; }; #define DEFINE_QNODE(_name, _id, _buswidth, _mas_rpm_id, _slv_rpm_id, \ -- cgit v1.2.3-70-g09d2 From 6b9bbedda02cfa7edda3b7556452f8b41109f836 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 4 Sep 2021 02:24:16 +0300 Subject: interconnect: msm8916: expand DEFINE_QNODE macros In preparation to adding AP-owned nodes support to msm8916 expand DEFINE_QNODE macros in the driver. Suggested-by: Bjorn Andersson Signed-off-by: Dmitry Baryshkov Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20210903232421.1384199-7-dmitry.baryshkov@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/msm8916.c | 1101 ++++++++++++++++++++++++++++++++--- 1 file changed, 1016 insertions(+), 85 deletions(-) diff --git a/drivers/interconnect/qcom/msm8916.c b/drivers/interconnect/qcom/msm8916.c index fc0d48d2997a..b7d662875c89 100644 --- a/drivers/interconnect/qcom/msm8916.c +++ b/drivers/interconnect/qcom/msm8916.c @@ -105,91 +105,1022 @@ enum { MSM8916_SNOC_PNOC_SLV, }; -DEFINE_QNODE(bimc_snoc_mas, MSM8916_BIMC_SNOC_MAS, 8, -1, -1, MSM8916_BIMC_SNOC_SLV); -DEFINE_QNODE(bimc_snoc_slv, MSM8916_BIMC_SNOC_SLV, 8, -1, -1, MSM8916_SNOC_INT_0, MSM8916_SNOC_INT_1); -DEFINE_QNODE(mas_apss, MSM8916_MASTER_AMPSS_M0, 8, -1, -1, MSM8916_SLAVE_EBI_CH0, MSM8916_BIMC_SNOC_MAS, MSM8916_SLAVE_AMPSS_L2); -DEFINE_QNODE(mas_audio, MSM8916_MASTER_LPASS, 4, -1, -1, MSM8916_PNOC_MAS_0); -DEFINE_QNODE(mas_blsp_1, MSM8916_MASTER_BLSP_1, 4, -1, -1, MSM8916_PNOC_MAS_1); -DEFINE_QNODE(mas_dehr, MSM8916_MASTER_DEHR, 4, -1, -1, MSM8916_PNOC_MAS_0); -DEFINE_QNODE(mas_gfx, MSM8916_MASTER_GRAPHICS_3D, 8, -1, -1, MSM8916_SLAVE_EBI_CH0, MSM8916_BIMC_SNOC_MAS, MSM8916_SLAVE_AMPSS_L2); -DEFINE_QNODE(mas_jpeg, MSM8916_MASTER_JPEG, 16, -1, -1, MSM8916_SNOC_MM_INT_0, MSM8916_SNOC_MM_INT_2); -DEFINE_QNODE(mas_mdp, MSM8916_MASTER_MDP_PORT0, 16, -1, -1, MSM8916_SNOC_MM_INT_0, MSM8916_SNOC_MM_INT_2); -DEFINE_QNODE(mas_pcnoc_crypto_0, MSM8916_MASTER_CRYPTO_CORE0, 8, -1, -1, MSM8916_PNOC_INT_1); -DEFINE_QNODE(mas_pcnoc_sdcc_1, MSM8916_MASTER_SDCC_1, 8, -1, -1, MSM8916_PNOC_INT_1); -DEFINE_QNODE(mas_pcnoc_sdcc_2, MSM8916_MASTER_SDCC_2, 8, -1, -1, MSM8916_PNOC_INT_1); -DEFINE_QNODE(mas_qdss_bam, MSM8916_MASTER_QDSS_BAM, 8, -1, -1, MSM8916_SNOC_QDSS_INT); -DEFINE_QNODE(mas_qdss_etr, MSM8916_MASTER_QDSS_ETR, 8, -1, -1, MSM8916_SNOC_QDSS_INT); -DEFINE_QNODE(mas_snoc_cfg, MSM8916_MASTER_SNOC_CFG, 4, -1, -1, MSM8916_SNOC_QDSS_INT); -DEFINE_QNODE(mas_spdm, MSM8916_MASTER_SPDM, 4, -1, -1, MSM8916_PNOC_MAS_0); -DEFINE_QNODE(mas_tcu0, MSM8916_MASTER_TCU0, 8, -1, -1, MSM8916_SLAVE_EBI_CH0, MSM8916_BIMC_SNOC_MAS, MSM8916_SLAVE_AMPSS_L2); -DEFINE_QNODE(mas_tcu1, MSM8916_MASTER_TCU1, 8, -1, -1, MSM8916_SLAVE_EBI_CH0, MSM8916_BIMC_SNOC_MAS, MSM8916_SLAVE_AMPSS_L2); -DEFINE_QNODE(mas_usb_hs, MSM8916_MASTER_USB_HS, 4, -1, -1, MSM8916_PNOC_MAS_1); -DEFINE_QNODE(mas_vfe, MSM8916_MASTER_VFE, 16, -1, -1, MSM8916_SNOC_MM_INT_1, MSM8916_SNOC_MM_INT_2); -DEFINE_QNODE(mas_video, MSM8916_MASTER_VIDEO_P0, 16, -1, -1, MSM8916_SNOC_MM_INT_0, MSM8916_SNOC_MM_INT_2); -DEFINE_QNODE(mm_int_0, MSM8916_SNOC_MM_INT_0, 16, -1, -1, MSM8916_SNOC_MM_INT_BIMC); -DEFINE_QNODE(mm_int_1, MSM8916_SNOC_MM_INT_1, 16, -1, -1, MSM8916_SNOC_MM_INT_BIMC); -DEFINE_QNODE(mm_int_2, MSM8916_SNOC_MM_INT_2, 16, -1, -1, MSM8916_SNOC_INT_0); -DEFINE_QNODE(mm_int_bimc, MSM8916_SNOC_MM_INT_BIMC, 16, -1, -1, MSM8916_SNOC_BIMC_1_MAS); -DEFINE_QNODE(pcnoc_int_0, MSM8916_PNOC_INT_0, 8, -1, -1, MSM8916_PNOC_SNOC_MAS, MSM8916_PNOC_SLV_0, MSM8916_PNOC_SLV_1, MSM8916_PNOC_SLV_2, MSM8916_PNOC_SLV_3, MSM8916_PNOC_SLV_4, MSM8916_PNOC_SLV_8, MSM8916_PNOC_SLV_9); -DEFINE_QNODE(pcnoc_int_1, MSM8916_PNOC_INT_1, 8, -1, -1, MSM8916_PNOC_SNOC_MAS); -DEFINE_QNODE(pcnoc_m_0, MSM8916_PNOC_MAS_0, 8, -1, -1, MSM8916_PNOC_INT_0); -DEFINE_QNODE(pcnoc_m_1, MSM8916_PNOC_MAS_1, 8, -1, -1, MSM8916_PNOC_SNOC_MAS); -DEFINE_QNODE(pcnoc_s_0, MSM8916_PNOC_SLV_0, 4, -1, -1, MSM8916_SLAVE_CLK_CTL, MSM8916_SLAVE_TLMM, MSM8916_SLAVE_TCSR, MSM8916_SLAVE_SECURITY, MSM8916_SLAVE_MSS); -DEFINE_QNODE(pcnoc_s_1, MSM8916_PNOC_SLV_1, 4, -1, -1, MSM8916_SLAVE_IMEM_CFG, MSM8916_SLAVE_CRYPTO_0_CFG, MSM8916_SLAVE_MSG_RAM, MSM8916_SLAVE_PDM, MSM8916_SLAVE_PRNG); -DEFINE_QNODE(pcnoc_s_2, MSM8916_PNOC_SLV_2, 4, -1, -1, MSM8916_SLAVE_SPDM, MSM8916_SLAVE_BOOT_ROM, MSM8916_SLAVE_BIMC_CFG, MSM8916_SLAVE_PNOC_CFG, MSM8916_SLAVE_PMIC_ARB); -DEFINE_QNODE(pcnoc_s_3, MSM8916_PNOC_SLV_3, 4, -1, -1, MSM8916_SLAVE_MPM, MSM8916_SLAVE_SNOC_CFG, MSM8916_SLAVE_RBCPR_CFG, MSM8916_SLAVE_QDSS_CFG, MSM8916_SLAVE_DEHR_CFG); -DEFINE_QNODE(pcnoc_s_4, MSM8916_PNOC_SLV_4, 4, -1, -1, MSM8916_SLAVE_VENUS_CFG, MSM8916_SLAVE_CAMERA_CFG, MSM8916_SLAVE_DISPLAY_CFG); -DEFINE_QNODE(pcnoc_s_8, MSM8916_PNOC_SLV_8, 4, -1, -1, MSM8916_SLAVE_USB_HS, MSM8916_SLAVE_SDCC_1, MSM8916_SLAVE_BLSP_1); -DEFINE_QNODE(pcnoc_s_9, MSM8916_PNOC_SLV_9, 4, -1, -1, MSM8916_SLAVE_SDCC_2, MSM8916_SLAVE_LPASS, MSM8916_SLAVE_GRAPHICS_3D_CFG); -DEFINE_QNODE(pcnoc_snoc_mas, MSM8916_PNOC_SNOC_MAS, 8, 29, -1, MSM8916_PNOC_SNOC_SLV); -DEFINE_QNODE(pcnoc_snoc_slv, MSM8916_PNOC_SNOC_SLV, 8, -1, 45, MSM8916_SNOC_INT_0, MSM8916_SNOC_INT_BIMC, MSM8916_SNOC_INT_1); -DEFINE_QNODE(qdss_int, MSM8916_SNOC_QDSS_INT, 8, -1, -1, MSM8916_SNOC_INT_0, MSM8916_SNOC_INT_BIMC); -DEFINE_QNODE(slv_apps_l2, MSM8916_SLAVE_AMPSS_L2, 8, -1, -1, 0); -DEFINE_QNODE(slv_apss, MSM8916_SLAVE_APSS, 4, -1, -1, 0); -DEFINE_QNODE(slv_audio, MSM8916_SLAVE_LPASS, 4, -1, -1, 0); -DEFINE_QNODE(slv_bimc_cfg, MSM8916_SLAVE_BIMC_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_blsp_1, MSM8916_SLAVE_BLSP_1, 4, -1, -1, 0); -DEFINE_QNODE(slv_boot_rom, MSM8916_SLAVE_BOOT_ROM, 4, -1, -1, 0); -DEFINE_QNODE(slv_camera_cfg, MSM8916_SLAVE_CAMERA_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_cats_0, MSM8916_SLAVE_CATS_128, 16, -1, -1, 0); -DEFINE_QNODE(slv_cats_1, MSM8916_SLAVE_OCMEM_64, 8, -1, -1, 0); -DEFINE_QNODE(slv_clk_ctl, MSM8916_SLAVE_CLK_CTL, 4, -1, -1, 0); -DEFINE_QNODE(slv_crypto_0_cfg, MSM8916_SLAVE_CRYPTO_0_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_dehr_cfg, MSM8916_SLAVE_DEHR_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_display_cfg, MSM8916_SLAVE_DISPLAY_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_ebi_ch0, MSM8916_SLAVE_EBI_CH0, 8, -1, 0, 0); -DEFINE_QNODE(slv_gfx_cfg, MSM8916_SLAVE_GRAPHICS_3D_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_imem_cfg, MSM8916_SLAVE_IMEM_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_imem, MSM8916_SLAVE_IMEM, 8, -1, 26, 0); -DEFINE_QNODE(slv_mpm, MSM8916_SLAVE_MPM, 4, -1, -1, 0); -DEFINE_QNODE(slv_msg_ram, MSM8916_SLAVE_MSG_RAM, 4, -1, -1, 0); -DEFINE_QNODE(slv_mss, MSM8916_SLAVE_MSS, 4, -1, -1, 0); -DEFINE_QNODE(slv_pdm, MSM8916_SLAVE_PDM, 4, -1, -1, 0); -DEFINE_QNODE(slv_pmic_arb, MSM8916_SLAVE_PMIC_ARB, 4, -1, -1, 0); -DEFINE_QNODE(slv_pcnoc_cfg, MSM8916_SLAVE_PNOC_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_prng, MSM8916_SLAVE_PRNG, 4, -1, -1, 0); -DEFINE_QNODE(slv_qdss_cfg, MSM8916_SLAVE_QDSS_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_qdss_stm, MSM8916_SLAVE_QDSS_STM, 4, -1, 30, 0); -DEFINE_QNODE(slv_rbcpr_cfg, MSM8916_SLAVE_RBCPR_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_sdcc_1, MSM8916_SLAVE_SDCC_1, 4, -1, -1, 0); -DEFINE_QNODE(slv_sdcc_2, MSM8916_SLAVE_SDCC_2, 4, -1, -1, 0); -DEFINE_QNODE(slv_security, MSM8916_SLAVE_SECURITY, 4, -1, -1, 0); -DEFINE_QNODE(slv_snoc_cfg, MSM8916_SLAVE_SNOC_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_spdm, MSM8916_SLAVE_SPDM, 4, -1, -1, 0); -DEFINE_QNODE(slv_srvc_snoc, MSM8916_SLAVE_SRVC_SNOC, 8, -1, -1, 0); -DEFINE_QNODE(slv_tcsr, MSM8916_SLAVE_TCSR, 4, -1, -1, 0); -DEFINE_QNODE(slv_tlmm, MSM8916_SLAVE_TLMM, 4, -1, -1, 0); -DEFINE_QNODE(slv_usb_hs, MSM8916_SLAVE_USB_HS, 4, -1, -1, 0); -DEFINE_QNODE(slv_venus_cfg, MSM8916_SLAVE_VENUS_CFG, 4, -1, -1, 0); -DEFINE_QNODE(snoc_bimc_0_mas, MSM8916_SNOC_BIMC_0_MAS, 8, 3, -1, MSM8916_SNOC_BIMC_0_SLV); -DEFINE_QNODE(snoc_bimc_0_slv, MSM8916_SNOC_BIMC_0_SLV, 8, -1, 24, MSM8916_SLAVE_EBI_CH0); -DEFINE_QNODE(snoc_bimc_1_mas, MSM8916_SNOC_BIMC_1_MAS, 16, -1, -1, MSM8916_SNOC_BIMC_1_SLV); -DEFINE_QNODE(snoc_bimc_1_slv, MSM8916_SNOC_BIMC_1_SLV, 8, -1, -1, MSM8916_SLAVE_EBI_CH0); -DEFINE_QNODE(snoc_int_0, MSM8916_SNOC_INT_0, 8, 99, 130, MSM8916_SLAVE_QDSS_STM, MSM8916_SLAVE_IMEM, MSM8916_SNOC_PNOC_MAS); -DEFINE_QNODE(snoc_int_1, MSM8916_SNOC_INT_1, 8, -1, -1, MSM8916_SLAVE_APSS, MSM8916_SLAVE_CATS_128, MSM8916_SLAVE_OCMEM_64); -DEFINE_QNODE(snoc_int_bimc, MSM8916_SNOC_INT_BIMC, 8, 101, 132, MSM8916_SNOC_BIMC_0_MAS); -DEFINE_QNODE(snoc_pcnoc_mas, MSM8916_SNOC_PNOC_MAS, 8, -1, -1, MSM8916_SNOC_PNOC_SLV); -DEFINE_QNODE(snoc_pcnoc_slv, MSM8916_SNOC_PNOC_SLV, 8, -1, -1, MSM8916_PNOC_INT_0); +static const u16 bimc_snoc_mas_links[] = { + MSM8916_BIMC_SNOC_SLV +}; + +static struct qcom_icc_node bimc_snoc_mas = { + .name = "bimc_snoc_mas", + .id = MSM8916_BIMC_SNOC_MAS, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(bimc_snoc_mas_links), + .links = bimc_snoc_mas_links, +}; + +static const u16 bimc_snoc_slv_links[] = { + MSM8916_SNOC_INT_0, + MSM8916_SNOC_INT_1 +}; + +static struct qcom_icc_node bimc_snoc_slv = { + .name = "bimc_snoc_slv", + .id = MSM8916_BIMC_SNOC_SLV, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(bimc_snoc_slv_links), + .links = bimc_snoc_slv_links, +}; + +static const u16 mas_apss_links[] = { + MSM8916_SLAVE_EBI_CH0, + MSM8916_BIMC_SNOC_MAS, + MSM8916_SLAVE_AMPSS_L2 +}; + +static struct qcom_icc_node mas_apss = { + .name = "mas_apss", + .id = MSM8916_MASTER_AMPSS_M0, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_apss_links), + .links = mas_apss_links, +}; + +static const u16 mas_audio_links[] = { + MSM8916_PNOC_MAS_0 +}; + +static struct qcom_icc_node mas_audio = { + .name = "mas_audio", + .id = MSM8916_MASTER_LPASS, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_audio_links), + .links = mas_audio_links, +}; + +static const u16 mas_blsp_1_links[] = { + MSM8916_PNOC_MAS_1 +}; + +static struct qcom_icc_node mas_blsp_1 = { + .name = "mas_blsp_1", + .id = MSM8916_MASTER_BLSP_1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_blsp_1_links), + .links = mas_blsp_1_links, +}; + +static const u16 mas_dehr_links[] = { + MSM8916_PNOC_MAS_0 +}; + +static struct qcom_icc_node mas_dehr = { + .name = "mas_dehr", + .id = MSM8916_MASTER_DEHR, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_dehr_links), + .links = mas_dehr_links, +}; + +static const u16 mas_gfx_links[] = { + MSM8916_SLAVE_EBI_CH0, + MSM8916_BIMC_SNOC_MAS, + MSM8916_SLAVE_AMPSS_L2 +}; + +static struct qcom_icc_node mas_gfx = { + .name = "mas_gfx", + .id = MSM8916_MASTER_GRAPHICS_3D, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_gfx_links), + .links = mas_gfx_links, +}; + +static const u16 mas_jpeg_links[] = { + MSM8916_SNOC_MM_INT_0, + MSM8916_SNOC_MM_INT_2 +}; + +static struct qcom_icc_node mas_jpeg = { + .name = "mas_jpeg", + .id = MSM8916_MASTER_JPEG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_jpeg_links), + .links = mas_jpeg_links, +}; + +static const u16 mas_mdp_links[] = { + MSM8916_SNOC_MM_INT_0, + MSM8916_SNOC_MM_INT_2 +}; + +static struct qcom_icc_node mas_mdp = { + .name = "mas_mdp", + .id = MSM8916_MASTER_MDP_PORT0, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_mdp_links), + .links = mas_mdp_links, +}; + +static const u16 mas_pcnoc_crypto_0_links[] = { + MSM8916_PNOC_INT_1 +}; + +static struct qcom_icc_node mas_pcnoc_crypto_0 = { + .name = "mas_pcnoc_crypto_0", + .id = MSM8916_MASTER_CRYPTO_CORE0, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_pcnoc_crypto_0_links), + .links = mas_pcnoc_crypto_0_links, +}; + +static const u16 mas_pcnoc_sdcc_1_links[] = { + MSM8916_PNOC_INT_1 +}; + +static struct qcom_icc_node mas_pcnoc_sdcc_1 = { + .name = "mas_pcnoc_sdcc_1", + .id = MSM8916_MASTER_SDCC_1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_pcnoc_sdcc_1_links), + .links = mas_pcnoc_sdcc_1_links, +}; + +static const u16 mas_pcnoc_sdcc_2_links[] = { + MSM8916_PNOC_INT_1 +}; + +static struct qcom_icc_node mas_pcnoc_sdcc_2 = { + .name = "mas_pcnoc_sdcc_2", + .id = MSM8916_MASTER_SDCC_2, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_pcnoc_sdcc_2_links), + .links = mas_pcnoc_sdcc_2_links, +}; + +static const u16 mas_qdss_bam_links[] = { + MSM8916_SNOC_QDSS_INT +}; + +static struct qcom_icc_node mas_qdss_bam = { + .name = "mas_qdss_bam", + .id = MSM8916_MASTER_QDSS_BAM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_qdss_bam_links), + .links = mas_qdss_bam_links, +}; + +static const u16 mas_qdss_etr_links[] = { + MSM8916_SNOC_QDSS_INT +}; + +static struct qcom_icc_node mas_qdss_etr = { + .name = "mas_qdss_etr", + .id = MSM8916_MASTER_QDSS_ETR, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_qdss_etr_links), + .links = mas_qdss_etr_links, +}; + +static const u16 mas_snoc_cfg_links[] = { + MSM8916_SNOC_QDSS_INT +}; + +static struct qcom_icc_node mas_snoc_cfg = { + .name = "mas_snoc_cfg", + .id = MSM8916_MASTER_SNOC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_snoc_cfg_links), + .links = mas_snoc_cfg_links, +}; + +static const u16 mas_spdm_links[] = { + MSM8916_PNOC_MAS_0 +}; + +static struct qcom_icc_node mas_spdm = { + .name = "mas_spdm", + .id = MSM8916_MASTER_SPDM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_spdm_links), + .links = mas_spdm_links, +}; + +static const u16 mas_tcu0_links[] = { + MSM8916_SLAVE_EBI_CH0, + MSM8916_BIMC_SNOC_MAS, + MSM8916_SLAVE_AMPSS_L2 +}; + +static struct qcom_icc_node mas_tcu0 = { + .name = "mas_tcu0", + .id = MSM8916_MASTER_TCU0, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_tcu0_links), + .links = mas_tcu0_links, +}; + +static const u16 mas_tcu1_links[] = { + MSM8916_SLAVE_EBI_CH0, + MSM8916_BIMC_SNOC_MAS, + MSM8916_SLAVE_AMPSS_L2 +}; + +static struct qcom_icc_node mas_tcu1 = { + .name = "mas_tcu1", + .id = MSM8916_MASTER_TCU1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_tcu1_links), + .links = mas_tcu1_links, +}; + +static const u16 mas_usb_hs_links[] = { + MSM8916_PNOC_MAS_1 +}; + +static struct qcom_icc_node mas_usb_hs = { + .name = "mas_usb_hs", + .id = MSM8916_MASTER_USB_HS, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_usb_hs_links), + .links = mas_usb_hs_links, +}; + +static const u16 mas_vfe_links[] = { + MSM8916_SNOC_MM_INT_1, + MSM8916_SNOC_MM_INT_2 +}; + +static struct qcom_icc_node mas_vfe = { + .name = "mas_vfe", + .id = MSM8916_MASTER_VFE, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_vfe_links), + .links = mas_vfe_links, +}; + +static const u16 mas_video_links[] = { + MSM8916_SNOC_MM_INT_0, + MSM8916_SNOC_MM_INT_2 +}; + +static struct qcom_icc_node mas_video = { + .name = "mas_video", + .id = MSM8916_MASTER_VIDEO_P0, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_video_links), + .links = mas_video_links, +}; + +static const u16 mm_int_0_links[] = { + MSM8916_SNOC_MM_INT_BIMC +}; + +static struct qcom_icc_node mm_int_0 = { + .name = "mm_int_0", + .id = MSM8916_SNOC_MM_INT_0, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mm_int_0_links), + .links = mm_int_0_links, +}; + +static const u16 mm_int_1_links[] = { + MSM8916_SNOC_MM_INT_BIMC +}; + +static struct qcom_icc_node mm_int_1 = { + .name = "mm_int_1", + .id = MSM8916_SNOC_MM_INT_1, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mm_int_1_links), + .links = mm_int_1_links, +}; + +static const u16 mm_int_2_links[] = { + MSM8916_SNOC_INT_0 +}; + +static struct qcom_icc_node mm_int_2 = { + .name = "mm_int_2", + .id = MSM8916_SNOC_MM_INT_2, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mm_int_2_links), + .links = mm_int_2_links, +}; + +static const u16 mm_int_bimc_links[] = { + MSM8916_SNOC_BIMC_1_MAS +}; + +static struct qcom_icc_node mm_int_bimc = { + .name = "mm_int_bimc", + .id = MSM8916_SNOC_MM_INT_BIMC, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mm_int_bimc_links), + .links = mm_int_bimc_links, +}; + +static const u16 pcnoc_int_0_links[] = { + MSM8916_PNOC_SNOC_MAS, + MSM8916_PNOC_SLV_0, + MSM8916_PNOC_SLV_1, + MSM8916_PNOC_SLV_2, + MSM8916_PNOC_SLV_3, + MSM8916_PNOC_SLV_4, + MSM8916_PNOC_SLV_8, + MSM8916_PNOC_SLV_9 +}; + +static struct qcom_icc_node pcnoc_int_0 = { + .name = "pcnoc_int_0", + .id = MSM8916_PNOC_INT_0, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_int_0_links), + .links = pcnoc_int_0_links, +}; + +static const u16 pcnoc_int_1_links[] = { + MSM8916_PNOC_SNOC_MAS +}; + +static struct qcom_icc_node pcnoc_int_1 = { + .name = "pcnoc_int_1", + .id = MSM8916_PNOC_INT_1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_int_1_links), + .links = pcnoc_int_1_links, +}; + +static const u16 pcnoc_m_0_links[] = { + MSM8916_PNOC_INT_0 +}; + +static struct qcom_icc_node pcnoc_m_0 = { + .name = "pcnoc_m_0", + .id = MSM8916_PNOC_MAS_0, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_m_0_links), + .links = pcnoc_m_0_links, +}; + +static const u16 pcnoc_m_1_links[] = { + MSM8916_PNOC_SNOC_MAS +}; + +static struct qcom_icc_node pcnoc_m_1 = { + .name = "pcnoc_m_1", + .id = MSM8916_PNOC_MAS_1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_m_1_links), + .links = pcnoc_m_1_links, +}; + +static const u16 pcnoc_s_0_links[] = { + MSM8916_SLAVE_CLK_CTL, + MSM8916_SLAVE_TLMM, + MSM8916_SLAVE_TCSR, + MSM8916_SLAVE_SECURITY, + MSM8916_SLAVE_MSS +}; + +static struct qcom_icc_node pcnoc_s_0 = { + .name = "pcnoc_s_0", + .id = MSM8916_PNOC_SLV_0, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_s_0_links), + .links = pcnoc_s_0_links, +}; + +static const u16 pcnoc_s_1_links[] = { + MSM8916_SLAVE_IMEM_CFG, + MSM8916_SLAVE_CRYPTO_0_CFG, + MSM8916_SLAVE_MSG_RAM, + MSM8916_SLAVE_PDM, + MSM8916_SLAVE_PRNG +}; + +static struct qcom_icc_node pcnoc_s_1 = { + .name = "pcnoc_s_1", + .id = MSM8916_PNOC_SLV_1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_s_1_links), + .links = pcnoc_s_1_links, +}; + +static const u16 pcnoc_s_2_links[] = { + MSM8916_SLAVE_SPDM, + MSM8916_SLAVE_BOOT_ROM, + MSM8916_SLAVE_BIMC_CFG, + MSM8916_SLAVE_PNOC_CFG, + MSM8916_SLAVE_PMIC_ARB +}; + +static struct qcom_icc_node pcnoc_s_2 = { + .name = "pcnoc_s_2", + .id = MSM8916_PNOC_SLV_2, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_s_2_links), + .links = pcnoc_s_2_links, +}; + +static const u16 pcnoc_s_3_links[] = { + MSM8916_SLAVE_MPM, + MSM8916_SLAVE_SNOC_CFG, + MSM8916_SLAVE_RBCPR_CFG, + MSM8916_SLAVE_QDSS_CFG, + MSM8916_SLAVE_DEHR_CFG +}; + +static struct qcom_icc_node pcnoc_s_3 = { + .name = "pcnoc_s_3", + .id = MSM8916_PNOC_SLV_3, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_s_3_links), + .links = pcnoc_s_3_links, +}; + +static const u16 pcnoc_s_4_links[] = { + MSM8916_SLAVE_VENUS_CFG, + MSM8916_SLAVE_CAMERA_CFG, + MSM8916_SLAVE_DISPLAY_CFG +}; + +static struct qcom_icc_node pcnoc_s_4 = { + .name = "pcnoc_s_4", + .id = MSM8916_PNOC_SLV_4, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_s_4_links), + .links = pcnoc_s_4_links, +}; + +static const u16 pcnoc_s_8_links[] = { + MSM8916_SLAVE_USB_HS, + MSM8916_SLAVE_SDCC_1, + MSM8916_SLAVE_BLSP_1 +}; + +static struct qcom_icc_node pcnoc_s_8 = { + .name = "pcnoc_s_8", + .id = MSM8916_PNOC_SLV_8, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_s_8_links), + .links = pcnoc_s_8_links, +}; + +static const u16 pcnoc_s_9_links[] = { + MSM8916_SLAVE_SDCC_2, + MSM8916_SLAVE_LPASS, + MSM8916_SLAVE_GRAPHICS_3D_CFG +}; + +static struct qcom_icc_node pcnoc_s_9 = { + .name = "pcnoc_s_9", + .id = MSM8916_PNOC_SLV_9, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_s_9_links), + .links = pcnoc_s_9_links, +}; + +static const u16 pcnoc_snoc_mas_links[] = { + MSM8916_PNOC_SNOC_SLV +}; + +static struct qcom_icc_node pcnoc_snoc_mas = { + .name = "pcnoc_snoc_mas", + .id = MSM8916_PNOC_SNOC_MAS, + .buswidth = 8, + .mas_rpm_id = 29, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_snoc_mas_links), + .links = pcnoc_snoc_mas_links, +}; + +static const u16 pcnoc_snoc_slv_links[] = { + MSM8916_SNOC_INT_0, + MSM8916_SNOC_INT_BIMC, + MSM8916_SNOC_INT_1 +}; + +static struct qcom_icc_node pcnoc_snoc_slv = { + .name = "pcnoc_snoc_slv", + .id = MSM8916_PNOC_SNOC_SLV, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 45, + .num_links = ARRAY_SIZE(pcnoc_snoc_slv_links), + .links = pcnoc_snoc_slv_links, +}; + +static const u16 qdss_int_links[] = { + MSM8916_SNOC_INT_0, + MSM8916_SNOC_INT_BIMC +}; + +static struct qcom_icc_node qdss_int = { + .name = "qdss_int", + .id = MSM8916_SNOC_QDSS_INT, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qdss_int_links), + .links = qdss_int_links, +}; + +static struct qcom_icc_node slv_apps_l2 = { + .name = "slv_apps_l2", + .id = MSM8916_SLAVE_AMPSS_L2, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_apss = { + .name = "slv_apss", + .id = MSM8916_SLAVE_APSS, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_audio = { + .name = "slv_audio", + .id = MSM8916_SLAVE_LPASS, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_bimc_cfg = { + .name = "slv_bimc_cfg", + .id = MSM8916_SLAVE_BIMC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_blsp_1 = { + .name = "slv_blsp_1", + .id = MSM8916_SLAVE_BLSP_1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_boot_rom = { + .name = "slv_boot_rom", + .id = MSM8916_SLAVE_BOOT_ROM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_camera_cfg = { + .name = "slv_camera_cfg", + .id = MSM8916_SLAVE_CAMERA_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_cats_0 = { + .name = "slv_cats_0", + .id = MSM8916_SLAVE_CATS_128, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_cats_1 = { + .name = "slv_cats_1", + .id = MSM8916_SLAVE_OCMEM_64, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_clk_ctl = { + .name = "slv_clk_ctl", + .id = MSM8916_SLAVE_CLK_CTL, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_crypto_0_cfg = { + .name = "slv_crypto_0_cfg", + .id = MSM8916_SLAVE_CRYPTO_0_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_dehr_cfg = { + .name = "slv_dehr_cfg", + .id = MSM8916_SLAVE_DEHR_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_display_cfg = { + .name = "slv_display_cfg", + .id = MSM8916_SLAVE_DISPLAY_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_ebi_ch0 = { + .name = "slv_ebi_ch0", + .id = MSM8916_SLAVE_EBI_CH0, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 0, +}; + +static struct qcom_icc_node slv_gfx_cfg = { + .name = "slv_gfx_cfg", + .id = MSM8916_SLAVE_GRAPHICS_3D_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_imem_cfg = { + .name = "slv_imem_cfg", + .id = MSM8916_SLAVE_IMEM_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_imem = { + .name = "slv_imem", + .id = MSM8916_SLAVE_IMEM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 26, +}; + +static struct qcom_icc_node slv_mpm = { + .name = "slv_mpm", + .id = MSM8916_SLAVE_MPM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_msg_ram = { + .name = "slv_msg_ram", + .id = MSM8916_SLAVE_MSG_RAM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_mss = { + .name = "slv_mss", + .id = MSM8916_SLAVE_MSS, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_pdm = { + .name = "slv_pdm", + .id = MSM8916_SLAVE_PDM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_pmic_arb = { + .name = "slv_pmic_arb", + .id = MSM8916_SLAVE_PMIC_ARB, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_pcnoc_cfg = { + .name = "slv_pcnoc_cfg", + .id = MSM8916_SLAVE_PNOC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_prng = { + .name = "slv_prng", + .id = MSM8916_SLAVE_PRNG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_qdss_cfg = { + .name = "slv_qdss_cfg", + .id = MSM8916_SLAVE_QDSS_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_qdss_stm = { + .name = "slv_qdss_stm", + .id = MSM8916_SLAVE_QDSS_STM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 30, +}; + +static struct qcom_icc_node slv_rbcpr_cfg = { + .name = "slv_rbcpr_cfg", + .id = MSM8916_SLAVE_RBCPR_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_sdcc_1 = { + .name = "slv_sdcc_1", + .id = MSM8916_SLAVE_SDCC_1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_sdcc_2 = { + .name = "slv_sdcc_2", + .id = MSM8916_SLAVE_SDCC_2, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_security = { + .name = "slv_security", + .id = MSM8916_SLAVE_SECURITY, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_snoc_cfg = { + .name = "slv_snoc_cfg", + .id = MSM8916_SLAVE_SNOC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_spdm = { + .name = "slv_spdm", + .id = MSM8916_SLAVE_SPDM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_srvc_snoc = { + .name = "slv_srvc_snoc", + .id = MSM8916_SLAVE_SRVC_SNOC, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_tcsr = { + .name = "slv_tcsr", + .id = MSM8916_SLAVE_TCSR, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_tlmm = { + .name = "slv_tlmm", + .id = MSM8916_SLAVE_TLMM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_usb_hs = { + .name = "slv_usb_hs", + .id = MSM8916_SLAVE_USB_HS, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_venus_cfg = { + .name = "slv_venus_cfg", + .id = MSM8916_SLAVE_VENUS_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static const u16 snoc_bimc_0_mas_links[] = { + MSM8916_SNOC_BIMC_0_SLV +}; + +static struct qcom_icc_node snoc_bimc_0_mas = { + .name = "snoc_bimc_0_mas", + .id = MSM8916_SNOC_BIMC_0_MAS, + .buswidth = 8, + .mas_rpm_id = 3, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(snoc_bimc_0_mas_links), + .links = snoc_bimc_0_mas_links, +}; + +static const u16 snoc_bimc_0_slv_links[] = { + MSM8916_SLAVE_EBI_CH0 +}; + +static struct qcom_icc_node snoc_bimc_0_slv = { + .name = "snoc_bimc_0_slv", + .id = MSM8916_SNOC_BIMC_0_SLV, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 24, + .num_links = ARRAY_SIZE(snoc_bimc_0_slv_links), + .links = snoc_bimc_0_slv_links, +}; + +static const u16 snoc_bimc_1_mas_links[] = { + MSM8916_SNOC_BIMC_1_SLV +}; + +static struct qcom_icc_node snoc_bimc_1_mas = { + .name = "snoc_bimc_1_mas", + .id = MSM8916_SNOC_BIMC_1_MAS, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(snoc_bimc_1_mas_links), + .links = snoc_bimc_1_mas_links, +}; + +static const u16 snoc_bimc_1_slv_links[] = { + MSM8916_SLAVE_EBI_CH0 +}; + +static struct qcom_icc_node snoc_bimc_1_slv = { + .name = "snoc_bimc_1_slv", + .id = MSM8916_SNOC_BIMC_1_SLV, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(snoc_bimc_1_slv_links), + .links = snoc_bimc_1_slv_links, +}; + +static const u16 snoc_int_0_links[] = { + MSM8916_SLAVE_QDSS_STM, + MSM8916_SLAVE_IMEM, + MSM8916_SNOC_PNOC_MAS +}; + +static struct qcom_icc_node snoc_int_0 = { + .name = "snoc_int_0", + .id = MSM8916_SNOC_INT_0, + .buswidth = 8, + .mas_rpm_id = 99, + .slv_rpm_id = 130, + .num_links = ARRAY_SIZE(snoc_int_0_links), + .links = snoc_int_0_links, +}; + +static const u16 snoc_int_1_links[] = { + MSM8916_SLAVE_APSS, + MSM8916_SLAVE_CATS_128, + MSM8916_SLAVE_OCMEM_64 +}; + +static struct qcom_icc_node snoc_int_1 = { + .name = "snoc_int_1", + .id = MSM8916_SNOC_INT_1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(snoc_int_1_links), + .links = snoc_int_1_links, +}; + +static const u16 snoc_int_bimc_links[] = { + MSM8916_SNOC_BIMC_0_MAS +}; + +static struct qcom_icc_node snoc_int_bimc = { + .name = "snoc_int_bimc", + .id = MSM8916_SNOC_INT_BIMC, + .buswidth = 8, + .mas_rpm_id = 101, + .slv_rpm_id = 132, + .num_links = ARRAY_SIZE(snoc_int_bimc_links), + .links = snoc_int_bimc_links, +}; + +static const u16 snoc_pcnoc_mas_links[] = { + MSM8916_SNOC_PNOC_SLV +}; + +static struct qcom_icc_node snoc_pcnoc_mas = { + .name = "snoc_pcnoc_mas", + .id = MSM8916_SNOC_PNOC_MAS, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(snoc_pcnoc_mas_links), + .links = snoc_pcnoc_mas_links, +}; + +static const u16 snoc_pcnoc_slv_links[] = { + MSM8916_PNOC_INT_0 +}; + +static struct qcom_icc_node snoc_pcnoc_slv = { + .name = "snoc_pcnoc_slv", + .id = MSM8916_SNOC_PNOC_SLV, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(snoc_pcnoc_slv_links), + .links = snoc_pcnoc_slv_links, +}; static struct qcom_icc_node *msm8916_snoc_nodes[] = { [BIMC_SNOC_SLV] = &bimc_snoc_slv, -- cgit v1.2.3-70-g09d2 From cbf91c87153e06c8fa216deebf459152a966f09f Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 4 Sep 2021 02:24:17 +0300 Subject: interconnect: msm8916: add support for AP-owned nodes Port support for AP-owned nodes from the downstream device tree. Signed-off-by: Dmitry Baryshkov Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20210903232421.1384199-8-dmitry.baryshkov@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/msm8916.c | 100 ++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/drivers/interconnect/qcom/msm8916.c b/drivers/interconnect/qcom/msm8916.c index b7d662875c89..e3c995b11357 100644 --- a/drivers/interconnect/qcom/msm8916.c +++ b/drivers/interconnect/qcom/msm8916.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -115,6 +116,8 @@ static struct qcom_icc_node bimc_snoc_mas = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(bimc_snoc_mas_links), .links = bimc_snoc_mas_links, }; @@ -130,6 +133,8 @@ static struct qcom_icc_node bimc_snoc_slv = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(bimc_snoc_slv_links), .links = bimc_snoc_slv_links, }; @@ -146,6 +151,11 @@ static struct qcom_icc_node mas_apss = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 0, .num_links = ARRAY_SIZE(mas_apss_links), .links = mas_apss_links, }; @@ -204,6 +214,11 @@ static struct qcom_icc_node mas_gfx = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 2, .num_links = ARRAY_SIZE(mas_gfx_links), .links = mas_gfx_links, }; @@ -219,6 +234,11 @@ static struct qcom_icc_node mas_jpeg = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 6, .num_links = ARRAY_SIZE(mas_jpeg_links), .links = mas_jpeg_links, }; @@ -234,6 +254,11 @@ static struct qcom_icc_node mas_mdp = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 7, .num_links = ARRAY_SIZE(mas_mdp_links), .links = mas_mdp_links, }; @@ -290,6 +315,11 @@ static struct qcom_icc_node mas_qdss_bam = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 11, .num_links = ARRAY_SIZE(mas_qdss_bam_links), .links = mas_qdss_bam_links, }; @@ -304,6 +334,11 @@ static struct qcom_icc_node mas_qdss_etr = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 10, .num_links = ARRAY_SIZE(mas_qdss_etr_links), .links = mas_qdss_etr_links, }; @@ -348,6 +383,11 @@ static struct qcom_icc_node mas_tcu0 = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .qos.prio_level = 2, + .qos.qos_port = 5, .num_links = ARRAY_SIZE(mas_tcu0_links), .links = mas_tcu0_links, }; @@ -364,6 +404,11 @@ static struct qcom_icc_node mas_tcu1 = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .qos.prio_level = 2, + .qos.qos_port = 6, .num_links = ARRAY_SIZE(mas_tcu1_links), .links = mas_tcu1_links, }; @@ -393,6 +438,11 @@ static struct qcom_icc_node mas_vfe = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 9, .num_links = ARRAY_SIZE(mas_vfe_links), .links = mas_vfe_links, }; @@ -408,6 +458,11 @@ static struct qcom_icc_node mas_video = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 8, .num_links = ARRAY_SIZE(mas_video_links), .links = mas_video_links, }; @@ -422,6 +477,8 @@ static struct qcom_icc_node mm_int_0 = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(mm_int_0_links), .links = mm_int_0_links, }; @@ -436,6 +493,8 @@ static struct qcom_icc_node mm_int_1 = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(mm_int_1_links), .links = mm_int_1_links, }; @@ -450,6 +509,8 @@ static struct qcom_icc_node mm_int_2 = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(mm_int_2_links), .links = mm_int_2_links, }; @@ -464,6 +525,8 @@ static struct qcom_icc_node mm_int_bimc = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(mm_int_bimc_links), .links = mm_int_bimc_links, }; @@ -692,6 +755,8 @@ static struct qcom_icc_node qdss_int = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(qdss_int_links), .links = qdss_int_links, }; @@ -1030,6 +1095,8 @@ static struct qcom_icc_node snoc_bimc_1_mas = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(snoc_bimc_1_mas_links), .links = snoc_bimc_1_mas_links, }; @@ -1044,6 +1111,8 @@ static struct qcom_icc_node snoc_bimc_1_slv = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(snoc_bimc_1_slv_links), .links = snoc_bimc_1_slv_links, }; @@ -1151,9 +1220,19 @@ static struct qcom_icc_node *msm8916_snoc_nodes[] = { [SNOC_QDSS_INT] = &qdss_int, }; +static const struct regmap_config msm8916_snoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x14000, + .fast_io = true, +}; + static struct qcom_icc_desc msm8916_snoc = { .nodes = msm8916_snoc_nodes, .num_nodes = ARRAY_SIZE(msm8916_snoc_nodes), + .regmap_cfg = &msm8916_snoc_regmap_config, + .qos_offset = 0x7000, }; static struct qcom_icc_node *msm8916_bimc_nodes[] = { @@ -1168,9 +1247,20 @@ static struct qcom_icc_node *msm8916_bimc_nodes[] = { [SNOC_BIMC_1_SLV] = &snoc_bimc_1_slv, }; +static const struct regmap_config msm8916_bimc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x62000, + .fast_io = true, +}; + static struct qcom_icc_desc msm8916_bimc = { .nodes = msm8916_bimc_nodes, .num_nodes = ARRAY_SIZE(msm8916_bimc_nodes), + .is_bimc_node = true, + .regmap_cfg = &msm8916_bimc_regmap_config, + .qos_offset = 0x8000, }; static struct qcom_icc_node *msm8916_pcnoc_nodes[] = { @@ -1226,9 +1316,19 @@ static struct qcom_icc_node *msm8916_pcnoc_nodes[] = { [SNOC_PCNOC_SLV] = &snoc_pcnoc_slv, }; +static const struct regmap_config msm8916_pcnoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x11000, + .fast_io = true, +}; + static struct qcom_icc_desc msm8916_pcnoc = { .nodes = msm8916_pcnoc_nodes, .num_nodes = ARRAY_SIZE(msm8916_pcnoc_nodes), + .regmap_cfg = &msm8916_pcnoc_regmap_config, + .qos_offset = 0x7000, }; static const struct of_device_id msm8916_noc_of_match[] = { -- cgit v1.2.3-70-g09d2 From 2427b06e4ca31373ba4a4e04835e4ad0fc3869fd Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 4 Sep 2021 02:24:18 +0300 Subject: interconnect: msm8939: expand DEFINE_QNODE macros In preparation to adding AP-owned nodes support to msm8939 expand DEFINE_QNODE macros in the driver. Suggested-by: Bjorn Andersson Signed-off-by: Dmitry Baryshkov Reviewed-by: AngeloGioacchino Del Regno Tested-by: Shawn Guo Link: https://lore.kernel.org/r/20210903232421.1384199-9-dmitry.baryshkov@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/msm8939.c | 1155 ++++++++++++++++++++++++++++++++--- 1 file changed, 1066 insertions(+), 89 deletions(-) diff --git a/drivers/interconnect/qcom/msm8939.c b/drivers/interconnect/qcom/msm8939.c index 4a5a2ec64960..4671538c8994 100644 --- a/drivers/interconnect/qcom/msm8939.c +++ b/drivers/interconnect/qcom/msm8939.c @@ -110,95 +110,1072 @@ enum { MSM8939_SNOC_PNOC_SLV, }; -DEFINE_QNODE(bimc_snoc_mas, MSM8939_BIMC_SNOC_MAS, 8, -1, -1, MSM8939_BIMC_SNOC_SLV); -DEFINE_QNODE(bimc_snoc_slv, MSM8939_BIMC_SNOC_SLV, 16, -1, 2, MSM8939_SNOC_INT_0, MSM8939_SNOC_INT_1); -DEFINE_QNODE(mas_apss, MSM8939_MASTER_AMPSS_M0, 16, -1, -1, MSM8939_SLAVE_EBI_CH0, MSM8939_BIMC_SNOC_MAS, MSM8939_SLAVE_AMPSS_L2); -DEFINE_QNODE(mas_audio, MSM8939_MASTER_LPASS, 4, -1, -1, MSM8939_PNOC_MAS_0); -DEFINE_QNODE(mas_blsp_1, MSM8939_MASTER_BLSP_1, 4, -1, -1, MSM8939_PNOC_MAS_1); -DEFINE_QNODE(mas_dehr, MSM8939_MASTER_DEHR, 4, -1, -1, MSM8939_PNOC_MAS_0); -DEFINE_QNODE(mas_gfx, MSM8939_MASTER_GRAPHICS_3D, 16, -1, -1, MSM8939_SLAVE_EBI_CH0, MSM8939_BIMC_SNOC_MAS, MSM8939_SLAVE_AMPSS_L2); -DEFINE_QNODE(mas_jpeg, MSM8939_MASTER_JPEG, 16, -1, -1, MSM8939_SNOC_MM_INT_0, MSM8939_SNOC_MM_INT_2); -DEFINE_QNODE(mas_mdp0, MSM8939_MASTER_MDP_PORT0, 16, -1, -1, MSM8939_SNOC_MM_INT_1, MSM8939_SNOC_MM_INT_2); -DEFINE_QNODE(mas_mdp1, MSM8939_MASTER_MDP_PORT1, 16, -1, -1, MSM8939_SNOC_MM_INT_0, MSM8939_SNOC_MM_INT_2); -DEFINE_QNODE(mas_cpp, MSM8939_MASTER_CPP, 16, -1, -1, MSM8939_SNOC_MM_INT_0, MSM8939_SNOC_MM_INT_2); -DEFINE_QNODE(mas_pcnoc_crypto_0, MSM8939_MASTER_CRYPTO_CORE0, 8, -1, -1, MSM8939_PNOC_INT_1); -DEFINE_QNODE(mas_pcnoc_sdcc_1, MSM8939_MASTER_SDCC_1, 8, -1, -1, MSM8939_PNOC_INT_1); -DEFINE_QNODE(mas_pcnoc_sdcc_2, MSM8939_MASTER_SDCC_2, 8, -1, -1, MSM8939_PNOC_INT_1); -DEFINE_QNODE(mas_qdss_bam, MSM8939_MASTER_QDSS_BAM, 8, -1, -1, MSM8939_SNOC_QDSS_INT); -DEFINE_QNODE(mas_qdss_etr, MSM8939_MASTER_QDSS_ETR, 8, -1, -1, MSM8939_SNOC_QDSS_INT); -DEFINE_QNODE(mas_snoc_cfg, MSM8939_MASTER_SNOC_CFG, 4, -1, -1, MSM8939_SLAVE_SRVC_SNOC); -DEFINE_QNODE(mas_spdm, MSM8939_MASTER_SPDM, 4, -1, -1, MSM8939_PNOC_MAS_0); -DEFINE_QNODE(mas_tcu0, MSM8939_MASTER_TCU0, 16, -1, -1, MSM8939_SLAVE_EBI_CH0, MSM8939_BIMC_SNOC_MAS, MSM8939_SLAVE_AMPSS_L2); -DEFINE_QNODE(mas_usb_hs1, MSM8939_MASTER_USB_HS1, 4, -1, -1, MSM8939_PNOC_MAS_1); -DEFINE_QNODE(mas_usb_hs2, MSM8939_MASTER_USB_HS2, 4, -1, -1, MSM8939_PNOC_MAS_1); -DEFINE_QNODE(mas_vfe, MSM8939_MASTER_VFE, 16, -1, -1, MSM8939_SNOC_MM_INT_1, MSM8939_SNOC_MM_INT_2); -DEFINE_QNODE(mas_video, MSM8939_MASTER_VIDEO_P0, 16, -1, -1, MSM8939_SNOC_MM_INT_0, MSM8939_SNOC_MM_INT_2); -DEFINE_QNODE(mm_int_0, MSM8939_SNOC_MM_INT_0, 16, -1, -1, MSM8939_SNOC_BIMC_2_MAS); -DEFINE_QNODE(mm_int_1, MSM8939_SNOC_MM_INT_1, 16, -1, -1, MSM8939_SNOC_BIMC_1_MAS); -DEFINE_QNODE(mm_int_2, MSM8939_SNOC_MM_INT_2, 16, -1, -1, MSM8939_SNOC_INT_0); -DEFINE_QNODE(pcnoc_int_0, MSM8939_PNOC_INT_0, 8, -1, -1, MSM8939_PNOC_SNOC_MAS, MSM8939_PNOC_SLV_0, MSM8939_PNOC_SLV_1, MSM8939_PNOC_SLV_2, MSM8939_PNOC_SLV_3, MSM8939_PNOC_SLV_4, MSM8939_PNOC_SLV_8, MSM8939_PNOC_SLV_9); -DEFINE_QNODE(pcnoc_int_1, MSM8939_PNOC_INT_1, 8, -1, -1, MSM8939_PNOC_SNOC_MAS); -DEFINE_QNODE(pcnoc_m_0, MSM8939_PNOC_MAS_0, 8, -1, -1, MSM8939_PNOC_INT_0); -DEFINE_QNODE(pcnoc_m_1, MSM8939_PNOC_MAS_1, 8, -1, -1, MSM8939_PNOC_SNOC_MAS); -DEFINE_QNODE(pcnoc_s_0, MSM8939_PNOC_SLV_0, 4, -1, -1, MSM8939_SLAVE_CLK_CTL, MSM8939_SLAVE_TLMM, MSM8939_SLAVE_TCSR, MSM8939_SLAVE_SECURITY, MSM8939_SLAVE_MSS); -DEFINE_QNODE(pcnoc_s_1, MSM8939_PNOC_SLV_1, 4, -1, -1, MSM8939_SLAVE_IMEM_CFG, MSM8939_SLAVE_CRYPTO_0_CFG, MSM8939_SLAVE_MSG_RAM, MSM8939_SLAVE_PDM, MSM8939_SLAVE_PRNG); -DEFINE_QNODE(pcnoc_s_2, MSM8939_PNOC_SLV_2, 4, -1, -1, MSM8939_SLAVE_SPDM, MSM8939_SLAVE_BOOT_ROM, MSM8939_SLAVE_BIMC_CFG, MSM8939_SLAVE_PNOC_CFG, MSM8939_SLAVE_PMIC_ARB); -DEFINE_QNODE(pcnoc_s_3, MSM8939_PNOC_SLV_3, 4, -1, -1, MSM8939_SLAVE_MPM, MSM8939_SLAVE_SNOC_CFG, MSM8939_SLAVE_RBCPR_CFG, MSM8939_SLAVE_QDSS_CFG, MSM8939_SLAVE_DEHR_CFG); -DEFINE_QNODE(pcnoc_s_4, MSM8939_PNOC_SLV_4, 4, -1, -1, MSM8939_SLAVE_VENUS_CFG, MSM8939_SLAVE_CAMERA_CFG, MSM8939_SLAVE_DISPLAY_CFG); -DEFINE_QNODE(pcnoc_s_8, MSM8939_PNOC_SLV_8, 4, -1, -1, MSM8939_SLAVE_USB_HS1, MSM8939_SLAVE_SDCC_1, MSM8939_SLAVE_BLSP_1); -DEFINE_QNODE(pcnoc_s_9, MSM8939_PNOC_SLV_9, 4, -1, -1, MSM8939_SLAVE_SDCC_2, MSM8939_SLAVE_LPASS, MSM8939_SLAVE_USB_HS2); -DEFINE_QNODE(pcnoc_snoc_mas, MSM8939_PNOC_SNOC_MAS, 8, 29, -1, MSM8939_PNOC_SNOC_SLV); -DEFINE_QNODE(pcnoc_snoc_slv, MSM8939_PNOC_SNOC_SLV, 8, -1, 45, MSM8939_SNOC_INT_0, MSM8939_SNOC_INT_BIMC, MSM8939_SNOC_INT_1); -DEFINE_QNODE(qdss_int, MSM8939_SNOC_QDSS_INT, 8, -1, -1, MSM8939_SNOC_INT_0, MSM8939_SNOC_INT_BIMC); -DEFINE_QNODE(slv_apps_l2, MSM8939_SLAVE_AMPSS_L2, 16, -1, -1, 0); -DEFINE_QNODE(slv_apss, MSM8939_SLAVE_APSS, 4, -1, -1, 0); -DEFINE_QNODE(slv_audio, MSM8939_SLAVE_LPASS, 4, -1, -1, 0); -DEFINE_QNODE(slv_bimc_cfg, MSM8939_SLAVE_BIMC_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_blsp_1, MSM8939_SLAVE_BLSP_1, 4, -1, -1, 0); -DEFINE_QNODE(slv_boot_rom, MSM8939_SLAVE_BOOT_ROM, 4, -1, -1, 0); -DEFINE_QNODE(slv_camera_cfg, MSM8939_SLAVE_CAMERA_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_cats_0, MSM8939_SLAVE_CATS_128, 16, -1, -1, 0); -DEFINE_QNODE(slv_cats_1, MSM8939_SLAVE_OCMEM_64, 8, -1, -1, 0); -DEFINE_QNODE(slv_clk_ctl, MSM8939_SLAVE_CLK_CTL, 4, -1, -1, 0); -DEFINE_QNODE(slv_crypto_0_cfg, MSM8939_SLAVE_CRYPTO_0_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_dehr_cfg, MSM8939_SLAVE_DEHR_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_display_cfg, MSM8939_SLAVE_DISPLAY_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_ebi_ch0, MSM8939_SLAVE_EBI_CH0, 16, -1, 0, 0); -DEFINE_QNODE(slv_gfx_cfg, MSM8939_SLAVE_GRAPHICS_3D_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_imem_cfg, MSM8939_SLAVE_IMEM_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_imem, MSM8939_SLAVE_IMEM, 8, -1, 26, 0); -DEFINE_QNODE(slv_mpm, MSM8939_SLAVE_MPM, 4, -1, -1, 0); -DEFINE_QNODE(slv_msg_ram, MSM8939_SLAVE_MSG_RAM, 4, -1, -1, 0); -DEFINE_QNODE(slv_mss, MSM8939_SLAVE_MSS, 4, -1, -1, 0); -DEFINE_QNODE(slv_pdm, MSM8939_SLAVE_PDM, 4, -1, -1, 0); -DEFINE_QNODE(slv_pmic_arb, MSM8939_SLAVE_PMIC_ARB, 4, -1, -1, 0); -DEFINE_QNODE(slv_pcnoc_cfg, MSM8939_SLAVE_PNOC_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_prng, MSM8939_SLAVE_PRNG, 4, -1, -1, 0); -DEFINE_QNODE(slv_qdss_cfg, MSM8939_SLAVE_QDSS_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_qdss_stm, MSM8939_SLAVE_QDSS_STM, 4, -1, 30, 0); -DEFINE_QNODE(slv_rbcpr_cfg, MSM8939_SLAVE_RBCPR_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_sdcc_1, MSM8939_SLAVE_SDCC_1, 4, -1, -1, 0); -DEFINE_QNODE(slv_sdcc_2, MSM8939_SLAVE_SDCC_2, 4, -1, -1, 0); -DEFINE_QNODE(slv_security, MSM8939_SLAVE_SECURITY, 4, -1, -1, 0); -DEFINE_QNODE(slv_snoc_cfg, MSM8939_SLAVE_SNOC_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_spdm, MSM8939_SLAVE_SPDM, 4, -1, -1, 0); -DEFINE_QNODE(slv_srvc_snoc, MSM8939_SLAVE_SRVC_SNOC, 8, -1, -1, 0); -DEFINE_QNODE(slv_tcsr, MSM8939_SLAVE_TCSR, 4, -1, -1, 0); -DEFINE_QNODE(slv_tlmm, MSM8939_SLAVE_TLMM, 4, -1, -1, 0); -DEFINE_QNODE(slv_usb_hs1, MSM8939_SLAVE_USB_HS1, 4, -1, -1, 0); -DEFINE_QNODE(slv_usb_hs2, MSM8939_SLAVE_USB_HS2, 4, -1, -1, 0); -DEFINE_QNODE(slv_venus_cfg, MSM8939_SLAVE_VENUS_CFG, 4, -1, -1, 0); -DEFINE_QNODE(snoc_bimc_0_mas, MSM8939_SNOC_BIMC_0_MAS, 16, -1, -1, MSM8939_SNOC_BIMC_0_SLV); -DEFINE_QNODE(snoc_bimc_0_slv, MSM8939_SNOC_BIMC_0_SLV, 16, -1, -1, MSM8939_SLAVE_EBI_CH0); -DEFINE_QNODE(snoc_bimc_1_mas, MSM8939_SNOC_BIMC_1_MAS, 16, 76, -1, MSM8939_SNOC_BIMC_1_SLV); -DEFINE_QNODE(snoc_bimc_1_slv, MSM8939_SNOC_BIMC_1_SLV, 16, -1, 104, MSM8939_SLAVE_EBI_CH0); -DEFINE_QNODE(snoc_bimc_2_mas, MSM8939_SNOC_BIMC_2_MAS, 16, -1, -1, MSM8939_SNOC_BIMC_2_SLV); -DEFINE_QNODE(snoc_bimc_2_slv, MSM8939_SNOC_BIMC_2_SLV, 16, -1, -1, MSM8939_SLAVE_EBI_CH0); -DEFINE_QNODE(snoc_int_0, MSM8939_SNOC_INT_0, 8, 99, 130, MSM8939_SLAVE_QDSS_STM, MSM8939_SLAVE_IMEM, MSM8939_SNOC_PNOC_MAS); -DEFINE_QNODE(snoc_int_1, MSM8939_SNOC_INT_1, 8, -1, -1, MSM8939_SLAVE_APSS, MSM8939_SLAVE_CATS_128, MSM8939_SLAVE_OCMEM_64); -DEFINE_QNODE(snoc_int_bimc, MSM8939_SNOC_INT_BIMC, 8, 101, 132, MSM8939_SNOC_BIMC_1_MAS); -DEFINE_QNODE(snoc_pcnoc_mas, MSM8939_SNOC_PNOC_MAS, 8, -1, -1, MSM8939_SNOC_PNOC_SLV); -DEFINE_QNODE(snoc_pcnoc_slv, MSM8939_SNOC_PNOC_SLV, 8, -1, -1, MSM8939_PNOC_INT_0); +static const u16 bimc_snoc_mas_links[] = { + MSM8939_BIMC_SNOC_SLV +}; + +static struct qcom_icc_node bimc_snoc_mas = { + .name = "bimc_snoc_mas", + .id = MSM8939_BIMC_SNOC_MAS, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(bimc_snoc_mas_links), + .links = bimc_snoc_mas_links, +}; + +static const u16 bimc_snoc_slv_links[] = { + MSM8939_SNOC_INT_0, + MSM8939_SNOC_INT_1 +}; + +static struct qcom_icc_node bimc_snoc_slv = { + .name = "bimc_snoc_slv", + .id = MSM8939_BIMC_SNOC_SLV, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 2, + .num_links = ARRAY_SIZE(bimc_snoc_slv_links), + .links = bimc_snoc_slv_links, +}; + +static const u16 mas_apss_links[] = { + MSM8939_SLAVE_EBI_CH0, + MSM8939_BIMC_SNOC_MAS, + MSM8939_SLAVE_AMPSS_L2 +}; + +static struct qcom_icc_node mas_apss = { + .name = "mas_apss", + .id = MSM8939_MASTER_AMPSS_M0, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_apss_links), + .links = mas_apss_links, +}; + +static const u16 mas_audio_links[] = { + MSM8939_PNOC_MAS_0 +}; + +static struct qcom_icc_node mas_audio = { + .name = "mas_audio", + .id = MSM8939_MASTER_LPASS, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_audio_links), + .links = mas_audio_links, +}; + +static const u16 mas_blsp_1_links[] = { + MSM8939_PNOC_MAS_1 +}; + +static struct qcom_icc_node mas_blsp_1 = { + .name = "mas_blsp_1", + .id = MSM8939_MASTER_BLSP_1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_blsp_1_links), + .links = mas_blsp_1_links, +}; + +static const u16 mas_dehr_links[] = { + MSM8939_PNOC_MAS_0 +}; + +static struct qcom_icc_node mas_dehr = { + .name = "mas_dehr", + .id = MSM8939_MASTER_DEHR, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_dehr_links), + .links = mas_dehr_links, +}; + +static const u16 mas_gfx_links[] = { + MSM8939_SLAVE_EBI_CH0, + MSM8939_BIMC_SNOC_MAS, + MSM8939_SLAVE_AMPSS_L2 +}; + +static struct qcom_icc_node mas_gfx = { + .name = "mas_gfx", + .id = MSM8939_MASTER_GRAPHICS_3D, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_gfx_links), + .links = mas_gfx_links, +}; + +static const u16 mas_jpeg_links[] = { + MSM8939_SNOC_MM_INT_0, + MSM8939_SNOC_MM_INT_2 +}; + +static struct qcom_icc_node mas_jpeg = { + .name = "mas_jpeg", + .id = MSM8939_MASTER_JPEG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_jpeg_links), + .links = mas_jpeg_links, +}; + +static const u16 mas_mdp0_links[] = { + MSM8939_SNOC_MM_INT_1, + MSM8939_SNOC_MM_INT_2 +}; + +static struct qcom_icc_node mas_mdp0 = { + .name = "mas_mdp0", + .id = MSM8939_MASTER_MDP_PORT0, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_mdp0_links), + .links = mas_mdp0_links, +}; + +static const u16 mas_mdp1_links[] = { + MSM8939_SNOC_MM_INT_0, + MSM8939_SNOC_MM_INT_2 +}; + +static struct qcom_icc_node mas_mdp1 = { + .name = "mas_mdp1", + .id = MSM8939_MASTER_MDP_PORT1, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_mdp1_links), + .links = mas_mdp1_links, +}; + +static const u16 mas_cpp_links[] = { + MSM8939_SNOC_MM_INT_0, + MSM8939_SNOC_MM_INT_2 +}; + +static struct qcom_icc_node mas_cpp = { + .name = "mas_cpp", + .id = MSM8939_MASTER_CPP, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_cpp_links), + .links = mas_cpp_links, +}; + +static const u16 mas_pcnoc_crypto_0_links[] = { + MSM8939_PNOC_INT_1 +}; + +static struct qcom_icc_node mas_pcnoc_crypto_0 = { + .name = "mas_pcnoc_crypto_0", + .id = MSM8939_MASTER_CRYPTO_CORE0, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_pcnoc_crypto_0_links), + .links = mas_pcnoc_crypto_0_links, +}; + +static const u16 mas_pcnoc_sdcc_1_links[] = { + MSM8939_PNOC_INT_1 +}; + +static struct qcom_icc_node mas_pcnoc_sdcc_1 = { + .name = "mas_pcnoc_sdcc_1", + .id = MSM8939_MASTER_SDCC_1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_pcnoc_sdcc_1_links), + .links = mas_pcnoc_sdcc_1_links, +}; + +static const u16 mas_pcnoc_sdcc_2_links[] = { + MSM8939_PNOC_INT_1 +}; + +static struct qcom_icc_node mas_pcnoc_sdcc_2 = { + .name = "mas_pcnoc_sdcc_2", + .id = MSM8939_MASTER_SDCC_2, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_pcnoc_sdcc_2_links), + .links = mas_pcnoc_sdcc_2_links, +}; + +static const u16 mas_qdss_bam_links[] = { + MSM8939_SNOC_QDSS_INT +}; + +static struct qcom_icc_node mas_qdss_bam = { + .name = "mas_qdss_bam", + .id = MSM8939_MASTER_QDSS_BAM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_qdss_bam_links), + .links = mas_qdss_bam_links, +}; + +static const u16 mas_qdss_etr_links[] = { + MSM8939_SNOC_QDSS_INT +}; + +static struct qcom_icc_node mas_qdss_etr = { + .name = "mas_qdss_etr", + .id = MSM8939_MASTER_QDSS_ETR, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_qdss_etr_links), + .links = mas_qdss_etr_links, +}; + +static const u16 mas_snoc_cfg_links[] = { + MSM8939_SLAVE_SRVC_SNOC +}; + +static struct qcom_icc_node mas_snoc_cfg = { + .name = "mas_snoc_cfg", + .id = MSM8939_MASTER_SNOC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_snoc_cfg_links), + .links = mas_snoc_cfg_links, +}; + +static const u16 mas_spdm_links[] = { + MSM8939_PNOC_MAS_0 +}; + +static struct qcom_icc_node mas_spdm = { + .name = "mas_spdm", + .id = MSM8939_MASTER_SPDM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_spdm_links), + .links = mas_spdm_links, +}; + +static const u16 mas_tcu0_links[] = { + MSM8939_SLAVE_EBI_CH0, + MSM8939_BIMC_SNOC_MAS, + MSM8939_SLAVE_AMPSS_L2 +}; + +static struct qcom_icc_node mas_tcu0 = { + .name = "mas_tcu0", + .id = MSM8939_MASTER_TCU0, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_tcu0_links), + .links = mas_tcu0_links, +}; + +static const u16 mas_usb_hs1_links[] = { + MSM8939_PNOC_MAS_1 +}; + +static struct qcom_icc_node mas_usb_hs1 = { + .name = "mas_usb_hs1", + .id = MSM8939_MASTER_USB_HS1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_usb_hs1_links), + .links = mas_usb_hs1_links, +}; + +static const u16 mas_usb_hs2_links[] = { + MSM8939_PNOC_MAS_1 +}; + +static struct qcom_icc_node mas_usb_hs2 = { + .name = "mas_usb_hs2", + .id = MSM8939_MASTER_USB_HS2, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_usb_hs2_links), + .links = mas_usb_hs2_links, +}; + +static const u16 mas_vfe_links[] = { + MSM8939_SNOC_MM_INT_1, + MSM8939_SNOC_MM_INT_2 +}; + +static struct qcom_icc_node mas_vfe = { + .name = "mas_vfe", + .id = MSM8939_MASTER_VFE, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_vfe_links), + .links = mas_vfe_links, +}; + +static const u16 mas_video_links[] = { + MSM8939_SNOC_MM_INT_0, + MSM8939_SNOC_MM_INT_2 +}; + +static struct qcom_icc_node mas_video = { + .name = "mas_video", + .id = MSM8939_MASTER_VIDEO_P0, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_video_links), + .links = mas_video_links, +}; + +static const u16 mm_int_0_links[] = { + MSM8939_SNOC_BIMC_2_MAS +}; + +static struct qcom_icc_node mm_int_0 = { + .name = "mm_int_0", + .id = MSM8939_SNOC_MM_INT_0, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mm_int_0_links), + .links = mm_int_0_links, +}; + +static const u16 mm_int_1_links[] = { + MSM8939_SNOC_BIMC_1_MAS +}; + +static struct qcom_icc_node mm_int_1 = { + .name = "mm_int_1", + .id = MSM8939_SNOC_MM_INT_1, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mm_int_1_links), + .links = mm_int_1_links, +}; + +static const u16 mm_int_2_links[] = { + MSM8939_SNOC_INT_0 +}; + +static struct qcom_icc_node mm_int_2 = { + .name = "mm_int_2", + .id = MSM8939_SNOC_MM_INT_2, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mm_int_2_links), + .links = mm_int_2_links, +}; + +static const u16 pcnoc_int_0_links[] = { + MSM8939_PNOC_SNOC_MAS, + MSM8939_PNOC_SLV_0, + MSM8939_PNOC_SLV_1, + MSM8939_PNOC_SLV_2, + MSM8939_PNOC_SLV_3, + MSM8939_PNOC_SLV_4, + MSM8939_PNOC_SLV_8, + MSM8939_PNOC_SLV_9 +}; + +static struct qcom_icc_node pcnoc_int_0 = { + .name = "pcnoc_int_0", + .id = MSM8939_PNOC_INT_0, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_int_0_links), + .links = pcnoc_int_0_links, +}; + +static const u16 pcnoc_int_1_links[] = { + MSM8939_PNOC_SNOC_MAS +}; + +static struct qcom_icc_node pcnoc_int_1 = { + .name = "pcnoc_int_1", + .id = MSM8939_PNOC_INT_1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_int_1_links), + .links = pcnoc_int_1_links, +}; + +static const u16 pcnoc_m_0_links[] = { + MSM8939_PNOC_INT_0 +}; + +static struct qcom_icc_node pcnoc_m_0 = { + .name = "pcnoc_m_0", + .id = MSM8939_PNOC_MAS_0, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_m_0_links), + .links = pcnoc_m_0_links, +}; + +static const u16 pcnoc_m_1_links[] = { + MSM8939_PNOC_SNOC_MAS +}; + +static struct qcom_icc_node pcnoc_m_1 = { + .name = "pcnoc_m_1", + .id = MSM8939_PNOC_MAS_1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_m_1_links), + .links = pcnoc_m_1_links, +}; + +static const u16 pcnoc_s_0_links[] = { + MSM8939_SLAVE_CLK_CTL, + MSM8939_SLAVE_TLMM, + MSM8939_SLAVE_TCSR, + MSM8939_SLAVE_SECURITY, + MSM8939_SLAVE_MSS +}; + +static struct qcom_icc_node pcnoc_s_0 = { + .name = "pcnoc_s_0", + .id = MSM8939_PNOC_SLV_0, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_s_0_links), + .links = pcnoc_s_0_links, +}; + +static const u16 pcnoc_s_1_links[] = { + MSM8939_SLAVE_IMEM_CFG, + MSM8939_SLAVE_CRYPTO_0_CFG, + MSM8939_SLAVE_MSG_RAM, + MSM8939_SLAVE_PDM, + MSM8939_SLAVE_PRNG +}; + +static struct qcom_icc_node pcnoc_s_1 = { + .name = "pcnoc_s_1", + .id = MSM8939_PNOC_SLV_1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_s_1_links), + .links = pcnoc_s_1_links, +}; + +static const u16 pcnoc_s_2_links[] = { + MSM8939_SLAVE_SPDM, + MSM8939_SLAVE_BOOT_ROM, + MSM8939_SLAVE_BIMC_CFG, + MSM8939_SLAVE_PNOC_CFG, + MSM8939_SLAVE_PMIC_ARB +}; + +static struct qcom_icc_node pcnoc_s_2 = { + .name = "pcnoc_s_2", + .id = MSM8939_PNOC_SLV_2, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_s_2_links), + .links = pcnoc_s_2_links, +}; + +static const u16 pcnoc_s_3_links[] = { + MSM8939_SLAVE_MPM, + MSM8939_SLAVE_SNOC_CFG, + MSM8939_SLAVE_RBCPR_CFG, + MSM8939_SLAVE_QDSS_CFG, + MSM8939_SLAVE_DEHR_CFG +}; + +static struct qcom_icc_node pcnoc_s_3 = { + .name = "pcnoc_s_3", + .id = MSM8939_PNOC_SLV_3, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_s_3_links), + .links = pcnoc_s_3_links, +}; + +static const u16 pcnoc_s_4_links[] = { + MSM8939_SLAVE_VENUS_CFG, + MSM8939_SLAVE_CAMERA_CFG, + MSM8939_SLAVE_DISPLAY_CFG +}; + +static struct qcom_icc_node pcnoc_s_4 = { + .name = "pcnoc_s_4", + .id = MSM8939_PNOC_SLV_4, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_s_4_links), + .links = pcnoc_s_4_links, +}; + +static const u16 pcnoc_s_8_links[] = { + MSM8939_SLAVE_USB_HS1, + MSM8939_SLAVE_SDCC_1, + MSM8939_SLAVE_BLSP_1 +}; + +static struct qcom_icc_node pcnoc_s_8 = { + .name = "pcnoc_s_8", + .id = MSM8939_PNOC_SLV_8, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_s_8_links), + .links = pcnoc_s_8_links, +}; + +static const u16 pcnoc_s_9_links[] = { + MSM8939_SLAVE_SDCC_2, + MSM8939_SLAVE_LPASS, + MSM8939_SLAVE_USB_HS2 +}; + +static struct qcom_icc_node pcnoc_s_9 = { + .name = "pcnoc_s_9", + .id = MSM8939_PNOC_SLV_9, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_s_9_links), + .links = pcnoc_s_9_links, +}; + +static const u16 pcnoc_snoc_mas_links[] = { + MSM8939_PNOC_SNOC_SLV +}; + +static struct qcom_icc_node pcnoc_snoc_mas = { + .name = "pcnoc_snoc_mas", + .id = MSM8939_PNOC_SNOC_MAS, + .buswidth = 8, + .mas_rpm_id = 29, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_snoc_mas_links), + .links = pcnoc_snoc_mas_links, +}; + +static const u16 pcnoc_snoc_slv_links[] = { + MSM8939_SNOC_INT_0, + MSM8939_SNOC_INT_BIMC, + MSM8939_SNOC_INT_1 +}; + +static struct qcom_icc_node pcnoc_snoc_slv = { + .name = "pcnoc_snoc_slv", + .id = MSM8939_PNOC_SNOC_SLV, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 45, + .num_links = ARRAY_SIZE(pcnoc_snoc_slv_links), + .links = pcnoc_snoc_slv_links, +}; + +static const u16 qdss_int_links[] = { + MSM8939_SNOC_INT_0, + MSM8939_SNOC_INT_BIMC +}; + +static struct qcom_icc_node qdss_int = { + .name = "qdss_int", + .id = MSM8939_SNOC_QDSS_INT, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qdss_int_links), + .links = qdss_int_links, +}; + +static struct qcom_icc_node slv_apps_l2 = { + .name = "slv_apps_l2", + .id = MSM8939_SLAVE_AMPSS_L2, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_apss = { + .name = "slv_apss", + .id = MSM8939_SLAVE_APSS, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_audio = { + .name = "slv_audio", + .id = MSM8939_SLAVE_LPASS, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_bimc_cfg = { + .name = "slv_bimc_cfg", + .id = MSM8939_SLAVE_BIMC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_blsp_1 = { + .name = "slv_blsp_1", + .id = MSM8939_SLAVE_BLSP_1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_boot_rom = { + .name = "slv_boot_rom", + .id = MSM8939_SLAVE_BOOT_ROM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_camera_cfg = { + .name = "slv_camera_cfg", + .id = MSM8939_SLAVE_CAMERA_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_cats_0 = { + .name = "slv_cats_0", + .id = MSM8939_SLAVE_CATS_128, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_cats_1 = { + .name = "slv_cats_1", + .id = MSM8939_SLAVE_OCMEM_64, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_clk_ctl = { + .name = "slv_clk_ctl", + .id = MSM8939_SLAVE_CLK_CTL, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_crypto_0_cfg = { + .name = "slv_crypto_0_cfg", + .id = MSM8939_SLAVE_CRYPTO_0_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_dehr_cfg = { + .name = "slv_dehr_cfg", + .id = MSM8939_SLAVE_DEHR_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_display_cfg = { + .name = "slv_display_cfg", + .id = MSM8939_SLAVE_DISPLAY_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_ebi_ch0 = { + .name = "slv_ebi_ch0", + .id = MSM8939_SLAVE_EBI_CH0, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 0, +}; + +static struct qcom_icc_node slv_gfx_cfg = { + .name = "slv_gfx_cfg", + .id = MSM8939_SLAVE_GRAPHICS_3D_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_imem_cfg = { + .name = "slv_imem_cfg", + .id = MSM8939_SLAVE_IMEM_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_imem = { + .name = "slv_imem", + .id = MSM8939_SLAVE_IMEM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 26, +}; + +static struct qcom_icc_node slv_mpm = { + .name = "slv_mpm", + .id = MSM8939_SLAVE_MPM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_msg_ram = { + .name = "slv_msg_ram", + .id = MSM8939_SLAVE_MSG_RAM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_mss = { + .name = "slv_mss", + .id = MSM8939_SLAVE_MSS, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_pdm = { + .name = "slv_pdm", + .id = MSM8939_SLAVE_PDM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_pmic_arb = { + .name = "slv_pmic_arb", + .id = MSM8939_SLAVE_PMIC_ARB, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_pcnoc_cfg = { + .name = "slv_pcnoc_cfg", + .id = MSM8939_SLAVE_PNOC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_prng = { + .name = "slv_prng", + .id = MSM8939_SLAVE_PRNG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_qdss_cfg = { + .name = "slv_qdss_cfg", + .id = MSM8939_SLAVE_QDSS_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_qdss_stm = { + .name = "slv_qdss_stm", + .id = MSM8939_SLAVE_QDSS_STM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 30, +}; + +static struct qcom_icc_node slv_rbcpr_cfg = { + .name = "slv_rbcpr_cfg", + .id = MSM8939_SLAVE_RBCPR_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_sdcc_1 = { + .name = "slv_sdcc_1", + .id = MSM8939_SLAVE_SDCC_1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_sdcc_2 = { + .name = "slv_sdcc_2", + .id = MSM8939_SLAVE_SDCC_2, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_security = { + .name = "slv_security", + .id = MSM8939_SLAVE_SECURITY, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_snoc_cfg = { + .name = "slv_snoc_cfg", + .id = MSM8939_SLAVE_SNOC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_spdm = { + .name = "slv_spdm", + .id = MSM8939_SLAVE_SPDM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_srvc_snoc = { + .name = "slv_srvc_snoc", + .id = MSM8939_SLAVE_SRVC_SNOC, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_tcsr = { + .name = "slv_tcsr", + .id = MSM8939_SLAVE_TCSR, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_tlmm = { + .name = "slv_tlmm", + .id = MSM8939_SLAVE_TLMM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_usb_hs1 = { + .name = "slv_usb_hs1", + .id = MSM8939_SLAVE_USB_HS1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_usb_hs2 = { + .name = "slv_usb_hs2", + .id = MSM8939_SLAVE_USB_HS2, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_venus_cfg = { + .name = "slv_venus_cfg", + .id = MSM8939_SLAVE_VENUS_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static const u16 snoc_bimc_0_mas_links[] = { + MSM8939_SNOC_BIMC_0_SLV +}; + +static struct qcom_icc_node snoc_bimc_0_mas = { + .name = "snoc_bimc_0_mas", + .id = MSM8939_SNOC_BIMC_0_MAS, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(snoc_bimc_0_mas_links), + .links = snoc_bimc_0_mas_links, +}; + +static const u16 snoc_bimc_0_slv_links[] = { + MSM8939_SLAVE_EBI_CH0 +}; + +static struct qcom_icc_node snoc_bimc_0_slv = { + .name = "snoc_bimc_0_slv", + .id = MSM8939_SNOC_BIMC_0_SLV, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(snoc_bimc_0_slv_links), + .links = snoc_bimc_0_slv_links, +}; + +static const u16 snoc_bimc_1_mas_links[] = { + MSM8939_SNOC_BIMC_1_SLV +}; + +static struct qcom_icc_node snoc_bimc_1_mas = { + .name = "snoc_bimc_1_mas", + .id = MSM8939_SNOC_BIMC_1_MAS, + .buswidth = 16, + .mas_rpm_id = 76, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(snoc_bimc_1_mas_links), + .links = snoc_bimc_1_mas_links, +}; + +static const u16 snoc_bimc_1_slv_links[] = { + MSM8939_SLAVE_EBI_CH0 +}; + +static struct qcom_icc_node snoc_bimc_1_slv = { + .name = "snoc_bimc_1_slv", + .id = MSM8939_SNOC_BIMC_1_SLV, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 104, + .num_links = ARRAY_SIZE(snoc_bimc_1_slv_links), + .links = snoc_bimc_1_slv_links, +}; + +static const u16 snoc_bimc_2_mas_links[] = { + MSM8939_SNOC_BIMC_2_SLV +}; + +static struct qcom_icc_node snoc_bimc_2_mas = { + .name = "snoc_bimc_2_mas", + .id = MSM8939_SNOC_BIMC_2_MAS, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(snoc_bimc_2_mas_links), + .links = snoc_bimc_2_mas_links, +}; + +static const u16 snoc_bimc_2_slv_links[] = { + MSM8939_SLAVE_EBI_CH0 +}; + +static struct qcom_icc_node snoc_bimc_2_slv = { + .name = "snoc_bimc_2_slv", + .id = MSM8939_SNOC_BIMC_2_SLV, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(snoc_bimc_2_slv_links), + .links = snoc_bimc_2_slv_links, +}; + +static const u16 snoc_int_0_links[] = { + MSM8939_SLAVE_QDSS_STM, + MSM8939_SLAVE_IMEM, + MSM8939_SNOC_PNOC_MAS +}; + +static struct qcom_icc_node snoc_int_0 = { + .name = "snoc_int_0", + .id = MSM8939_SNOC_INT_0, + .buswidth = 8, + .mas_rpm_id = 99, + .slv_rpm_id = 130, + .num_links = ARRAY_SIZE(snoc_int_0_links), + .links = snoc_int_0_links, +}; + +static const u16 snoc_int_1_links[] = { + MSM8939_SLAVE_APSS, + MSM8939_SLAVE_CATS_128, + MSM8939_SLAVE_OCMEM_64 +}; + +static struct qcom_icc_node snoc_int_1 = { + .name = "snoc_int_1", + .id = MSM8939_SNOC_INT_1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(snoc_int_1_links), + .links = snoc_int_1_links, +}; + +static const u16 snoc_int_bimc_links[] = { + MSM8939_SNOC_BIMC_1_MAS +}; + +static struct qcom_icc_node snoc_int_bimc = { + .name = "snoc_int_bimc", + .id = MSM8939_SNOC_INT_BIMC, + .buswidth = 8, + .mas_rpm_id = 101, + .slv_rpm_id = 132, + .num_links = ARRAY_SIZE(snoc_int_bimc_links), + .links = snoc_int_bimc_links, +}; + +static const u16 snoc_pcnoc_mas_links[] = { + MSM8939_SNOC_PNOC_SLV +}; + +static struct qcom_icc_node snoc_pcnoc_mas = { + .name = "snoc_pcnoc_mas", + .id = MSM8939_SNOC_PNOC_MAS, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(snoc_pcnoc_mas_links), + .links = snoc_pcnoc_mas_links, +}; + +static const u16 snoc_pcnoc_slv_links[] = { + MSM8939_PNOC_INT_0 +}; + +static struct qcom_icc_node snoc_pcnoc_slv = { + .name = "snoc_pcnoc_slv", + .id = MSM8939_SNOC_PNOC_SLV, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(snoc_pcnoc_slv_links), + .links = snoc_pcnoc_slv_links, +}; static struct qcom_icc_node *msm8939_snoc_nodes[] = { [BIMC_SNOC_SLV] = &bimc_snoc_slv, -- cgit v1.2.3-70-g09d2 From 55867ea29f9cb8407e52cec0221fa7390a0bcd5d Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 4 Sep 2021 02:24:19 +0300 Subject: interconnect: msm8939: add support for AP-owned nodes Port support for AP-owned nodes from the downstream device tree. Signed-off-by: Dmitry Baryshkov Reviewed-by: AngeloGioacchino Del Regno Tested-by: Shawn Guo Link: https://lore.kernel.org/r/20210903232421.1384199-10-dmitry.baryshkov@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/msm8939.c | 115 ++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/drivers/interconnect/qcom/msm8939.c b/drivers/interconnect/qcom/msm8939.c index 4671538c8994..16272a477bd8 100644 --- a/drivers/interconnect/qcom/msm8939.c +++ b/drivers/interconnect/qcom/msm8939.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -120,6 +121,8 @@ static struct qcom_icc_node bimc_snoc_mas = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(bimc_snoc_mas_links), .links = bimc_snoc_mas_links, }; @@ -151,6 +154,11 @@ static struct qcom_icc_node mas_apss = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 0, .num_links = ARRAY_SIZE(mas_apss_links), .links = mas_apss_links, }; @@ -209,6 +217,11 @@ static struct qcom_icc_node mas_gfx = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 2, .num_links = ARRAY_SIZE(mas_gfx_links), .links = mas_gfx_links, }; @@ -224,6 +237,11 @@ static struct qcom_icc_node mas_jpeg = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 6, .num_links = ARRAY_SIZE(mas_jpeg_links), .links = mas_jpeg_links, }; @@ -239,6 +257,11 @@ static struct qcom_icc_node mas_mdp0 = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 7, .num_links = ARRAY_SIZE(mas_mdp0_links), .links = mas_mdp0_links, }; @@ -254,6 +277,11 @@ static struct qcom_icc_node mas_mdp1 = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 13, .num_links = ARRAY_SIZE(mas_mdp1_links), .links = mas_mdp1_links, }; @@ -269,6 +297,11 @@ static struct qcom_icc_node mas_cpp = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 12, .num_links = ARRAY_SIZE(mas_cpp_links), .links = mas_cpp_links, }; @@ -325,6 +358,11 @@ static struct qcom_icc_node mas_qdss_bam = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 11, .num_links = ARRAY_SIZE(mas_qdss_bam_links), .links = mas_qdss_bam_links, }; @@ -339,6 +377,11 @@ static struct qcom_icc_node mas_qdss_etr = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 10, .num_links = ARRAY_SIZE(mas_qdss_etr_links), .links = mas_qdss_etr_links, }; @@ -383,6 +426,11 @@ static struct qcom_icc_node mas_tcu0 = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .qos.prio_level = 2, + .qos.qos_port = 6, .num_links = ARRAY_SIZE(mas_tcu0_links), .links = mas_tcu0_links, }; @@ -426,6 +474,11 @@ static struct qcom_icc_node mas_vfe = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 9, .num_links = ARRAY_SIZE(mas_vfe_links), .links = mas_vfe_links, }; @@ -441,6 +494,11 @@ static struct qcom_icc_node mas_video = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 8, .num_links = ARRAY_SIZE(mas_video_links), .links = mas_video_links, }; @@ -455,6 +513,8 @@ static struct qcom_icc_node mm_int_0 = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(mm_int_0_links), .links = mm_int_0_links, }; @@ -469,6 +529,8 @@ static struct qcom_icc_node mm_int_1 = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(mm_int_1_links), .links = mm_int_1_links, }; @@ -483,6 +545,8 @@ static struct qcom_icc_node mm_int_2 = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(mm_int_2_links), .links = mm_int_2_links, }; @@ -711,6 +775,8 @@ static struct qcom_icc_node qdss_int = { .buswidth = 8, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(qdss_int_links), .links = qdss_int_links, }; @@ -1029,6 +1095,8 @@ static struct qcom_icc_node snoc_bimc_0_mas = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(snoc_bimc_0_mas_links), .links = snoc_bimc_0_mas_links, }; @@ -1043,6 +1111,8 @@ static struct qcom_icc_node snoc_bimc_0_slv = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(snoc_bimc_0_slv_links), .links = snoc_bimc_0_slv_links, }; @@ -1085,6 +1155,8 @@ static struct qcom_icc_node snoc_bimc_2_mas = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(snoc_bimc_2_mas_links), .links = snoc_bimc_2_mas_links, }; @@ -1099,6 +1171,8 @@ static struct qcom_icc_node snoc_bimc_2_slv = { .buswidth = 16, .mas_rpm_id = -1, .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, .num_links = ARRAY_SIZE(snoc_bimc_2_slv_links), .links = snoc_bimc_2_slv_links, }; @@ -1199,9 +1273,19 @@ static struct qcom_icc_node *msm8939_snoc_nodes[] = { [SNOC_QDSS_INT] = &qdss_int, }; +static const struct regmap_config msm8939_snoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x14080, + .fast_io = true, +}; + static struct qcom_icc_desc msm8939_snoc = { .nodes = msm8939_snoc_nodes, .num_nodes = ARRAY_SIZE(msm8939_snoc_nodes), + .regmap_cfg = &msm8939_snoc_regmap_config, + .qos_offset = 0x7000, }; static struct qcom_icc_node *msm8939_snoc_mm_nodes[] = { @@ -1216,9 +1300,19 @@ static struct qcom_icc_node *msm8939_snoc_mm_nodes[] = { [SNOC_MM_INT_2] = &mm_int_2, }; +static const struct regmap_config msm8939_snoc_mm_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x14080, + .fast_io = true, +}; + static struct qcom_icc_desc msm8939_snoc_mm = { .nodes = msm8939_snoc_mm_nodes, .num_nodes = ARRAY_SIZE(msm8939_snoc_mm_nodes), + .regmap_cfg = &msm8939_snoc_mm_regmap_config, + .qos_offset = 0x7000, }; static struct qcom_icc_node *msm8939_bimc_nodes[] = { @@ -1233,9 +1327,20 @@ static struct qcom_icc_node *msm8939_bimc_nodes[] = { [SNOC_BIMC_2_SLV] = &snoc_bimc_2_slv, }; +static const struct regmap_config msm8939_bimc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x62000, + .fast_io = true, +}; + static struct qcom_icc_desc msm8939_bimc = { .nodes = msm8939_bimc_nodes, .num_nodes = ARRAY_SIZE(msm8939_bimc_nodes), + .is_bimc_node = true, + .regmap_cfg = &msm8939_bimc_regmap_config, + .qos_offset = 0x8000, }; static struct qcom_icc_node *msm8939_pcnoc_nodes[] = { @@ -1293,9 +1398,19 @@ static struct qcom_icc_node *msm8939_pcnoc_nodes[] = { [SNOC_PCNOC_SLV] = &snoc_pcnoc_slv, }; +static const struct regmap_config msm8939_pcnoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x11000, + .fast_io = true, +}; + static struct qcom_icc_desc msm8939_pcnoc = { .nodes = msm8939_pcnoc_nodes, .num_nodes = ARRAY_SIZE(msm8939_pcnoc_nodes), + .regmap_cfg = &msm8939_pcnoc_regmap_config, + .qos_offset = 0x7000, }; static const struct of_device_id msm8939_noc_of_match[] = { -- cgit v1.2.3-70-g09d2 From 42f236e275e6ae2a1c9b96296892819b164fd204 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 4 Sep 2021 02:24:20 +0300 Subject: interconnect: qcs404: expand DEFINE_QNODE macros To follow the example of the rest of icc-rpm.h drivers, expand DEFINE_QNODE macros in the driver. Suggested-by: Bjorn Andersson Signed-off-by: Dmitry Baryshkov Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20210903232421.1384199-11-dmitry.baryshkov@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/qcs404.c | 954 ++++++++++++++++++++++++++++++++++--- 1 file changed, 881 insertions(+), 73 deletions(-) diff --git a/drivers/interconnect/qcom/qcs404.c b/drivers/interconnect/qcom/qcs404.c index 0f2fff230b13..416c8bff8efa 100644 --- a/drivers/interconnect/qcom/qcs404.c +++ b/drivers/interconnect/qcom/qcs404.c @@ -92,79 +92,887 @@ enum { QCS404_SLAVE_LPASS, }; -DEFINE_QNODE(mas_apps_proc, QCS404_MASTER_AMPSS_M0, 8, 0, -1, QCS404_SLAVE_EBI_CH0, QCS404_BIMC_SNOC_SLV); -DEFINE_QNODE(mas_oxili, QCS404_MASTER_GRAPHICS_3D, 8, -1, -1, QCS404_SLAVE_EBI_CH0, QCS404_BIMC_SNOC_SLV); -DEFINE_QNODE(mas_mdp, QCS404_MASTER_MDP_PORT0, 8, -1, -1, QCS404_SLAVE_EBI_CH0, QCS404_BIMC_SNOC_SLV); -DEFINE_QNODE(mas_snoc_bimc_1, QCS404_SNOC_BIMC_1_MAS, 8, 76, -1, QCS404_SLAVE_EBI_CH0); -DEFINE_QNODE(mas_tcu_0, QCS404_MASTER_TCU_0, 8, -1, -1, QCS404_SLAVE_EBI_CH0, QCS404_BIMC_SNOC_SLV); -DEFINE_QNODE(mas_spdm, QCS404_MASTER_SPDM, 4, -1, -1, QCS404_PNOC_INT_3); -DEFINE_QNODE(mas_blsp_1, QCS404_MASTER_BLSP_1, 4, 41, -1, QCS404_PNOC_INT_3); -DEFINE_QNODE(mas_blsp_2, QCS404_MASTER_BLSP_2, 4, 39, -1, QCS404_PNOC_INT_3); -DEFINE_QNODE(mas_xi_usb_hs1, QCS404_MASTER_XM_USB_HS1, 8, 138, -1, QCS404_PNOC_INT_0); -DEFINE_QNODE(mas_crypto, QCS404_MASTER_CRYPTO_CORE0, 8, 23, -1, QCS404_PNOC_SNOC_SLV, QCS404_PNOC_INT_2); -DEFINE_QNODE(mas_sdcc_1, QCS404_MASTER_SDCC_1, 8, 33, -1, QCS404_PNOC_INT_0); -DEFINE_QNODE(mas_sdcc_2, QCS404_MASTER_SDCC_2, 8, 35, -1, QCS404_PNOC_INT_0); -DEFINE_QNODE(mas_snoc_pcnoc, QCS404_SNOC_PNOC_MAS, 8, 77, -1, QCS404_PNOC_INT_2); -DEFINE_QNODE(mas_qpic, QCS404_MASTER_QPIC, 4, -1, -1, QCS404_PNOC_INT_0); -DEFINE_QNODE(mas_qdss_bam, QCS404_MASTER_QDSS_BAM, 4, -1, -1, QCS404_SNOC_QDSS_INT); -DEFINE_QNODE(mas_bimc_snoc, QCS404_BIMC_SNOC_MAS, 8, 21, -1, QCS404_SLAVE_OCMEM_64, QCS404_SLAVE_CATS_128, QCS404_SNOC_INT_0, QCS404_SNOC_INT_1); -DEFINE_QNODE(mas_pcnoc_snoc, QCS404_PNOC_SNOC_MAS, 8, 29, -1, QCS404_SNOC_BIMC_1_SLV, QCS404_SNOC_INT_2, QCS404_SNOC_INT_0); -DEFINE_QNODE(mas_qdss_etr, QCS404_MASTER_QDSS_ETR, 8, -1, -1, QCS404_SNOC_QDSS_INT); -DEFINE_QNODE(mas_emac, QCS404_MASTER_EMAC, 8, -1, -1, QCS404_SNOC_BIMC_1_SLV, QCS404_SNOC_INT_1); -DEFINE_QNODE(mas_pcie, QCS404_MASTER_PCIE, 8, -1, -1, QCS404_SNOC_BIMC_1_SLV, QCS404_SNOC_INT_1); -DEFINE_QNODE(mas_usb3, QCS404_MASTER_USB3, 8, -1, -1, QCS404_SNOC_BIMC_1_SLV, QCS404_SNOC_INT_1); -DEFINE_QNODE(pcnoc_int_0, QCS404_PNOC_INT_0, 8, 85, 114, QCS404_PNOC_SNOC_SLV, QCS404_PNOC_INT_2); -DEFINE_QNODE(pcnoc_int_2, QCS404_PNOC_INT_2, 8, 124, 184, QCS404_PNOC_SLV_10, QCS404_SLAVE_TCU, QCS404_PNOC_SLV_11, QCS404_PNOC_SLV_2, QCS404_PNOC_SLV_3, QCS404_PNOC_SLV_0, QCS404_PNOC_SLV_1, QCS404_PNOC_SLV_6, QCS404_PNOC_SLV_7, QCS404_PNOC_SLV_4, QCS404_PNOC_SLV_8, QCS404_PNOC_SLV_9); -DEFINE_QNODE(pcnoc_int_3, QCS404_PNOC_INT_3, 8, 125, 185, QCS404_PNOC_SNOC_SLV); -DEFINE_QNODE(pcnoc_s_0, QCS404_PNOC_SLV_0, 4, 89, 118, QCS404_SLAVE_PRNG, QCS404_SLAVE_SPDM_WRAPPER, QCS404_SLAVE_PDM); -DEFINE_QNODE(pcnoc_s_1, QCS404_PNOC_SLV_1, 4, 90, 119, QCS404_SLAVE_TCSR); -DEFINE_QNODE(pcnoc_s_2, QCS404_PNOC_SLV_2, 4, -1, -1, QCS404_SLAVE_GRAPHICS_3D_CFG); -DEFINE_QNODE(pcnoc_s_3, QCS404_PNOC_SLV_3, 4, 92, 121, QCS404_SLAVE_MESSAGE_RAM); -DEFINE_QNODE(pcnoc_s_4, QCS404_PNOC_SLV_4, 4, 93, 122, QCS404_SLAVE_SNOC_CFG); -DEFINE_QNODE(pcnoc_s_6, QCS404_PNOC_SLV_6, 4, 94, 123, QCS404_SLAVE_BLSP_1, QCS404_SLAVE_TLMM_NORTH, QCS404_SLAVE_EMAC_CFG); -DEFINE_QNODE(pcnoc_s_7, QCS404_PNOC_SLV_7, 4, 95, 124, QCS404_SLAVE_TLMM_SOUTH, QCS404_SLAVE_DISPLAY_CFG, QCS404_SLAVE_SDCC_1, QCS404_SLAVE_PCIE_1, QCS404_SLAVE_SDCC_2); -DEFINE_QNODE(pcnoc_s_8, QCS404_PNOC_SLV_8, 4, 96, 125, QCS404_SLAVE_CRYPTO_0_CFG); -DEFINE_QNODE(pcnoc_s_9, QCS404_PNOC_SLV_9, 4, 97, 126, QCS404_SLAVE_BLSP_2, QCS404_SLAVE_TLMM_EAST, QCS404_SLAVE_PMIC_ARB); -DEFINE_QNODE(pcnoc_s_10, QCS404_PNOC_SLV_10, 4, 157, -1, QCS404_SLAVE_USB_HS); -DEFINE_QNODE(pcnoc_s_11, QCS404_PNOC_SLV_11, 4, 158, 246, QCS404_SLAVE_USB3); -DEFINE_QNODE(qdss_int, QCS404_SNOC_QDSS_INT, 8, -1, -1, QCS404_SNOC_BIMC_1_SLV, QCS404_SNOC_INT_1); -DEFINE_QNODE(snoc_int_0, QCS404_SNOC_INT_0, 8, 99, 130, QCS404_SLAVE_LPASS, QCS404_SLAVE_APPSS, QCS404_SLAVE_WCSS); -DEFINE_QNODE(snoc_int_1, QCS404_SNOC_INT_1, 8, 100, 131, QCS404_SNOC_PNOC_SLV, QCS404_SNOC_INT_2); -DEFINE_QNODE(snoc_int_2, QCS404_SNOC_INT_2, 8, 134, 197, QCS404_SLAVE_QDSS_STM, QCS404_SLAVE_OCIMEM); -DEFINE_QNODE(slv_ebi, QCS404_SLAVE_EBI_CH0, 8, -1, 0, 0); -DEFINE_QNODE(slv_bimc_snoc, QCS404_BIMC_SNOC_SLV, 8, -1, 2, QCS404_BIMC_SNOC_MAS); -DEFINE_QNODE(slv_spdm, QCS404_SLAVE_SPDM_WRAPPER, 4, -1, -1, 0); -DEFINE_QNODE(slv_pdm, QCS404_SLAVE_PDM, 4, -1, 41, 0); -DEFINE_QNODE(slv_prng, QCS404_SLAVE_PRNG, 4, -1, 44, 0); -DEFINE_QNODE(slv_tcsr, QCS404_SLAVE_TCSR, 4, -1, 50, 0); -DEFINE_QNODE(slv_snoc_cfg, QCS404_SLAVE_SNOC_CFG, 4, -1, 70, 0); -DEFINE_QNODE(slv_message_ram, QCS404_SLAVE_MESSAGE_RAM, 4, -1, 55, 0); -DEFINE_QNODE(slv_disp_ss_cfg, QCS404_SLAVE_DISPLAY_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_gpu_cfg, QCS404_SLAVE_GRAPHICS_3D_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_blsp_1, QCS404_SLAVE_BLSP_1, 4, -1, 39, 0); -DEFINE_QNODE(slv_tlmm_north, QCS404_SLAVE_TLMM_NORTH, 4, -1, 214, 0); -DEFINE_QNODE(slv_pcie, QCS404_SLAVE_PCIE_1, 4, -1, -1, 0); -DEFINE_QNODE(slv_ethernet, QCS404_SLAVE_EMAC_CFG, 4, -1, -1, 0); -DEFINE_QNODE(slv_blsp_2, QCS404_SLAVE_BLSP_2, 4, -1, 37, 0); -DEFINE_QNODE(slv_tlmm_east, QCS404_SLAVE_TLMM_EAST, 4, -1, 213, 0); -DEFINE_QNODE(slv_tcu, QCS404_SLAVE_TCU, 8, -1, -1, 0); -DEFINE_QNODE(slv_pmic_arb, QCS404_SLAVE_PMIC_ARB, 4, -1, 59, 0); -DEFINE_QNODE(slv_sdcc_1, QCS404_SLAVE_SDCC_1, 4, -1, 31, 0); -DEFINE_QNODE(slv_sdcc_2, QCS404_SLAVE_SDCC_2, 4, -1, 33, 0); -DEFINE_QNODE(slv_tlmm_south, QCS404_SLAVE_TLMM_SOUTH, 4, -1, -1, 0); -DEFINE_QNODE(slv_usb_hs, QCS404_SLAVE_USB_HS, 4, -1, 40, 0); -DEFINE_QNODE(slv_usb3, QCS404_SLAVE_USB3, 4, -1, 22, 0); -DEFINE_QNODE(slv_crypto_0_cfg, QCS404_SLAVE_CRYPTO_0_CFG, 4, -1, 52, 0); -DEFINE_QNODE(slv_pcnoc_snoc, QCS404_PNOC_SNOC_SLV, 8, -1, 45, QCS404_PNOC_SNOC_MAS); -DEFINE_QNODE(slv_kpss_ahb, QCS404_SLAVE_APPSS, 4, -1, -1, 0); -DEFINE_QNODE(slv_wcss, QCS404_SLAVE_WCSS, 4, -1, 23, 0); -DEFINE_QNODE(slv_snoc_bimc_1, QCS404_SNOC_BIMC_1_SLV, 8, -1, 104, QCS404_SNOC_BIMC_1_MAS); -DEFINE_QNODE(slv_imem, QCS404_SLAVE_OCIMEM, 8, -1, 26, 0); -DEFINE_QNODE(slv_snoc_pcnoc, QCS404_SNOC_PNOC_SLV, 8, -1, 28, QCS404_SNOC_PNOC_MAS); -DEFINE_QNODE(slv_qdss_stm, QCS404_SLAVE_QDSS_STM, 4, -1, 30, 0); -DEFINE_QNODE(slv_cats_0, QCS404_SLAVE_CATS_128, 16, -1, -1, 0); -DEFINE_QNODE(slv_cats_1, QCS404_SLAVE_OCMEM_64, 8, -1, -1, 0); -DEFINE_QNODE(slv_lpass, QCS404_SLAVE_LPASS, 4, -1, -1, 0); +static const u16 mas_apps_proc_links[] = { + QCS404_SLAVE_EBI_CH0, + QCS404_BIMC_SNOC_SLV +}; + +static struct qcom_icc_node mas_apps_proc = { + .name = "mas_apps_proc", + .id = QCS404_MASTER_AMPSS_M0, + .buswidth = 8, + .mas_rpm_id = 0, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_apps_proc_links), + .links = mas_apps_proc_links, +}; + +static const u16 mas_oxili_links[] = { + QCS404_SLAVE_EBI_CH0, + QCS404_BIMC_SNOC_SLV +}; + +static struct qcom_icc_node mas_oxili = { + .name = "mas_oxili", + .id = QCS404_MASTER_GRAPHICS_3D, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_oxili_links), + .links = mas_oxili_links, +}; + +static const u16 mas_mdp_links[] = { + QCS404_SLAVE_EBI_CH0, + QCS404_BIMC_SNOC_SLV +}; + +static struct qcom_icc_node mas_mdp = { + .name = "mas_mdp", + .id = QCS404_MASTER_MDP_PORT0, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_mdp_links), + .links = mas_mdp_links, +}; + +static const u16 mas_snoc_bimc_1_links[] = { + QCS404_SLAVE_EBI_CH0 +}; + +static struct qcom_icc_node mas_snoc_bimc_1 = { + .name = "mas_snoc_bimc_1", + .id = QCS404_SNOC_BIMC_1_MAS, + .buswidth = 8, + .mas_rpm_id = 76, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_snoc_bimc_1_links), + .links = mas_snoc_bimc_1_links, +}; + +static const u16 mas_tcu_0_links[] = { + QCS404_SLAVE_EBI_CH0, + QCS404_BIMC_SNOC_SLV +}; + +static struct qcom_icc_node mas_tcu_0 = { + .name = "mas_tcu_0", + .id = QCS404_MASTER_TCU_0, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_tcu_0_links), + .links = mas_tcu_0_links, +}; + +static const u16 mas_spdm_links[] = { + QCS404_PNOC_INT_3 +}; + +static struct qcom_icc_node mas_spdm = { + .name = "mas_spdm", + .id = QCS404_MASTER_SPDM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_spdm_links), + .links = mas_spdm_links, +}; + +static const u16 mas_blsp_1_links[] = { + QCS404_PNOC_INT_3 +}; + +static struct qcom_icc_node mas_blsp_1 = { + .name = "mas_blsp_1", + .id = QCS404_MASTER_BLSP_1, + .buswidth = 4, + .mas_rpm_id = 41, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_blsp_1_links), + .links = mas_blsp_1_links, +}; + +static const u16 mas_blsp_2_links[] = { + QCS404_PNOC_INT_3 +}; + +static struct qcom_icc_node mas_blsp_2 = { + .name = "mas_blsp_2", + .id = QCS404_MASTER_BLSP_2, + .buswidth = 4, + .mas_rpm_id = 39, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_blsp_2_links), + .links = mas_blsp_2_links, +}; + +static const u16 mas_xi_usb_hs1_links[] = { + QCS404_PNOC_INT_0 +}; + +static struct qcom_icc_node mas_xi_usb_hs1 = { + .name = "mas_xi_usb_hs1", + .id = QCS404_MASTER_XM_USB_HS1, + .buswidth = 8, + .mas_rpm_id = 138, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_xi_usb_hs1_links), + .links = mas_xi_usb_hs1_links, +}; + +static const u16 mas_crypto_links[] = { + QCS404_PNOC_SNOC_SLV, + QCS404_PNOC_INT_2 +}; + +static struct qcom_icc_node mas_crypto = { + .name = "mas_crypto", + .id = QCS404_MASTER_CRYPTO_CORE0, + .buswidth = 8, + .mas_rpm_id = 23, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_crypto_links), + .links = mas_crypto_links, +}; + +static const u16 mas_sdcc_1_links[] = { + QCS404_PNOC_INT_0 +}; + +static struct qcom_icc_node mas_sdcc_1 = { + .name = "mas_sdcc_1", + .id = QCS404_MASTER_SDCC_1, + .buswidth = 8, + .mas_rpm_id = 33, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_sdcc_1_links), + .links = mas_sdcc_1_links, +}; + +static const u16 mas_sdcc_2_links[] = { + QCS404_PNOC_INT_0 +}; + +static struct qcom_icc_node mas_sdcc_2 = { + .name = "mas_sdcc_2", + .id = QCS404_MASTER_SDCC_2, + .buswidth = 8, + .mas_rpm_id = 35, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_sdcc_2_links), + .links = mas_sdcc_2_links, +}; + +static const u16 mas_snoc_pcnoc_links[] = { + QCS404_PNOC_INT_2 +}; + +static struct qcom_icc_node mas_snoc_pcnoc = { + .name = "mas_snoc_pcnoc", + .id = QCS404_SNOC_PNOC_MAS, + .buswidth = 8, + .mas_rpm_id = 77, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_snoc_pcnoc_links), + .links = mas_snoc_pcnoc_links, +}; + +static const u16 mas_qpic_links[] = { + QCS404_PNOC_INT_0 +}; + +static struct qcom_icc_node mas_qpic = { + .name = "mas_qpic", + .id = QCS404_MASTER_QPIC, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_qpic_links), + .links = mas_qpic_links, +}; + +static const u16 mas_qdss_bam_links[] = { + QCS404_SNOC_QDSS_INT +}; + +static struct qcom_icc_node mas_qdss_bam = { + .name = "mas_qdss_bam", + .id = QCS404_MASTER_QDSS_BAM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_qdss_bam_links), + .links = mas_qdss_bam_links, +}; + +static const u16 mas_bimc_snoc_links[] = { + QCS404_SLAVE_OCMEM_64, + QCS404_SLAVE_CATS_128, + QCS404_SNOC_INT_0, + QCS404_SNOC_INT_1 +}; + +static struct qcom_icc_node mas_bimc_snoc = { + .name = "mas_bimc_snoc", + .id = QCS404_BIMC_SNOC_MAS, + .buswidth = 8, + .mas_rpm_id = 21, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_bimc_snoc_links), + .links = mas_bimc_snoc_links, +}; + +static const u16 mas_pcnoc_snoc_links[] = { + QCS404_SNOC_BIMC_1_SLV, + QCS404_SNOC_INT_2, + QCS404_SNOC_INT_0 +}; + +static struct qcom_icc_node mas_pcnoc_snoc = { + .name = "mas_pcnoc_snoc", + .id = QCS404_PNOC_SNOC_MAS, + .buswidth = 8, + .mas_rpm_id = 29, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_pcnoc_snoc_links), + .links = mas_pcnoc_snoc_links, +}; + +static const u16 mas_qdss_etr_links[] = { + QCS404_SNOC_QDSS_INT +}; + +static struct qcom_icc_node mas_qdss_etr = { + .name = "mas_qdss_etr", + .id = QCS404_MASTER_QDSS_ETR, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_qdss_etr_links), + .links = mas_qdss_etr_links, +}; + +static const u16 mas_emac_links[] = { + QCS404_SNOC_BIMC_1_SLV, + QCS404_SNOC_INT_1 +}; + +static struct qcom_icc_node mas_emac = { + .name = "mas_emac", + .id = QCS404_MASTER_EMAC, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_emac_links), + .links = mas_emac_links, +}; + +static const u16 mas_pcie_links[] = { + QCS404_SNOC_BIMC_1_SLV, + QCS404_SNOC_INT_1 +}; + +static struct qcom_icc_node mas_pcie = { + .name = "mas_pcie", + .id = QCS404_MASTER_PCIE, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_pcie_links), + .links = mas_pcie_links, +}; + +static const u16 mas_usb3_links[] = { + QCS404_SNOC_BIMC_1_SLV, + QCS404_SNOC_INT_1 +}; + +static struct qcom_icc_node mas_usb3 = { + .name = "mas_usb3", + .id = QCS404_MASTER_USB3, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_usb3_links), + .links = mas_usb3_links, +}; + +static const u16 pcnoc_int_0_links[] = { + QCS404_PNOC_SNOC_SLV, + QCS404_PNOC_INT_2 +}; + +static struct qcom_icc_node pcnoc_int_0 = { + .name = "pcnoc_int_0", + .id = QCS404_PNOC_INT_0, + .buswidth = 8, + .mas_rpm_id = 85, + .slv_rpm_id = 114, + .num_links = ARRAY_SIZE(pcnoc_int_0_links), + .links = pcnoc_int_0_links, +}; + +static const u16 pcnoc_int_2_links[] = { + QCS404_PNOC_SLV_10, + QCS404_SLAVE_TCU, + QCS404_PNOC_SLV_11, + QCS404_PNOC_SLV_2, + QCS404_PNOC_SLV_3, + QCS404_PNOC_SLV_0, + QCS404_PNOC_SLV_1, + QCS404_PNOC_SLV_6, + QCS404_PNOC_SLV_7, + QCS404_PNOC_SLV_4, + QCS404_PNOC_SLV_8, + QCS404_PNOC_SLV_9 +}; + +static struct qcom_icc_node pcnoc_int_2 = { + .name = "pcnoc_int_2", + .id = QCS404_PNOC_INT_2, + .buswidth = 8, + .mas_rpm_id = 124, + .slv_rpm_id = 184, + .num_links = ARRAY_SIZE(pcnoc_int_2_links), + .links = pcnoc_int_2_links, +}; + +static const u16 pcnoc_int_3_links[] = { + QCS404_PNOC_SNOC_SLV +}; + +static struct qcom_icc_node pcnoc_int_3 = { + .name = "pcnoc_int_3", + .id = QCS404_PNOC_INT_3, + .buswidth = 8, + .mas_rpm_id = 125, + .slv_rpm_id = 185, + .num_links = ARRAY_SIZE(pcnoc_int_3_links), + .links = pcnoc_int_3_links, +}; + +static const u16 pcnoc_s_0_links[] = { + QCS404_SLAVE_PRNG, + QCS404_SLAVE_SPDM_WRAPPER, + QCS404_SLAVE_PDM +}; + +static struct qcom_icc_node pcnoc_s_0 = { + .name = "pcnoc_s_0", + .id = QCS404_PNOC_SLV_0, + .buswidth = 4, + .mas_rpm_id = 89, + .slv_rpm_id = 118, + .num_links = ARRAY_SIZE(pcnoc_s_0_links), + .links = pcnoc_s_0_links, +}; + +static const u16 pcnoc_s_1_links[] = { + QCS404_SLAVE_TCSR +}; + +static struct qcom_icc_node pcnoc_s_1 = { + .name = "pcnoc_s_1", + .id = QCS404_PNOC_SLV_1, + .buswidth = 4, + .mas_rpm_id = 90, + .slv_rpm_id = 119, + .num_links = ARRAY_SIZE(pcnoc_s_1_links), + .links = pcnoc_s_1_links, +}; + +static const u16 pcnoc_s_2_links[] = { + QCS404_SLAVE_GRAPHICS_3D_CFG +}; + +static struct qcom_icc_node pcnoc_s_2 = { + .name = "pcnoc_s_2", + .id = QCS404_PNOC_SLV_2, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_s_2_links), + .links = pcnoc_s_2_links, +}; + +static const u16 pcnoc_s_3_links[] = { + QCS404_SLAVE_MESSAGE_RAM +}; + +static struct qcom_icc_node pcnoc_s_3 = { + .name = "pcnoc_s_3", + .id = QCS404_PNOC_SLV_3, + .buswidth = 4, + .mas_rpm_id = 92, + .slv_rpm_id = 121, + .num_links = ARRAY_SIZE(pcnoc_s_3_links), + .links = pcnoc_s_3_links, +}; + +static const u16 pcnoc_s_4_links[] = { + QCS404_SLAVE_SNOC_CFG +}; + +static struct qcom_icc_node pcnoc_s_4 = { + .name = "pcnoc_s_4", + .id = QCS404_PNOC_SLV_4, + .buswidth = 4, + .mas_rpm_id = 93, + .slv_rpm_id = 122, + .num_links = ARRAY_SIZE(pcnoc_s_4_links), + .links = pcnoc_s_4_links, +}; + +static const u16 pcnoc_s_6_links[] = { + QCS404_SLAVE_BLSP_1, + QCS404_SLAVE_TLMM_NORTH, + QCS404_SLAVE_EMAC_CFG +}; + +static struct qcom_icc_node pcnoc_s_6 = { + .name = "pcnoc_s_6", + .id = QCS404_PNOC_SLV_6, + .buswidth = 4, + .mas_rpm_id = 94, + .slv_rpm_id = 123, + .num_links = ARRAY_SIZE(pcnoc_s_6_links), + .links = pcnoc_s_6_links, +}; + +static const u16 pcnoc_s_7_links[] = { + QCS404_SLAVE_TLMM_SOUTH, + QCS404_SLAVE_DISPLAY_CFG, + QCS404_SLAVE_SDCC_1, + QCS404_SLAVE_PCIE_1, + QCS404_SLAVE_SDCC_2 +}; + +static struct qcom_icc_node pcnoc_s_7 = { + .name = "pcnoc_s_7", + .id = QCS404_PNOC_SLV_7, + .buswidth = 4, + .mas_rpm_id = 95, + .slv_rpm_id = 124, + .num_links = ARRAY_SIZE(pcnoc_s_7_links), + .links = pcnoc_s_7_links, +}; + +static const u16 pcnoc_s_8_links[] = { + QCS404_SLAVE_CRYPTO_0_CFG +}; + +static struct qcom_icc_node pcnoc_s_8 = { + .name = "pcnoc_s_8", + .id = QCS404_PNOC_SLV_8, + .buswidth = 4, + .mas_rpm_id = 96, + .slv_rpm_id = 125, + .num_links = ARRAY_SIZE(pcnoc_s_8_links), + .links = pcnoc_s_8_links, +}; + +static const u16 pcnoc_s_9_links[] = { + QCS404_SLAVE_BLSP_2, + QCS404_SLAVE_TLMM_EAST, + QCS404_SLAVE_PMIC_ARB +}; + +static struct qcom_icc_node pcnoc_s_9 = { + .name = "pcnoc_s_9", + .id = QCS404_PNOC_SLV_9, + .buswidth = 4, + .mas_rpm_id = 97, + .slv_rpm_id = 126, + .num_links = ARRAY_SIZE(pcnoc_s_9_links), + .links = pcnoc_s_9_links, +}; + +static const u16 pcnoc_s_10_links[] = { + QCS404_SLAVE_USB_HS +}; + +static struct qcom_icc_node pcnoc_s_10 = { + .name = "pcnoc_s_10", + .id = QCS404_PNOC_SLV_10, + .buswidth = 4, + .mas_rpm_id = 157, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(pcnoc_s_10_links), + .links = pcnoc_s_10_links, +}; + +static const u16 pcnoc_s_11_links[] = { + QCS404_SLAVE_USB3 +}; + +static struct qcom_icc_node pcnoc_s_11 = { + .name = "pcnoc_s_11", + .id = QCS404_PNOC_SLV_11, + .buswidth = 4, + .mas_rpm_id = 158, + .slv_rpm_id = 246, + .num_links = ARRAY_SIZE(pcnoc_s_11_links), + .links = pcnoc_s_11_links, +}; + +static const u16 qdss_int_links[] = { + QCS404_SNOC_BIMC_1_SLV, + QCS404_SNOC_INT_1 +}; + +static struct qcom_icc_node qdss_int = { + .name = "qdss_int", + .id = QCS404_SNOC_QDSS_INT, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qdss_int_links), + .links = qdss_int_links, +}; + +static const u16 snoc_int_0_links[] = { + QCS404_SLAVE_LPASS, + QCS404_SLAVE_APPSS, + QCS404_SLAVE_WCSS +}; + +static struct qcom_icc_node snoc_int_0 = { + .name = "snoc_int_0", + .id = QCS404_SNOC_INT_0, + .buswidth = 8, + .mas_rpm_id = 99, + .slv_rpm_id = 130, + .num_links = ARRAY_SIZE(snoc_int_0_links), + .links = snoc_int_0_links, +}; + +static const u16 snoc_int_1_links[] = { + QCS404_SNOC_PNOC_SLV, + QCS404_SNOC_INT_2 +}; + +static struct qcom_icc_node snoc_int_1 = { + .name = "snoc_int_1", + .id = QCS404_SNOC_INT_1, + .buswidth = 8, + .mas_rpm_id = 100, + .slv_rpm_id = 131, + .num_links = ARRAY_SIZE(snoc_int_1_links), + .links = snoc_int_1_links, +}; + +static const u16 snoc_int_2_links[] = { + QCS404_SLAVE_QDSS_STM, + QCS404_SLAVE_OCIMEM +}; + +static struct qcom_icc_node snoc_int_2 = { + .name = "snoc_int_2", + .id = QCS404_SNOC_INT_2, + .buswidth = 8, + .mas_rpm_id = 134, + .slv_rpm_id = 197, + .num_links = ARRAY_SIZE(snoc_int_2_links), + .links = snoc_int_2_links, +}; + +static struct qcom_icc_node slv_ebi = { + .name = "slv_ebi", + .id = QCS404_SLAVE_EBI_CH0, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 0, +}; + +static const u16 slv_bimc_snoc_links[] = { + QCS404_BIMC_SNOC_MAS +}; + +static struct qcom_icc_node slv_bimc_snoc = { + .name = "slv_bimc_snoc", + .id = QCS404_BIMC_SNOC_SLV, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 2, + .num_links = ARRAY_SIZE(slv_bimc_snoc_links), + .links = slv_bimc_snoc_links, +}; + +static struct qcom_icc_node slv_spdm = { + .name = "slv_spdm", + .id = QCS404_SLAVE_SPDM_WRAPPER, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_pdm = { + .name = "slv_pdm", + .id = QCS404_SLAVE_PDM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 41, +}; + +static struct qcom_icc_node slv_prng = { + .name = "slv_prng", + .id = QCS404_SLAVE_PRNG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 44, +}; + +static struct qcom_icc_node slv_tcsr = { + .name = "slv_tcsr", + .id = QCS404_SLAVE_TCSR, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 50, +}; + +static struct qcom_icc_node slv_snoc_cfg = { + .name = "slv_snoc_cfg", + .id = QCS404_SLAVE_SNOC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 70, +}; + +static struct qcom_icc_node slv_message_ram = { + .name = "slv_message_ram", + .id = QCS404_SLAVE_MESSAGE_RAM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 55, +}; + +static struct qcom_icc_node slv_disp_ss_cfg = { + .name = "slv_disp_ss_cfg", + .id = QCS404_SLAVE_DISPLAY_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_gpu_cfg = { + .name = "slv_gpu_cfg", + .id = QCS404_SLAVE_GRAPHICS_3D_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_blsp_1 = { + .name = "slv_blsp_1", + .id = QCS404_SLAVE_BLSP_1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 39, +}; + +static struct qcom_icc_node slv_tlmm_north = { + .name = "slv_tlmm_north", + .id = QCS404_SLAVE_TLMM_NORTH, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 214, +}; + +static struct qcom_icc_node slv_pcie = { + .name = "slv_pcie", + .id = QCS404_SLAVE_PCIE_1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_ethernet = { + .name = "slv_ethernet", + .id = QCS404_SLAVE_EMAC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_blsp_2 = { + .name = "slv_blsp_2", + .id = QCS404_SLAVE_BLSP_2, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 37, +}; + +static struct qcom_icc_node slv_tlmm_east = { + .name = "slv_tlmm_east", + .id = QCS404_SLAVE_TLMM_EAST, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 213, +}; + +static struct qcom_icc_node slv_tcu = { + .name = "slv_tcu", + .id = QCS404_SLAVE_TCU, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_pmic_arb = { + .name = "slv_pmic_arb", + .id = QCS404_SLAVE_PMIC_ARB, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 59, +}; + +static struct qcom_icc_node slv_sdcc_1 = { + .name = "slv_sdcc_1", + .id = QCS404_SLAVE_SDCC_1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 31, +}; + +static struct qcom_icc_node slv_sdcc_2 = { + .name = "slv_sdcc_2", + .id = QCS404_SLAVE_SDCC_2, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 33, +}; + +static struct qcom_icc_node slv_tlmm_south = { + .name = "slv_tlmm_south", + .id = QCS404_SLAVE_TLMM_SOUTH, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_usb_hs = { + .name = "slv_usb_hs", + .id = QCS404_SLAVE_USB_HS, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 40, +}; + +static struct qcom_icc_node slv_usb3 = { + .name = "slv_usb3", + .id = QCS404_SLAVE_USB3, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 22, +}; + +static struct qcom_icc_node slv_crypto_0_cfg = { + .name = "slv_crypto_0_cfg", + .id = QCS404_SLAVE_CRYPTO_0_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 52, +}; + +static const u16 slv_pcnoc_snoc_links[] = { + QCS404_PNOC_SNOC_MAS +}; + +static struct qcom_icc_node slv_pcnoc_snoc = { + .name = "slv_pcnoc_snoc", + .id = QCS404_PNOC_SNOC_SLV, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 45, + .num_links = ARRAY_SIZE(slv_pcnoc_snoc_links), + .links = slv_pcnoc_snoc_links, +}; + +static struct qcom_icc_node slv_kpss_ahb = { + .name = "slv_kpss_ahb", + .id = QCS404_SLAVE_APPSS, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_wcss = { + .name = "slv_wcss", + .id = QCS404_SLAVE_WCSS, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 23, +}; + +static const u16 slv_snoc_bimc_1_links[] = { + QCS404_SNOC_BIMC_1_MAS +}; + +static struct qcom_icc_node slv_snoc_bimc_1 = { + .name = "slv_snoc_bimc_1", + .id = QCS404_SNOC_BIMC_1_SLV, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 104, + .num_links = ARRAY_SIZE(slv_snoc_bimc_1_links), + .links = slv_snoc_bimc_1_links, +}; + +static struct qcom_icc_node slv_imem = { + .name = "slv_imem", + .id = QCS404_SLAVE_OCIMEM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 26, +}; + +static const u16 slv_snoc_pcnoc_links[] = { + QCS404_SNOC_PNOC_MAS +}; + +static struct qcom_icc_node slv_snoc_pcnoc = { + .name = "slv_snoc_pcnoc", + .id = QCS404_SNOC_PNOC_SLV, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 28, + .num_links = ARRAY_SIZE(slv_snoc_pcnoc_links), + .links = slv_snoc_pcnoc_links, +}; + +static struct qcom_icc_node slv_qdss_stm = { + .name = "slv_qdss_stm", + .id = QCS404_SLAVE_QDSS_STM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 30, +}; + +static struct qcom_icc_node slv_cats_0 = { + .name = "slv_cats_0", + .id = QCS404_SLAVE_CATS_128, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_cats_1 = { + .name = "slv_cats_1", + .id = QCS404_SLAVE_OCMEM_64, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_lpass = { + .name = "slv_lpass", + .id = QCS404_SLAVE_LPASS, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; static struct qcom_icc_node *qcs404_bimc_nodes[] = { [MASTER_AMPSS_M0] = &mas_apps_proc, -- cgit v1.2.3-70-g09d2 From 79099cd003c30d04e32523027c66324ec90a9dc1 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 4 Sep 2021 02:24:21 +0300 Subject: interconnect: qcom: drop DEFINE_QNODE macro Drop DEFINE_QNODE macro which has become unused. Signed-off-by: Dmitry Baryshkov Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Marijn Suijten Link: https://lore.kernel.org/r/20210903232421.1384199-12-dmitry.baryshkov@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.h | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index 0824ee34d816..f5744de4da19 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -82,20 +82,6 @@ struct qcom_icc_desc { unsigned int qos_offset; }; -#define DEFINE_QNODE(_name, _id, _buswidth, _mas_rpm_id, _slv_rpm_id, \ - ...) \ - static const u16 _name ## _links[] = { __VA_ARGS__ }; \ - \ - static struct qcom_icc_node _name = { \ - .name = #_name, \ - .id = _id, \ - .buswidth = _buswidth, \ - .mas_rpm_id = _mas_rpm_id, \ - .slv_rpm_id = _slv_rpm_id, \ - .num_links = ARRAY_SIZE(_name ## _links), \ - .links = _name ## _links, \ - } - /* Valid for both NoC and BIMC */ #define NOC_QOS_MODE_INVALID -1 #define NOC_QOS_MODE_FIXED 0x0 -- cgit v1.2.3-70-g09d2 From 2661342953f66279f50589ed26f03ec237a4655c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 24 Sep 2021 15:34:40 +0200 Subject: interconnect: samsung: describe drivers in KConfig Describe better which driver applies to which SoC, to make configuring kernel for Samsung SoC easier. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20210924133441.112263-1-krzysztof.kozlowski@canonical.com Signed-off-by: Georgi Djakov --- drivers/interconnect/samsung/Kconfig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/interconnect/samsung/Kconfig b/drivers/interconnect/samsung/Kconfig index 6820e4f772cc..fbee87e379d0 100644 --- a/drivers/interconnect/samsung/Kconfig +++ b/drivers/interconnect/samsung/Kconfig @@ -6,8 +6,10 @@ config INTERCONNECT_SAMSUNG Interconnect drivers for Samsung SoCs. config INTERCONNECT_EXYNOS - tristate "Exynos generic interconnect driver" + tristate "Exynos SoC generic interconnect driver" depends on INTERCONNECT_SAMSUNG default y if ARCH_EXYNOS help - Generic interconnect driver for Exynos SoCs. + Generic interconnect driver for Samsung Exynos SoCs (e.g. Exynos3250, + Exynos4210, Exynos4412, Exynos542x, Exynos5433). + Choose Y here only if you build for such Samsung SoC. -- cgit v1.2.3-70-g09d2 From c6ae0bce6bf39042796df9fb1e857b596047f0a5 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 28 Sep 2021 10:20:00 +0800 Subject: dt-bindings: phy: qcom,qusb2: Add missing vdd-supply Besides vdda-pll and vdda-phy-dpdm, vdd-supply is a required supply for PHY digital circuit operation. Add it for correctness and completeness. Signed-off-by: Shawn Guo Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20210928022002.26286-2-shawn.guo@linaro.org Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml index 48ae604b2194..aa2e409a1a09 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml @@ -51,6 +51,10 @@ properties: - const: ref - const: iface + vdd-supply: + description: + Phandle to 0.9V regulator supply to PHY digital circuit. + vdda-pll-supply: description: Phandle to 1.8V regulator supply to PHY refclk pll block. @@ -157,6 +161,7 @@ required: - "#phy-cells" - clocks - clock-names + - vdd-supply - vdda-pll-supply - vdda-phy-dpdm-supply - resets @@ -175,6 +180,7 @@ examples: <&gcc GCC_RX1_USB2_CLKREF_CLK>; clock-names = "cfg_ahb", "ref"; + vdd-supply = <&pm8994_l28>; vdda-pll-supply = <&pm8994_l12>; vdda-phy-dpdm-supply = <&pm8994_l24>; -- cgit v1.2.3-70-g09d2 From 19b6348e472c34e360157d68c6ee10b19dd23154 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 28 Sep 2021 10:20:01 +0800 Subject: phy: qcom-qusb2: Add missing vdd supply Per downstream kernel, beside vdda-pll and vdda-phy-dpdm, vdd is also a required supply for QUSB2 PHY digital circuit operation. The driver works right now likely because firmware already sets it up. Add it for correctness and completeness. Signed-off-by: Shawn Guo Link: https://lore.kernel.org/r/20210928022002.26286-3-shawn.guo@linaro.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qusb2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c index ae063a8b3e28..7a8e4333babe 100644 --- a/drivers/phy/qualcomm/phy-qcom-qusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c @@ -371,7 +371,7 @@ static const struct qusb2_phy_cfg sm6115_phy_cfg = { }; static const char * const qusb2_phy_vreg_names[] = { - "vdda-pll", "vdda-phy-dpdm", + "vdd", "vdda-pll", "vdda-phy-dpdm", }; #define QUSB2_NUM_VREGS ARRAY_SIZE(qusb2_phy_vreg_names) -- cgit v1.2.3-70-g09d2 From 304b0ba0a21b216683bc887d18dc5ad8d7d94752 Mon Sep 17 00:00:00 2001 From: Jeya R Date: Mon, 20 Sep 2021 13:04:34 +0530 Subject: misc: fastrpc: Update number of max fastrpc sessions For latest chipsets, upto 13 fastrpc sessions can be supported. This includes 12 compute sessions and 1 cpz session. Not updating this might result to out of bounds memory access issues if more than 9 context bank nodes are added to the DT file. Reviewed-by: Srinivas Kandagatla Signed-off-by: Jeya R Link: https://lore.kernel.org/r/1632123274-32054-1-git-send-email-jeyr@codeaurora.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index beda610e6b30..bd7811e3f761 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -24,7 +24,7 @@ #define SDSP_DOMAIN_ID (2) #define CDSP_DOMAIN_ID (3) #define FASTRPC_DEV_MAX 4 /* adsp, mdsp, slpi, cdsp*/ -#define FASTRPC_MAX_SESSIONS 9 /*8 compute, 1 cpz*/ +#define FASTRPC_MAX_SESSIONS 13 /*12 compute, 1 cpz*/ #define FASTRPC_ALIGN 128 #define FASTRPC_MAX_FDLIST 16 #define FASTRPC_MAX_CRCLIST 64 -- cgit v1.2.3-70-g09d2 From 847afd7bd5607ca974e562d7b2b8f800c4594fe6 Mon Sep 17 00:00:00 2001 From: Jeya R Date: Thu, 23 Sep 2021 14:07:52 +0530 Subject: misc: fastrpc: copy to user only for non-DMA-BUF heap buffers fastrpc_put_args is copying all the output buffers to user. For large number of output context buffers, this might cause performance degradation. Copying is not needed for DMA-BUF heap buffers. Signed-off-by: Jeya R Link: https://lore.kernel.org/r/1632386272-18139-1-git-send-email-jeyr@codeaurora.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index bd7811e3f761..e0d0c894c5fd 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -890,15 +890,17 @@ static int fastrpc_put_args(struct fastrpc_invoke_ctx *ctx, inbufs = REMOTE_SCALARS_INBUFS(ctx->sc); for (i = inbufs; i < ctx->nbufs; ++i) { - void *src = (void *)(uintptr_t)rpra[i].pv; - void *dst = (void *)(uintptr_t)ctx->args[i].ptr; - u64 len = rpra[i].len; + if (!ctx->maps[i]) { + void *src = (void *)(uintptr_t)rpra[i].pv; + void *dst = (void *)(uintptr_t)ctx->args[i].ptr; + u64 len = rpra[i].len; - if (!kernel) { - if (copy_to_user((void __user *)dst, src, len)) - return -EFAULT; - } else { - memcpy(dst, src, len); + if (!kernel) { + if (copy_to_user((void __user *)dst, src, len)) + return -EFAULT; + } else { + memcpy(dst, src, len); + } } } -- cgit v1.2.3-70-g09d2 From c31bbc140b94d0e40481966d974038569051433e Mon Sep 17 00:00:00 2001 From: Eli Billauer Date: Wed, 29 Sep 2021 12:44:42 +0300 Subject: char: xillybus: Eliminate redundant wrappers to DMA related calls The driver was originally written with the assumption that a different API must be used for DMA-related functions if the device is PCIe based or if not. Since Xillybus' driver supports devices on a PCIe bus (with xillybus_pcie) as well as connected directly to the processor (with xillybus_of), it originally used wrapper functions that ensure that a different API is used for each. This patch eliminates the said wrapper functions, as all use the same dma_* API now. This is most notable by the code deleted in xillybus_pcie.c and xillybus_of.c. It also eliminates the OF driver's check for a "dma-coherent" attribute in the device's OF entry, since this is taken care of by the kernel's implementation of dma_sync_single_for_*(). There is however still need for one wrapper function, which is merged from xillybus_pcie.c and xillybus_of.c into xillybus_core.c: The call to dma_map_single() is wrapped by a function that uses the Managed Device (devres) framework, in the absence of a relevant function in the current kernel's API. Suggested-by: Christophe JAILLET Suggested-by: Arnd Bergmann Reviewed-by: Arnd Bergmann Signed-off-by: Eli Billauer Link: https://lore.kernel.org/r/20210929094442.46383-1-eli.billauer@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/char/xillybus/xillybus.h | 23 +----- drivers/char/xillybus/xillybus_core.c | 129 ++++++++++++++++++++-------------- drivers/char/xillybus/xillybus_of.c | 86 +---------------------- drivers/char/xillybus/xillybus_pcie.c | 90 +----------------------- 4 files changed, 86 insertions(+), 242 deletions(-) diff --git a/drivers/char/xillybus/xillybus.h b/drivers/char/xillybus/xillybus.h index afce5bb4d127..51de7cbc579e 100644 --- a/drivers/char/xillybus/xillybus.h +++ b/drivers/char/xillybus/xillybus.h @@ -88,7 +88,7 @@ struct xilly_channel { struct xilly_endpoint { struct device *dev; - struct xilly_endpoint_hardware *ephw; + struct module *owner; int dma_using_dac; /* =1 if 64-bit DMA is used, =0 otherwise. */ __iomem void *registers; @@ -108,23 +108,6 @@ struct xilly_endpoint { unsigned int msg_buf_size; }; -struct xilly_endpoint_hardware { - struct module *owner; - void (*hw_sync_sgl_for_cpu)(struct xilly_endpoint *, - dma_addr_t, - size_t, - int); - void (*hw_sync_sgl_for_device)(struct xilly_endpoint *, - dma_addr_t, - size_t, - int); - int (*map_single)(struct xilly_endpoint *, - void *, - size_t, - int, - dma_addr_t *); -}; - struct xilly_mapping { struct device *device; dma_addr_t dma_addr; @@ -134,9 +117,7 @@ struct xilly_mapping { irqreturn_t xillybus_isr(int irq, void *data); -struct xilly_endpoint *xillybus_init_endpoint(struct device *dev, - struct xilly_endpoint_hardware - *ephw); +struct xilly_endpoint *xillybus_init_endpoint(struct device *dev); int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint); diff --git a/drivers/char/xillybus/xillybus_core.c b/drivers/char/xillybus/xillybus_core.c index 02f30140c2d5..11b7c4749274 100644 --- a/drivers/char/xillybus/xillybus_core.c +++ b/drivers/char/xillybus/xillybus_core.c @@ -122,10 +122,8 @@ irqreturn_t xillybus_isr(int irq, void *data) buf = ep->msgbuf_addr; buf_size = ep->msg_buf_size/sizeof(u32); - ep->ephw->hw_sync_sgl_for_cpu(ep, - ep->msgbuf_dma_addr, - ep->msg_buf_size, - DMA_FROM_DEVICE); + dma_sync_single_for_cpu(ep->dev, ep->msgbuf_dma_addr, + ep->msg_buf_size, DMA_FROM_DEVICE); for (i = 0; i < buf_size; i += 2) { if (((buf[i+1] >> 28) & 0xf) != ep->msg_counter) { @@ -140,11 +138,10 @@ irqreturn_t xillybus_isr(int irq, void *data) dev_err(ep->dev, "Lost sync with interrupt messages. Stopping.\n"); } else { - ep->ephw->hw_sync_sgl_for_device( - ep, - ep->msgbuf_dma_addr, - ep->msg_buf_size, - DMA_FROM_DEVICE); + dma_sync_single_for_device(ep->dev, + ep->msgbuf_dma_addr, + ep->msg_buf_size, + DMA_FROM_DEVICE); iowrite32(0x01, /* Message NACK */ ep->registers + fpga_msg_ctrl_reg); @@ -275,10 +272,8 @@ irqreturn_t xillybus_isr(int irq, void *data) } } - ep->ephw->hw_sync_sgl_for_device(ep, - ep->msgbuf_dma_addr, - ep->msg_buf_size, - DMA_FROM_DEVICE); + dma_sync_single_for_device(ep->dev, ep->msgbuf_dma_addr, + ep->msg_buf_size, DMA_FROM_DEVICE); ep->msg_counter = (ep->msg_counter + 1) & 0xf; ep->failed_messages = 0; @@ -304,6 +299,47 @@ struct xilly_alloc_state { u32 regdirection; }; +static void xilly_unmap(void *ptr) +{ + struct xilly_mapping *data = ptr; + + dma_unmap_single(data->device, data->dma_addr, + data->size, data->direction); + + kfree(ptr); +} + +static int xilly_map_single(struct xilly_endpoint *ep, + void *ptr, + size_t size, + int direction, + dma_addr_t *ret_dma_handle + ) +{ + dma_addr_t addr; + struct xilly_mapping *this; + + this = kzalloc(sizeof(*this), GFP_KERNEL); + if (!this) + return -ENOMEM; + + addr = dma_map_single(ep->dev, ptr, size, direction); + + if (dma_mapping_error(ep->dev, addr)) { + kfree(this); + return -ENODEV; + } + + this->device = ep->dev; + this->dma_addr = addr; + this->size = size; + this->direction = direction; + + *ret_dma_handle = addr; + + return devm_add_action_or_reset(ep->dev, xilly_unmap, this); +} + static int xilly_get_dma_buffers(struct xilly_endpoint *ep, struct xilly_alloc_state *s, struct xilly_buffer **buffers, @@ -355,9 +391,9 @@ static int xilly_get_dma_buffers(struct xilly_endpoint *ep, s->left_of_salami = allocsize; } - rc = ep->ephw->map_single(ep, s->salami, - bytebufsize, s->direction, - &dma_addr); + rc = xilly_map_single(ep, s->salami, + bytebufsize, s->direction, + &dma_addr); if (rc) return rc; @@ -620,11 +656,10 @@ static int xilly_obtain_idt(struct xilly_endpoint *endpoint) return -ENODEV; } - endpoint->ephw->hw_sync_sgl_for_cpu( - channel->endpoint, - channel->wr_buffers[0]->dma_addr, - channel->wr_buf_size, - DMA_FROM_DEVICE); + dma_sync_single_for_cpu(channel->endpoint->dev, + channel->wr_buffers[0]->dma_addr, + channel->wr_buf_size, + DMA_FROM_DEVICE); if (channel->wr_buffers[0]->end_offset != endpoint->idtlen) { dev_err(endpoint->dev, @@ -735,11 +770,10 @@ static ssize_t xillybus_read(struct file *filp, char __user *userbuf, if (!empty) { /* Go on, now without the spinlock */ if (bufpos == 0) /* Position zero means it's virgin */ - channel->endpoint->ephw->hw_sync_sgl_for_cpu( - channel->endpoint, - channel->wr_buffers[bufidx]->dma_addr, - channel->wr_buf_size, - DMA_FROM_DEVICE); + dma_sync_single_for_cpu(channel->endpoint->dev, + channel->wr_buffers[bufidx]->dma_addr, + channel->wr_buf_size, + DMA_FROM_DEVICE); if (copy_to_user( userbuf, @@ -751,11 +785,10 @@ static ssize_t xillybus_read(struct file *filp, char __user *userbuf, bytes_done += howmany; if (bufferdone) { - channel->endpoint->ephw->hw_sync_sgl_for_device( - channel->endpoint, - channel->wr_buffers[bufidx]->dma_addr, - channel->wr_buf_size, - DMA_FROM_DEVICE); + dma_sync_single_for_device(channel->endpoint->dev, + channel->wr_buffers[bufidx]->dma_addr, + channel->wr_buf_size, + DMA_FROM_DEVICE); /* * Tell FPGA the buffer is done with. It's an @@ -1055,11 +1088,10 @@ static int xillybus_myflush(struct xilly_channel *channel, long timeout) else channel->rd_host_buf_idx++; - channel->endpoint->ephw->hw_sync_sgl_for_device( - channel->endpoint, - channel->rd_buffers[bufidx]->dma_addr, - channel->rd_buf_size, - DMA_TO_DEVICE); + dma_sync_single_for_device(channel->endpoint->dev, + channel->rd_buffers[bufidx]->dma_addr, + channel->rd_buf_size, + DMA_TO_DEVICE); mutex_lock(&channel->endpoint->register_mutex); @@ -1275,11 +1307,10 @@ static ssize_t xillybus_write(struct file *filp, const char __user *userbuf, if ((bufpos == 0) || /* Zero means it's virgin */ (channel->rd_leftovers[3] != 0)) { - channel->endpoint->ephw->hw_sync_sgl_for_cpu( - channel->endpoint, - channel->rd_buffers[bufidx]->dma_addr, - channel->rd_buf_size, - DMA_TO_DEVICE); + dma_sync_single_for_cpu(channel->endpoint->dev, + channel->rd_buffers[bufidx]->dma_addr, + channel->rd_buf_size, + DMA_TO_DEVICE); /* Virgin, but leftovers are due */ for (i = 0; i < bufpos; i++) @@ -1297,11 +1328,10 @@ static ssize_t xillybus_write(struct file *filp, const char __user *userbuf, bytes_done += howmany; if (bufferdone) { - channel->endpoint->ephw->hw_sync_sgl_for_device( - channel->endpoint, - channel->rd_buffers[bufidx]->dma_addr, - channel->rd_buf_size, - DMA_TO_DEVICE); + dma_sync_single_for_device(channel->endpoint->dev, + channel->rd_buffers[bufidx]->dma_addr, + channel->rd_buf_size, + DMA_TO_DEVICE); mutex_lock(&channel->endpoint->register_mutex); @@ -1772,9 +1802,7 @@ static const struct file_operations xillybus_fops = { .poll = xillybus_poll, }; -struct xilly_endpoint *xillybus_init_endpoint(struct device *dev, - struct xilly_endpoint_hardware - *ephw) +struct xilly_endpoint *xillybus_init_endpoint(struct device *dev) { struct xilly_endpoint *endpoint; @@ -1783,7 +1811,6 @@ struct xilly_endpoint *xillybus_init_endpoint(struct device *dev, return NULL; endpoint->dev = dev; - endpoint->ephw = ephw; endpoint->msg_counter = 0x0b; endpoint->failed_messages = 0; endpoint->fatal_error = 0; @@ -1910,7 +1937,7 @@ int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint) goto failed_idt; rc = xillybus_init_chrdev(dev, &xillybus_fops, - endpoint->ephw->owner, endpoint, + endpoint->owner, endpoint, idt_handle.names, idt_handle.names_len, endpoint->num_channels, diff --git a/drivers/char/xillybus/xillybus_of.c b/drivers/char/xillybus/xillybus_of.c index 4e6e0c19d8c8..e5372e45d211 100644 --- a/drivers/char/xillybus/xillybus_of.c +++ b/drivers/char/xillybus/xillybus_of.c @@ -31,102 +31,22 @@ static const struct of_device_id xillybus_of_match[] = { MODULE_DEVICE_TABLE(of, xillybus_of_match); -static void xilly_dma_sync_single_for_cpu_of(struct xilly_endpoint *ep, - dma_addr_t dma_handle, - size_t size, - int direction) -{ - dma_sync_single_for_cpu(ep->dev, dma_handle, size, direction); -} - -static void xilly_dma_sync_single_for_device_of(struct xilly_endpoint *ep, - dma_addr_t dma_handle, - size_t size, - int direction) -{ - dma_sync_single_for_device(ep->dev, dma_handle, size, direction); -} - -static void xilly_dma_sync_single_nop(struct xilly_endpoint *ep, - dma_addr_t dma_handle, - size_t size, - int direction) -{ -} - -static void xilly_of_unmap(void *ptr) -{ - struct xilly_mapping *data = ptr; - - dma_unmap_single(data->device, data->dma_addr, - data->size, data->direction); - - kfree(ptr); -} - -static int xilly_map_single_of(struct xilly_endpoint *ep, - void *ptr, - size_t size, - int direction, - dma_addr_t *ret_dma_handle - ) -{ - dma_addr_t addr; - struct xilly_mapping *this; - - this = kzalloc(sizeof(*this), GFP_KERNEL); - if (!this) - return -ENOMEM; - - addr = dma_map_single(ep->dev, ptr, size, direction); - - if (dma_mapping_error(ep->dev, addr)) { - kfree(this); - return -ENODEV; - } - - this->device = ep->dev; - this->dma_addr = addr; - this->size = size; - this->direction = direction; - - *ret_dma_handle = addr; - - return devm_add_action_or_reset(ep->dev, xilly_of_unmap, this); -} - -static struct xilly_endpoint_hardware of_hw = { - .owner = THIS_MODULE, - .hw_sync_sgl_for_cpu = xilly_dma_sync_single_for_cpu_of, - .hw_sync_sgl_for_device = xilly_dma_sync_single_for_device_of, - .map_single = xilly_map_single_of, -}; - -static struct xilly_endpoint_hardware of_hw_coherent = { - .owner = THIS_MODULE, - .hw_sync_sgl_for_cpu = xilly_dma_sync_single_nop, - .hw_sync_sgl_for_device = xilly_dma_sync_single_nop, - .map_single = xilly_map_single_of, -}; - static int xilly_drv_probe(struct platform_device *op) { struct device *dev = &op->dev; struct xilly_endpoint *endpoint; int rc; int irq; - struct xilly_endpoint_hardware *ephw = &of_hw; - if (of_property_read_bool(dev->of_node, "dma-coherent")) - ephw = &of_hw_coherent; - - endpoint = xillybus_init_endpoint(dev, ephw); + endpoint = xillybus_init_endpoint(dev); if (!endpoint) return -ENOMEM; dev_set_drvdata(dev, endpoint); + endpoint->owner = THIS_MODULE; + endpoint->registers = devm_platform_ioremap_resource(op, 0); if (IS_ERR(endpoint->registers)) return PTR_ERR(endpoint->registers); diff --git a/drivers/char/xillybus/xillybus_pcie.c b/drivers/char/xillybus/xillybus_pcie.c index a6ef4ce90649..9858711e3e79 100644 --- a/drivers/char/xillybus/xillybus_pcie.c +++ b/drivers/char/xillybus/xillybus_pcie.c @@ -32,105 +32,21 @@ static const struct pci_device_id xillyids[] = { { /* End: all zeroes */ } }; -static int xilly_pci_direction(int direction) -{ - switch (direction) { - case DMA_TO_DEVICE: - case DMA_FROM_DEVICE: - return direction; - default: - return DMA_BIDIRECTIONAL; - } -} - -static void xilly_dma_sync_single_for_cpu_pci(struct xilly_endpoint *ep, - dma_addr_t dma_handle, - size_t size, - int direction) -{ - dma_sync_single_for_cpu(ep->dev, dma_handle, size, - xilly_pci_direction(direction)); -} - -static void xilly_dma_sync_single_for_device_pci(struct xilly_endpoint *ep, - dma_addr_t dma_handle, - size_t size, - int direction) -{ - dma_sync_single_for_device(ep->dev, dma_handle, size, - xilly_pci_direction(direction)); -} - -static void xilly_pci_unmap(void *ptr) -{ - struct xilly_mapping *data = ptr; - - dma_unmap_single(data->device, data->dma_addr, data->size, - data->direction); - - kfree(ptr); -} - -/* - * Map either through the PCI DMA mapper or the non_PCI one. Behind the - * scenes exactly the same functions are called with the same parameters, - * but that can change. - */ - -static int xilly_map_single_pci(struct xilly_endpoint *ep, - void *ptr, - size_t size, - int direction, - dma_addr_t *ret_dma_handle - ) -{ - int pci_direction; - dma_addr_t addr; - struct xilly_mapping *this; - - this = kzalloc(sizeof(*this), GFP_KERNEL); - if (!this) - return -ENOMEM; - - pci_direction = xilly_pci_direction(direction); - - addr = dma_map_single(ep->dev, ptr, size, pci_direction); - - if (dma_mapping_error(ep->dev, addr)) { - kfree(this); - return -ENODEV; - } - - this->device = ep->dev; - this->dma_addr = addr; - this->size = size; - this->direction = pci_direction; - - *ret_dma_handle = addr; - - return devm_add_action_or_reset(ep->dev, xilly_pci_unmap, this); -} - -static struct xilly_endpoint_hardware pci_hw = { - .owner = THIS_MODULE, - .hw_sync_sgl_for_cpu = xilly_dma_sync_single_for_cpu_pci, - .hw_sync_sgl_for_device = xilly_dma_sync_single_for_device_pci, - .map_single = xilly_map_single_pci, -}; - static int xilly_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct xilly_endpoint *endpoint; int rc; - endpoint = xillybus_init_endpoint(&pdev->dev, &pci_hw); + endpoint = xillybus_init_endpoint(&pdev->dev); if (!endpoint) return -ENOMEM; pci_set_drvdata(pdev, endpoint); + endpoint->owner = THIS_MODULE; + rc = pcim_enable_device(pdev); if (rc) { dev_err(endpoint->dev, -- cgit v1.2.3-70-g09d2 From 29a9f27574692a71c04fd41ca4bbf8eae842af13 Mon Sep 17 00:00:00 2001 From: Shuo Liu Date: Thu, 23 Sep 2021 16:41:27 +0800 Subject: virt: acrn: Introduce interfaces for MMIO device passthrough MMIO device passthrough enables an OS in a virtual machine to directly access a MMIO device in the host. It promises almost the native performance, which is required in performance-critical scenarios of ACRN. HSM provides the following ioctls: - Assign - ACRN_IOCTL_ASSIGN_MMIODEV Pass data struct acrn_mmiodev from userspace to the hypervisor, and inform the hypervisor to assign a MMIO device to a User VM. - De-assign - ACRN_IOCTL_DEASSIGN_PCIDEV Pass data struct acrn_mmiodev from userspace to the hypervisor, and inform the hypervisor to de-assign a MMIO device from a User VM. These new APIs will be used by user space code vm_assign_mmiodev and vm_deassign_mmiodev in https://github.com/projectacrn/acrn-hypervisor/blob/master/devicemodel/core/vmmapi.c Signed-off-by: Shuo Liu Signed-off-by: Fei Li Link: https://lore.kernel.org/r/20210923084128.18902-2-fei1.li@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/virt/acrn/hsm.c | 25 +++++++++++++++++++++++++ drivers/virt/acrn/hypercall.h | 26 ++++++++++++++++++++++++++ include/uapi/linux/acrn.h | 28 ++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+) diff --git a/drivers/virt/acrn/hsm.c b/drivers/virt/acrn/hsm.c index 130e12b8652a..f567ca59d7c2 100644 --- a/drivers/virt/acrn/hsm.c +++ b/drivers/virt/acrn/hsm.c @@ -114,6 +114,7 @@ static long acrn_dev_ioctl(struct file *filp, unsigned int cmd, struct acrn_ptdev_irq *irq_info; struct acrn_ioeventfd ioeventfd; struct acrn_vm_memmap memmap; + struct acrn_mmiodev *mmiodev; struct acrn_msi_entry *msi; struct acrn_pcidev *pcidev; struct acrn_irqfd irqfd; @@ -217,6 +218,30 @@ static long acrn_dev_ioctl(struct file *filp, unsigned int cmd, ret = acrn_vm_memseg_unmap(vm, &memmap); break; + case ACRN_IOCTL_ASSIGN_MMIODEV: + mmiodev = memdup_user((void __user *)ioctl_param, + sizeof(struct acrn_mmiodev)); + if (IS_ERR(mmiodev)) + return PTR_ERR(mmiodev); + + ret = hcall_assign_mmiodev(vm->vmid, virt_to_phys(mmiodev)); + if (ret < 0) + dev_dbg(acrn_dev.this_device, + "Failed to assign MMIO device!\n"); + kfree(mmiodev); + break; + case ACRN_IOCTL_DEASSIGN_MMIODEV: + mmiodev = memdup_user((void __user *)ioctl_param, + sizeof(struct acrn_mmiodev)); + if (IS_ERR(mmiodev)) + return PTR_ERR(mmiodev); + + ret = hcall_deassign_mmiodev(vm->vmid, virt_to_phys(mmiodev)); + if (ret < 0) + dev_dbg(acrn_dev.this_device, + "Failed to deassign MMIO device!\n"); + kfree(mmiodev); + break; case ACRN_IOCTL_ASSIGN_PCIDEV: pcidev = memdup_user((void __user *)ioctl_param, sizeof(struct acrn_pcidev)); diff --git a/drivers/virt/acrn/hypercall.h b/drivers/virt/acrn/hypercall.h index 0cfad05bd1a9..f0c78e52cebb 100644 --- a/drivers/virt/acrn/hypercall.h +++ b/drivers/virt/acrn/hypercall.h @@ -41,6 +41,8 @@ #define HC_RESET_PTDEV_INTR _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x04) #define HC_ASSIGN_PCIDEV _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x05) #define HC_DEASSIGN_PCIDEV _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x06) +#define HC_ASSIGN_MMIODEV _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x07) +#define HC_DEASSIGN_MMIODEV _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x08) #define HC_ID_PM_BASE 0x80UL #define HC_PM_GET_CPU_STATE _HC_ID(HC_ID, HC_ID_PM_BASE + 0x00) @@ -194,6 +196,30 @@ static inline long hcall_set_memory_regions(u64 regions_pa) return acrn_hypercall1(HC_VM_SET_MEMORY_REGIONS, regions_pa); } +/** + * hcall_assign_mmiodev() - Assign a MMIO device to a User VM + * @vmid: User VM ID + * @addr: Service VM GPA of the &struct acrn_mmiodev + * + * Return: 0 on success, <0 on failure + */ +static inline long hcall_assign_mmiodev(u64 vmid, u64 addr) +{ + return acrn_hypercall2(HC_ASSIGN_MMIODEV, vmid, addr); +} + +/** + * hcall_deassign_mmiodev() - De-assign a PCI device from a User VM + * @vmid: User VM ID + * @addr: Service VM GPA of the &struct acrn_mmiodev + * + * Return: 0 on success, <0 on failure + */ +static inline long hcall_deassign_mmiodev(u64 vmid, u64 addr) +{ + return acrn_hypercall2(HC_DEASSIGN_MMIODEV, vmid, addr); +} + /** * hcall_assign_pcidev() - Assign a PCI device to a User VM * @vmid: User VM ID diff --git a/include/uapi/linux/acrn.h b/include/uapi/linux/acrn.h index 353b2a2e4536..470036d6b1ac 100644 --- a/include/uapi/linux/acrn.h +++ b/include/uapi/linux/acrn.h @@ -396,6 +396,7 @@ struct acrn_ptdev_irq { /* Type of PCI device assignment */ #define ACRN_PTDEV_QUIRK_ASSIGN (1U << 0) +#define ACRN_MMIODEV_RES_NUM 3 #define ACRN_PCI_NUM_BARS 6 /** * struct acrn_pcidev - Info for assigning or de-assigning a PCI device @@ -417,6 +418,29 @@ struct acrn_pcidev { __u32 bar[ACRN_PCI_NUM_BARS]; }; +/** + * struct acrn_mmiodev - Info for assigning or de-assigning a MMIO device + * @name: Name of the MMIO device. + * @res[].user_vm_pa: Physical address of User VM of the MMIO region + * for the MMIO device. + * @res[].service_vm_pa: Physical address of Service VM of the MMIO + * region for the MMIO device. + * @res[].size: Size of the MMIO region for the MMIO device. + * @res[].mem_type: Memory type of the MMIO region for the MMIO + * device. + * + * This structure will be passed to hypervisor directly. + */ +struct acrn_mmiodev { + __u8 name[8]; + struct { + __u64 user_vm_pa; + __u64 service_vm_pa; + __u64 size; + __u64 mem_type; + } res[ACRN_MMIODEV_RES_NUM]; +}; + /** * struct acrn_msi_entry - Info for injecting a MSI interrupt to a VM * @msi_addr: MSI addr[19:12] with dest vCPU ID @@ -568,6 +592,10 @@ struct acrn_irqfd { _IOW(ACRN_IOCTL_TYPE, 0x55, struct acrn_pcidev) #define ACRN_IOCTL_DEASSIGN_PCIDEV \ _IOW(ACRN_IOCTL_TYPE, 0x56, struct acrn_pcidev) +#define ACRN_IOCTL_ASSIGN_MMIODEV \ + _IOW(ACRN_IOCTL_TYPE, 0x57, struct acrn_mmiodev) +#define ACRN_IOCTL_DEASSIGN_MMIODEV \ + _IOW(ACRN_IOCTL_TYPE, 0x58, struct acrn_mmiodev) #define ACRN_IOCTL_PM_GET_CPU_STATE \ _IOWR(ACRN_IOCTL_TYPE, 0x60, __u64) -- cgit v1.2.3-70-g09d2 From 424f1ac2d832f31a2814c799bd50decf6a9f8e74 Mon Sep 17 00:00:00 2001 From: Shuo Liu Date: Thu, 23 Sep 2021 16:41:28 +0800 Subject: virt: acrn: Introduce interfaces for virtual device creating/destroying The ACRN hypervisor can emulate a virtual device within hypervisor for a Guest VM. The emulated virtual device can work without the ACRN userspace after creation. The hypervisor do the emulation of that device. To support the virtual device creating/destroying, HSM provides the following ioctls: - ACRN_IOCTL_CREATE_VDEV Pass data struct acrn_vdev from userspace to the hypervisor, and inform the hypervisor to create a virtual device for a User VM. - ACRN_IOCTL_DESTROY_VDEV Pass data struct acrn_vdev from userspace to the hypervisor, and inform the hypervisor to destroy a virtual device of a User VM. These new APIs will be used by user space code vm_add_hv_vdev and vm_remove_hv_vdev in https://github.com/projectacrn/acrn-hypervisor/blob/master/devicemodel/core/vmmapi.c Signed-off-by: Shuo Liu Signed-off-by: Fei Li Link: https://lore.kernel.org/r/20210923084128.18902-3-fei1.li@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/virt/acrn/hsm.c | 24 ++++++++++++++++++++++++ drivers/virt/acrn/hypercall.h | 26 ++++++++++++++++++++++++++ include/uapi/linux/acrn.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+) diff --git a/drivers/virt/acrn/hsm.c b/drivers/virt/acrn/hsm.c index f567ca59d7c2..5419794fccf1 100644 --- a/drivers/virt/acrn/hsm.c +++ b/drivers/virt/acrn/hsm.c @@ -118,6 +118,7 @@ static long acrn_dev_ioctl(struct file *filp, unsigned int cmd, struct acrn_msi_entry *msi; struct acrn_pcidev *pcidev; struct acrn_irqfd irqfd; + struct acrn_vdev *vdev; struct page *page; u64 cstate_cmd; int i, ret = 0; @@ -266,6 +267,29 @@ static long acrn_dev_ioctl(struct file *filp, unsigned int cmd, "Failed to deassign pci device!\n"); kfree(pcidev); break; + case ACRN_IOCTL_CREATE_VDEV: + vdev = memdup_user((void __user *)ioctl_param, + sizeof(struct acrn_vdev)); + if (IS_ERR(vdev)) + return PTR_ERR(vdev); + + ret = hcall_create_vdev(vm->vmid, virt_to_phys(vdev)); + if (ret < 0) + dev_dbg(acrn_dev.this_device, + "Failed to create virtual device!\n"); + kfree(vdev); + break; + case ACRN_IOCTL_DESTROY_VDEV: + vdev = memdup_user((void __user *)ioctl_param, + sizeof(struct acrn_vdev)); + if (IS_ERR(vdev)) + return PTR_ERR(vdev); + ret = hcall_destroy_vdev(vm->vmid, virt_to_phys(vdev)); + if (ret < 0) + dev_dbg(acrn_dev.this_device, + "Failed to destroy virtual device!\n"); + kfree(vdev); + break; case ACRN_IOCTL_SET_PTDEV_INTR: irq_info = memdup_user((void __user *)ioctl_param, sizeof(struct acrn_ptdev_irq)); diff --git a/drivers/virt/acrn/hypercall.h b/drivers/virt/acrn/hypercall.h index f0c78e52cebb..71d300821a18 100644 --- a/drivers/virt/acrn/hypercall.h +++ b/drivers/virt/acrn/hypercall.h @@ -43,6 +43,8 @@ #define HC_DEASSIGN_PCIDEV _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x06) #define HC_ASSIGN_MMIODEV _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x07) #define HC_DEASSIGN_MMIODEV _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x08) +#define HC_CREATE_VDEV _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x09) +#define HC_DESTROY_VDEV _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x0A) #define HC_ID_PM_BASE 0x80UL #define HC_PM_GET_CPU_STATE _HC_ID(HC_ID, HC_ID_PM_BASE + 0x00) @@ -196,6 +198,30 @@ static inline long hcall_set_memory_regions(u64 regions_pa) return acrn_hypercall1(HC_VM_SET_MEMORY_REGIONS, regions_pa); } +/** + * hcall_create_vdev() - Create a virtual device for a User VM + * @vmid: User VM ID + * @addr: Service VM GPA of the &struct acrn_vdev + * + * Return: 0 on success, <0 on failure + */ +static inline long hcall_create_vdev(u64 vmid, u64 addr) +{ + return acrn_hypercall2(HC_CREATE_VDEV, vmid, addr); +} + +/** + * hcall_destroy_vdev() - Destroy a virtual device of a User VM + * @vmid: User VM ID + * @addr: Service VM GPA of the &struct acrn_vdev + * + * Return: 0 on success, <0 on failure + */ +static inline long hcall_destroy_vdev(u64 vmid, u64 addr) +{ + return acrn_hypercall2(HC_DESTROY_VDEV, vmid, addr); +} + /** * hcall_assign_mmiodev() - Assign a MMIO device to a User VM * @vmid: User VM ID diff --git a/include/uapi/linux/acrn.h b/include/uapi/linux/acrn.h index 470036d6b1ac..ccf47ed92500 100644 --- a/include/uapi/linux/acrn.h +++ b/include/uapi/linux/acrn.h @@ -441,6 +441,44 @@ struct acrn_mmiodev { } res[ACRN_MMIODEV_RES_NUM]; }; +/** + * struct acrn_vdev - Info for creating or destroying a virtual device + * @id: Union of identifier of the virtual device + * @id.value: Raw data of the identifier + * @id.fields.vendor: Vendor id of the virtual PCI device + * @id.fields.device: Device id of the virtual PCI device + * @id.fields.legacy_id: ID of the virtual device if not a PCI device + * @slot: Virtual Bus/Device/Function of the virtual + * device + * @io_base: IO resource base address of the virtual device + * @io_size: IO resource size of the virtual device + * @args: Arguments for the virtual device creation + * + * The created virtual device can be a PCI device or a legacy device (e.g. + * a virtual UART controller) and it is emulated by the hypervisor. This + * structure will be passed to hypervisor directly. + */ +struct acrn_vdev { + /* + * the identifier of the device, the low 32 bits represent the vendor + * id and device id of PCI device and the high 32 bits represent the + * device number of the legacy device + */ + union { + __u64 value; + struct { + __le16 vendor; + __le16 device; + __le32 legacy_id; + } fields; + } id; + + __u64 slot; + __u32 io_addr[ACRN_PCI_NUM_BARS]; + __u32 io_size[ACRN_PCI_NUM_BARS]; + __u8 args[128]; +}; + /** * struct acrn_msi_entry - Info for injecting a MSI interrupt to a VM * @msi_addr: MSI addr[19:12] with dest vCPU ID @@ -596,6 +634,10 @@ struct acrn_irqfd { _IOW(ACRN_IOCTL_TYPE, 0x57, struct acrn_mmiodev) #define ACRN_IOCTL_DEASSIGN_MMIODEV \ _IOW(ACRN_IOCTL_TYPE, 0x58, struct acrn_mmiodev) +#define ACRN_IOCTL_CREATE_VDEV \ + _IOW(ACRN_IOCTL_TYPE, 0x59, struct acrn_vdev) +#define ACRN_IOCTL_DESTROY_VDEV \ + _IOW(ACRN_IOCTL_TYPE, 0x5A, struct acrn_vdev) #define ACRN_IOCTL_PM_GET_CPU_STATE \ _IOWR(ACRN_IOCTL_TYPE, 0x60, __u64) -- cgit v1.2.3-70-g09d2 From 03748d4e003c9f2ad3cd00e3e46f054dcad6b96d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 27 Sep 2021 14:41:53 +0100 Subject: iio: st_pressure_spi: Add missing entries SPI to device ID table Currently autoloading for SPI devices does not use the DT ID table, it uses SPI modalises. Supporting OF modalises is going to be difficult if not impractical, an attempt was made but has been reverted, so ensure that module autoloading works for this driver by adding SPI IDs for parts that only have a compatible listed. Fixes: 96c8395e2166 ("spi: Revert modalias changes") Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20210927134153.12739-1-broonie@kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/st_pressure_spi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c index ee393df54cee..ada2e2185282 100644 --- a/drivers/iio/pressure/st_pressure_spi.c +++ b/drivers/iio/pressure/st_pressure_spi.c @@ -117,6 +117,10 @@ static const struct spi_device_id st_press_id_table[] = { { LPS33HW_PRESS_DEV_NAME }, { LPS35HW_PRESS_DEV_NAME }, { LPS22HH_PRESS_DEV_NAME }, + { "lps001wp-press" }, + { "lps25h-press", }, + { "lps331ap-press" }, + { "lps22hb-press" }, {}, }; MODULE_DEVICE_TABLE(spi, st_press_id_table); -- cgit v1.2.3-70-g09d2 From d9de0fbdeb0103a204055efb69cb5cc8f5f12a6a Mon Sep 17 00:00:00 2001 From: Mihail Chindris Date: Thu, 7 Oct 2021 08:00:34 +0000 Subject: drivers: iio: dac: ad5766: Fix dt property name In the documentation the name for the property is output-range-microvolts which is a standard name, therefore this name must be used. Fixes: fd9373e41b9ba ("iio: dac: ad5766: add driver support for AD5766") Signed-off-by: Mihail Chindris Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20211007080035.2531-5-mihail.chindris@analog.com Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5766.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/dac/ad5766.c b/drivers/iio/dac/ad5766.c index 3104ec32dfac..dafda84fdea3 100644 --- a/drivers/iio/dac/ad5766.c +++ b/drivers/iio/dac/ad5766.c @@ -503,13 +503,13 @@ static int ad5766_get_output_range(struct ad5766_state *st) int i, ret, min, max, tmp[2]; ret = device_property_read_u32_array(&st->spi->dev, - "output-range-voltage", + "output-range-microvolts", tmp, 2); if (ret) return ret; - min = tmp[0] / 1000; - max = tmp[1] / 1000; + min = tmp[0] / 1000000; + max = tmp[1] / 1000000; for (i = 0; i < ARRAY_SIZE(ad5766_span_tbl); i++) { if (ad5766_span_tbl[i].min != min || ad5766_span_tbl[i].max != max) -- cgit v1.2.3-70-g09d2 From 8fc4f038fa832ec3543907fdcbe1334e1b0a8950 Mon Sep 17 00:00:00 2001 From: Mihail Chindris Date: Thu, 7 Oct 2021 08:00:36 +0000 Subject: Documentation:devicetree:bindings:iio:dac: Fix val A correct value for output-range-microvolts is -5 to 5 Volts not -5 to 5 milivolts Fixes: e904cc899293f ("dt-bindings: iio: dac: AD5766 yaml documentation") Signed-off-by: Mihail Chindris Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20211007080035.2531-6-mihail.chindris@analog.com Cc: Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/dac/adi,ad5766.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5766.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5766.yaml index d5c54813ce87..a8f7720d1e3e 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5766.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5766.yaml @@ -54,7 +54,7 @@ examples: ad5766@0 { compatible = "adi,ad5766"; - output-range-microvolts = <(-5000) 5000>; + output-range-microvolts = <(-5000000) 5000000>; reg = <0>; spi-cpol; spi-max-frequency = <1000000>; -- cgit v1.2.3-70-g09d2 From 4df4946d26bb7866b71cbdf86abe285ed3a79cee Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 12 Oct 2021 17:39:37 +0200 Subject: misc: lis3lv02d: Make lis3lv02d_remove_fs() return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up to now lis3lv02d_remove_fs() returns zero unconditionally. Make it return void instead which makes it easier to see in the callers that there is no error to handle. Also the return value of i2c and spi remove callbacks is ignored anyway. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211012153945.2651412-13-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/misc/lis3lv02d/lis3lv02d.c | 3 +-- drivers/misc/lis3lv02d/lis3lv02d.h | 2 +- drivers/misc/lis3lv02d/lis3lv02d_spi.c | 4 +++- drivers/platform/x86/hp_accel.c | 3 ++- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/misc/lis3lv02d/lis3lv02d.c b/drivers/misc/lis3lv02d/lis3lv02d.c index 70c5bb1e6f49..3a7808b796b1 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d.c +++ b/drivers/misc/lis3lv02d/lis3lv02d.c @@ -878,7 +878,7 @@ static int lis3lv02d_add_fs(struct lis3lv02d *lis3) return sysfs_create_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group); } -int lis3lv02d_remove_fs(struct lis3lv02d *lis3) +void lis3lv02d_remove_fs(struct lis3lv02d *lis3) { sysfs_remove_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group); platform_device_unregister(lis3->pdev); @@ -894,7 +894,6 @@ int lis3lv02d_remove_fs(struct lis3lv02d *lis3) pm_runtime_set_suspended(lis3->pm_dev); } kfree(lis3->reg_cache); - return 0; } EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); diff --git a/drivers/misc/lis3lv02d/lis3lv02d.h b/drivers/misc/lis3lv02d/lis3lv02d.h index c394c0b08519..195bd2fd2eb5 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d.h +++ b/drivers/misc/lis3lv02d/lis3lv02d.h @@ -312,7 +312,7 @@ int lis3lv02d_joystick_enable(struct lis3lv02d *lis3); void lis3lv02d_joystick_disable(struct lis3lv02d *lis3); void lis3lv02d_poweroff(struct lis3lv02d *lis3); int lis3lv02d_poweron(struct lis3lv02d *lis3); -int lis3lv02d_remove_fs(struct lis3lv02d *lis3); +void lis3lv02d_remove_fs(struct lis3lv02d *lis3); int lis3lv02d_init_dt(struct lis3lv02d *lis3); extern struct lis3lv02d lis3_dev; diff --git a/drivers/misc/lis3lv02d/lis3lv02d_spi.c b/drivers/misc/lis3lv02d/lis3lv02d_spi.c index f664ed123730..9e40dfb60742 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d_spi.c +++ b/drivers/misc/lis3lv02d/lis3lv02d_spi.c @@ -102,7 +102,9 @@ static int lis302dl_spi_remove(struct spi_device *spi) lis3lv02d_joystick_disable(lis3); lis3lv02d_poweroff(lis3); - return lis3lv02d_remove_fs(&lis3_dev); + lis3lv02d_remove_fs(&lis3_dev); + + return 0; } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c index cc53f725c041..b183967ecfb7 100644 --- a/drivers/platform/x86/hp_accel.c +++ b/drivers/platform/x86/hp_accel.c @@ -349,7 +349,8 @@ static int lis3lv02d_remove(struct platform_device *device) led_classdev_unregister(&hpled_led.led_classdev); flush_work(&hpled_led.work); - return lis3lv02d_remove_fs(&lis3_dev); + lis3lv02d_remove_fs(&lis3_dev); + return 0; } #ifdef CONFIG_PM_SLEEP -- cgit v1.2.3-70-g09d2 From 85385a51ceadfe79ae3b1eb5848fab6c3b917ea8 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 12 Oct 2021 17:39:36 +0200 Subject: misc: ad525x_dpot: Make ad_dpot_remove() return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up to now ad_dpot_remove() returns zero unconditionally. Make it return void instead which makes it easier to see in the callers that there is no error to handle. Also the return value of i2c and spi remove callbacks is ignored anyway. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211012153945.2651412-12-u.kleine-koenig@pengutronix.de Acked-by: Michael Hennerich Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ad525x_dpot-i2c.c | 3 ++- drivers/misc/ad525x_dpot-spi.c | 3 ++- drivers/misc/ad525x_dpot.c | 4 +--- drivers/misc/ad525x_dpot.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/misc/ad525x_dpot-i2c.c b/drivers/misc/ad525x_dpot-i2c.c index bd869ec5edba..0ee0c6d808c3 100644 --- a/drivers/misc/ad525x_dpot-i2c.c +++ b/drivers/misc/ad525x_dpot-i2c.c @@ -69,7 +69,8 @@ static int ad_dpot_i2c_probe(struct i2c_client *client, static int ad_dpot_i2c_remove(struct i2c_client *client) { - return ad_dpot_remove(&client->dev); + ad_dpot_remove(&client->dev); + return 0; } static const struct i2c_device_id ad_dpot_id[] = { diff --git a/drivers/misc/ad525x_dpot-spi.c b/drivers/misc/ad525x_dpot-spi.c index aea931dd272e..a9e75d80ad36 100644 --- a/drivers/misc/ad525x_dpot-spi.c +++ b/drivers/misc/ad525x_dpot-spi.c @@ -92,7 +92,8 @@ static int ad_dpot_spi_probe(struct spi_device *spi) static int ad_dpot_spi_remove(struct spi_device *spi) { - return ad_dpot_remove(&spi->dev); + ad_dpot_remove(&spi->dev); + return 0; } static const struct spi_device_id ad_dpot_spi_id[] = { diff --git a/drivers/misc/ad525x_dpot.c b/drivers/misc/ad525x_dpot.c index 5d8f3f6a95f2..756ef6912b5a 100644 --- a/drivers/misc/ad525x_dpot.c +++ b/drivers/misc/ad525x_dpot.c @@ -743,7 +743,7 @@ exit: } EXPORT_SYMBOL(ad_dpot_probe); -int ad_dpot_remove(struct device *dev) +void ad_dpot_remove(struct device *dev) { struct dpot_data *data = dev_get_drvdata(dev); int i; @@ -753,8 +753,6 @@ int ad_dpot_remove(struct device *dev) ad_dpot_remove_files(dev, data->feat, i); kfree(data); - - return 0; } EXPORT_SYMBOL(ad_dpot_remove); diff --git a/drivers/misc/ad525x_dpot.h b/drivers/misc/ad525x_dpot.h index ee8dc9f5a45a..72a9d6801937 100644 --- a/drivers/misc/ad525x_dpot.h +++ b/drivers/misc/ad525x_dpot.h @@ -209,6 +209,6 @@ struct ad_dpot_bus_data { int ad_dpot_probe(struct device *dev, struct ad_dpot_bus_data *bdata, unsigned long devid, const char *name); -int ad_dpot_remove(struct device *dev); +void ad_dpot_remove(struct device *dev); #endif -- cgit v1.2.3-70-g09d2 From 880732ae31e890a568a71eb50d5747284b3c4dbe Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 23 Sep 2021 19:29:18 +0200 Subject: samples/kfifo: Rename read_lock/write_lock The variables names read_lock and write_lock can clash with functions used for read/writer locks. Rename read_lock to read_access and write_lock to write_access to avoid a name collision. Signed-off-by: Sebastian Andrzej Siewior Link: https://lkml.kernel.org/r/20210806152551.qio7c3ho6pexezup@linutronix.de Link: https://lore.kernel.org/r/20210923172918.o22iwgvn3w7ilh44@linutronix.de Acked-by: Stefani Seibold Signed-off-by: Greg Kroah-Hartman --- samples/kfifo/bytestream-example.c | 12 ++++++------ samples/kfifo/inttype-example.c | 12 ++++++------ samples/kfifo/record-example.c | 12 ++++++------ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/samples/kfifo/bytestream-example.c b/samples/kfifo/bytestream-example.c index 5a90aa527877..642d0748c169 100644 --- a/samples/kfifo/bytestream-example.c +++ b/samples/kfifo/bytestream-example.c @@ -22,10 +22,10 @@ #define PROC_FIFO "bytestream-fifo" /* lock for procfs read access */ -static DEFINE_MUTEX(read_lock); +static DEFINE_MUTEX(read_access); /* lock for procfs write access */ -static DEFINE_MUTEX(write_lock); +static DEFINE_MUTEX(write_access); /* * define DYNAMIC in this example for a dynamically allocated fifo. @@ -116,12 +116,12 @@ static ssize_t fifo_write(struct file *file, const char __user *buf, int ret; unsigned int copied; - if (mutex_lock_interruptible(&write_lock)) + if (mutex_lock_interruptible(&write_access)) return -ERESTARTSYS; ret = kfifo_from_user(&test, buf, count, &copied); - mutex_unlock(&write_lock); + mutex_unlock(&write_access); if (ret) return ret; @@ -134,12 +134,12 @@ static ssize_t fifo_read(struct file *file, char __user *buf, int ret; unsigned int copied; - if (mutex_lock_interruptible(&read_lock)) + if (mutex_lock_interruptible(&read_access)) return -ERESTARTSYS; ret = kfifo_to_user(&test, buf, count, &copied); - mutex_unlock(&read_lock); + mutex_unlock(&read_access); if (ret) return ret; diff --git a/samples/kfifo/inttype-example.c b/samples/kfifo/inttype-example.c index e5403d8c971a..c61482ba94f4 100644 --- a/samples/kfifo/inttype-example.c +++ b/samples/kfifo/inttype-example.c @@ -22,10 +22,10 @@ #define PROC_FIFO "int-fifo" /* lock for procfs read access */ -static DEFINE_MUTEX(read_lock); +static DEFINE_MUTEX(read_access); /* lock for procfs write access */ -static DEFINE_MUTEX(write_lock); +static DEFINE_MUTEX(write_access); /* * define DYNAMIC in this example for a dynamically allocated fifo. @@ -109,12 +109,12 @@ static ssize_t fifo_write(struct file *file, const char __user *buf, int ret; unsigned int copied; - if (mutex_lock_interruptible(&write_lock)) + if (mutex_lock_interruptible(&write_access)) return -ERESTARTSYS; ret = kfifo_from_user(&test, buf, count, &copied); - mutex_unlock(&write_lock); + mutex_unlock(&write_access); if (ret) return ret; @@ -127,12 +127,12 @@ static ssize_t fifo_read(struct file *file, char __user *buf, int ret; unsigned int copied; - if (mutex_lock_interruptible(&read_lock)) + if (mutex_lock_interruptible(&read_access)) return -ERESTARTSYS; ret = kfifo_to_user(&test, buf, count, &copied); - mutex_unlock(&read_lock); + mutex_unlock(&read_access); if (ret) return ret; diff --git a/samples/kfifo/record-example.c b/samples/kfifo/record-example.c index f64f3d62d6c2..e4087b2d3fc4 100644 --- a/samples/kfifo/record-example.c +++ b/samples/kfifo/record-example.c @@ -22,10 +22,10 @@ #define PROC_FIFO "record-fifo" /* lock for procfs read access */ -static DEFINE_MUTEX(read_lock); +static DEFINE_MUTEX(read_access); /* lock for procfs write access */ -static DEFINE_MUTEX(write_lock); +static DEFINE_MUTEX(write_access); /* * define DYNAMIC in this example for a dynamically allocated fifo. @@ -123,12 +123,12 @@ static ssize_t fifo_write(struct file *file, const char __user *buf, int ret; unsigned int copied; - if (mutex_lock_interruptible(&write_lock)) + if (mutex_lock_interruptible(&write_access)) return -ERESTARTSYS; ret = kfifo_from_user(&test, buf, count, &copied); - mutex_unlock(&write_lock); + mutex_unlock(&write_access); if (ret) return ret; @@ -141,12 +141,12 @@ static ssize_t fifo_read(struct file *file, char __user *buf, int ret; unsigned int copied; - if (mutex_lock_interruptible(&read_lock)) + if (mutex_lock_interruptible(&read_access)) return -ERESTARTSYS; ret = kfifo_to_user(&test, buf, count, &copied); - mutex_unlock(&read_lock); + mutex_unlock(&read_access); if (ret) return ret; -- cgit v1.2.3-70-g09d2 From 7ae6478b304bc004c3139b422665b0e23b57f05c Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 13 Oct 2021 14:19:55 +0100 Subject: nvmem: core: rework nvmem cell instance creation In the existing design, we do not create a instance per nvmem cell consumer but we directly refer cell from nvmem cell list that are added to provider. However this design has some limitations when consumers want to assign name or connection id the nvmem cell instance, ex: via "nvmem-cell-names" or id in nvmem_cell_get(id). Having a name associated with nvmem cell consumer instance will help provider drivers in performing post processing of nvmem cell data if required before data is seen by the consumers. This is pretty normal with some vendors storing nvmem cells like mac-address in a vendor specific data layouts that are not directly usable by the consumer drivers. With this patch nvmem cell will be created dynamically during nvmem_cell_get and destroyed in nvmem_cell_put, allowing consumers to associate name with nvmem cell consumer instance. With this patch a new struct nvmem_cell_entry replaces struct nvmem_cell for storing nvmem cell information within the core. This patch does not change nvmem-consumer interface based on nvmem_cell. Tested-by: Joakim Zhang Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20211013131957.30271-2-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 165 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 105 insertions(+), 60 deletions(-) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 3d87fadaa160..f37d7838d2e3 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -45,8 +45,7 @@ struct nvmem_device { #define to_nvmem_device(d) container_of(d, struct nvmem_device, dev) #define FLAG_COMPAT BIT(0) - -struct nvmem_cell { +struct nvmem_cell_entry { const char *name; int offset; int bytes; @@ -57,6 +56,11 @@ struct nvmem_cell { struct list_head node; }; +struct nvmem_cell { + struct nvmem_cell_entry *entry; + const char *id; +}; + static DEFINE_MUTEX(nvmem_mutex); static DEFINE_IDA(nvmem_ida); @@ -422,7 +426,7 @@ static struct bus_type nvmem_bus_type = { .name = "nvmem", }; -static void nvmem_cell_drop(struct nvmem_cell *cell) +static void nvmem_cell_entry_drop(struct nvmem_cell_entry *cell) { blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_REMOVE, cell); mutex_lock(&nvmem_mutex); @@ -435,13 +439,13 @@ static void nvmem_cell_drop(struct nvmem_cell *cell) static void nvmem_device_remove_all_cells(const struct nvmem_device *nvmem) { - struct nvmem_cell *cell, *p; + struct nvmem_cell_entry *cell, *p; list_for_each_entry_safe(cell, p, &nvmem->cells, node) - nvmem_cell_drop(cell); + nvmem_cell_entry_drop(cell); } -static void nvmem_cell_add(struct nvmem_cell *cell) +static void nvmem_cell_entry_add(struct nvmem_cell_entry *cell) { mutex_lock(&nvmem_mutex); list_add_tail(&cell->node, &cell->nvmem->cells); @@ -449,9 +453,9 @@ static void nvmem_cell_add(struct nvmem_cell *cell) blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_ADD, cell); } -static int nvmem_cell_info_to_nvmem_cell_nodup(struct nvmem_device *nvmem, - const struct nvmem_cell_info *info, - struct nvmem_cell *cell) +static int nvmem_cell_info_to_nvmem_cell_entry_nodup(struct nvmem_device *nvmem, + const struct nvmem_cell_info *info, + struct nvmem_cell_entry *cell) { cell->nvmem = nvmem; cell->offset = info->offset; @@ -475,13 +479,13 @@ static int nvmem_cell_info_to_nvmem_cell_nodup(struct nvmem_device *nvmem, return 0; } -static int nvmem_cell_info_to_nvmem_cell(struct nvmem_device *nvmem, - const struct nvmem_cell_info *info, - struct nvmem_cell *cell) +static int nvmem_cell_info_to_nvmem_cell_entry(struct nvmem_device *nvmem, + const struct nvmem_cell_info *info, + struct nvmem_cell_entry *cell) { int err; - err = nvmem_cell_info_to_nvmem_cell_nodup(nvmem, info, cell); + err = nvmem_cell_info_to_nvmem_cell_entry_nodup(nvmem, info, cell); if (err) return err; @@ -505,7 +509,7 @@ static int nvmem_add_cells(struct nvmem_device *nvmem, const struct nvmem_cell_info *info, int ncells) { - struct nvmem_cell **cells; + struct nvmem_cell_entry **cells; int i, rval; cells = kcalloc(ncells, sizeof(*cells), GFP_KERNEL); @@ -519,13 +523,13 @@ static int nvmem_add_cells(struct nvmem_device *nvmem, goto err; } - rval = nvmem_cell_info_to_nvmem_cell(nvmem, &info[i], cells[i]); + rval = nvmem_cell_info_to_nvmem_cell_entry(nvmem, &info[i], cells[i]); if (rval) { kfree(cells[i]); goto err; } - nvmem_cell_add(cells[i]); + nvmem_cell_entry_add(cells[i]); } /* remove tmp array */ @@ -534,7 +538,7 @@ static int nvmem_add_cells(struct nvmem_device *nvmem, return 0; err: while (i--) - nvmem_cell_drop(cells[i]); + nvmem_cell_entry_drop(cells[i]); kfree(cells); @@ -571,7 +575,7 @@ static int nvmem_add_cells_from_table(struct nvmem_device *nvmem) { const struct nvmem_cell_info *info; struct nvmem_cell_table *table; - struct nvmem_cell *cell; + struct nvmem_cell_entry *cell; int rval = 0, i; mutex_lock(&nvmem_cell_mutex); @@ -586,15 +590,13 @@ static int nvmem_add_cells_from_table(struct nvmem_device *nvmem) goto out; } - rval = nvmem_cell_info_to_nvmem_cell(nvmem, - info, - cell); + rval = nvmem_cell_info_to_nvmem_cell_entry(nvmem, info, cell); if (rval) { kfree(cell); goto out; } - nvmem_cell_add(cell); + nvmem_cell_entry_add(cell); } } } @@ -604,10 +606,10 @@ out: return rval; } -static struct nvmem_cell * -nvmem_find_cell_by_name(struct nvmem_device *nvmem, const char *cell_id) +static struct nvmem_cell_entry * +nvmem_find_cell_entry_by_name(struct nvmem_device *nvmem, const char *cell_id) { - struct nvmem_cell *iter, *cell = NULL; + struct nvmem_cell_entry *iter, *cell = NULL; mutex_lock(&nvmem_mutex); list_for_each_entry(iter, &nvmem->cells, node) { @@ -678,7 +680,7 @@ static int nvmem_add_cells_from_of(struct nvmem_device *nvmem) { struct device_node *parent, *child; struct device *dev = &nvmem->dev; - struct nvmem_cell *cell; + struct nvmem_cell_entry *cell; const __be32 *addr; int len; @@ -727,7 +729,7 @@ static int nvmem_add_cells_from_of(struct nvmem_device *nvmem) } cell->np = of_node_get(child); - nvmem_cell_add(cell); + nvmem_cell_entry_add(cell); } return 0; @@ -1142,9 +1144,33 @@ struct nvmem_device *devm_nvmem_device_get(struct device *dev, const char *id) } EXPORT_SYMBOL_GPL(devm_nvmem_device_get); +static struct nvmem_cell *nvmem_create_cell(struct nvmem_cell_entry *entry, const char *id) +{ + struct nvmem_cell *cell; + const char *name = NULL; + + cell = kzalloc(sizeof(*cell), GFP_KERNEL); + if (!cell) + return ERR_PTR(-ENOMEM); + + if (id) { + name = kstrdup_const(id, GFP_KERNEL); + if (!name) { + kfree(cell); + return ERR_PTR(-ENOMEM); + } + } + + cell->id = name; + cell->entry = entry; + + return cell; +} + static struct nvmem_cell * nvmem_cell_get_from_lookup(struct device *dev, const char *con_id) { + struct nvmem_cell_entry *cell_entry; struct nvmem_cell *cell = ERR_PTR(-ENOENT); struct nvmem_cell_lookup *lookup; struct nvmem_device *nvmem; @@ -1169,11 +1195,15 @@ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id) break; } - cell = nvmem_find_cell_by_name(nvmem, - lookup->cell_name); - if (!cell) { + cell_entry = nvmem_find_cell_entry_by_name(nvmem, + lookup->cell_name); + if (!cell_entry) { __nvmem_device_put(nvmem); cell = ERR_PTR(-ENOENT); + } else { + cell = nvmem_create_cell(cell_entry, con_id); + if (IS_ERR(cell)) + __nvmem_device_put(nvmem); } break; } @@ -1184,10 +1214,10 @@ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id) } #if IS_ENABLED(CONFIG_OF) -static struct nvmem_cell * -nvmem_find_cell_by_node(struct nvmem_device *nvmem, struct device_node *np) +static struct nvmem_cell_entry * +nvmem_find_cell_entry_by_node(struct nvmem_device *nvmem, struct device_node *np) { - struct nvmem_cell *iter, *cell = NULL; + struct nvmem_cell_entry *iter, *cell = NULL; mutex_lock(&nvmem_mutex); list_for_each_entry(iter, &nvmem->cells, node) { @@ -1217,6 +1247,7 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id) { struct device_node *cell_np, *nvmem_np; struct nvmem_device *nvmem; + struct nvmem_cell_entry *cell_entry; struct nvmem_cell *cell; int index = 0; @@ -1237,12 +1268,16 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id) if (IS_ERR(nvmem)) return ERR_CAST(nvmem); - cell = nvmem_find_cell_by_node(nvmem, cell_np); - if (!cell) { + cell_entry = nvmem_find_cell_entry_by_node(nvmem, cell_np); + if (!cell_entry) { __nvmem_device_put(nvmem); return ERR_PTR(-ENOENT); } + cell = nvmem_create_cell(cell_entry, id); + if (IS_ERR(cell)) + __nvmem_device_put(nvmem); + return cell; } EXPORT_SYMBOL_GPL(of_nvmem_cell_get); @@ -1348,13 +1383,17 @@ EXPORT_SYMBOL(devm_nvmem_cell_put); */ void nvmem_cell_put(struct nvmem_cell *cell) { - struct nvmem_device *nvmem = cell->nvmem; + struct nvmem_device *nvmem = cell->entry->nvmem; + + if (cell->id) + kfree_const(cell->id); + kfree(cell); __nvmem_device_put(nvmem); } EXPORT_SYMBOL_GPL(nvmem_cell_put); -static void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, void *buf) +static void nvmem_shift_read_buffer_in_place(struct nvmem_cell_entry *cell, void *buf) { u8 *p, *b; int i, extra, bit_offset = cell->bit_offset; @@ -1387,8 +1426,8 @@ static void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, void *buf) } static int __nvmem_cell_read(struct nvmem_device *nvmem, - struct nvmem_cell *cell, - void *buf, size_t *len) + struct nvmem_cell_entry *cell, + void *buf, size_t *len, const char *id) { int rc; @@ -1419,18 +1458,18 @@ static int __nvmem_cell_read(struct nvmem_device *nvmem, */ void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len) { - struct nvmem_device *nvmem = cell->nvmem; + struct nvmem_device *nvmem = cell->entry->nvmem; u8 *buf; int rc; if (!nvmem) return ERR_PTR(-EINVAL); - buf = kzalloc(cell->bytes, GFP_KERNEL); + buf = kzalloc(cell->entry->bytes, GFP_KERNEL); if (!buf) return ERR_PTR(-ENOMEM); - rc = __nvmem_cell_read(nvmem, cell, buf, len); + rc = __nvmem_cell_read(nvmem, cell->entry, buf, len, cell->id); if (rc) { kfree(buf); return ERR_PTR(rc); @@ -1440,7 +1479,7 @@ void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len) } EXPORT_SYMBOL_GPL(nvmem_cell_read); -static void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell, +static void *nvmem_cell_prepare_write_buffer(struct nvmem_cell_entry *cell, u8 *_buf, int len) { struct nvmem_device *nvmem = cell->nvmem; @@ -1493,16 +1532,7 @@ err: return ERR_PTR(rc); } -/** - * nvmem_cell_write() - Write to a given nvmem cell - * - * @cell: nvmem cell to be written. - * @buf: Buffer to be written. - * @len: length of buffer to be written to nvmem cell. - * - * Return: length of bytes written or negative on failure. - */ -int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len) +static int __nvmem_cell_entry_write(struct nvmem_cell_entry *cell, void *buf, size_t len) { struct nvmem_device *nvmem = cell->nvmem; int rc; @@ -1528,6 +1558,21 @@ int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len) return len; } + +/** + * nvmem_cell_write() - Write to a given nvmem cell + * + * @cell: nvmem cell to be written. + * @buf: Buffer to be written. + * @len: length of buffer to be written to nvmem cell. + * + * Return: length of bytes written or negative on failure. + */ +int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len) +{ + return __nvmem_cell_entry_write(cell->entry, buf, len); +} + EXPORT_SYMBOL_GPL(nvmem_cell_write); static int nvmem_cell_read_common(struct device *dev, const char *cell_id, @@ -1630,7 +1675,7 @@ static const void *nvmem_cell_read_variable_common(struct device *dev, if (IS_ERR(cell)) return cell; - nbits = cell->nbits; + nbits = cell->entry->nbits; buf = nvmem_cell_read(cell, len); nvmem_cell_put(cell); if (IS_ERR(buf)) @@ -1726,18 +1771,18 @@ EXPORT_SYMBOL_GPL(nvmem_cell_read_variable_le_u64); ssize_t nvmem_device_cell_read(struct nvmem_device *nvmem, struct nvmem_cell_info *info, void *buf) { - struct nvmem_cell cell; + struct nvmem_cell_entry cell; int rc; ssize_t len; if (!nvmem) return -EINVAL; - rc = nvmem_cell_info_to_nvmem_cell_nodup(nvmem, info, &cell); + rc = nvmem_cell_info_to_nvmem_cell_entry_nodup(nvmem, info, &cell); if (rc) return rc; - rc = __nvmem_cell_read(nvmem, &cell, buf, &len); + rc = __nvmem_cell_read(nvmem, &cell, buf, &len, NULL); if (rc) return rc; @@ -1757,17 +1802,17 @@ EXPORT_SYMBOL_GPL(nvmem_device_cell_read); int nvmem_device_cell_write(struct nvmem_device *nvmem, struct nvmem_cell_info *info, void *buf) { - struct nvmem_cell cell; + struct nvmem_cell_entry cell; int rc; if (!nvmem) return -EINVAL; - rc = nvmem_cell_info_to_nvmem_cell_nodup(nvmem, info, &cell); + rc = nvmem_cell_info_to_nvmem_cell_entry_nodup(nvmem, info, &cell); if (rc) return rc; - return nvmem_cell_write(&cell, buf, cell.bytes); + return __nvmem_cell_entry_write(&cell, buf, cell.bytes); } EXPORT_SYMBOL_GPL(nvmem_device_cell_write); -- cgit v1.2.3-70-g09d2 From 5008062f1c3f5af3acf86164aa6fcc77b0c7bdce Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 13 Oct 2021 14:19:56 +0100 Subject: nvmem: core: add nvmem cell post processing callback Some NVMEM providers have certain nvmem cells encoded, which requires post processing before actually using it. For example mac-address is stored in either in ascii or delimited or reverse-order. Having a post-process callback hook to provider drivers would enable them to do this vendor specific post processing before nvmem consumers see it. Tested-by: Joakim Zhang Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20211013131957.30271-3-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 9 +++++++++ include/linux/nvmem-provider.h | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index f37d7838d2e3..d7013c9b0fae 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -38,6 +38,7 @@ struct nvmem_device { unsigned int nkeepout; nvmem_reg_read_t reg_read; nvmem_reg_write_t reg_write; + nvmem_cell_post_process_t cell_post_process; struct gpio_desc *wp_gpio; void *priv; }; @@ -796,6 +797,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) nvmem->type = config->type; nvmem->reg_read = config->reg_read; nvmem->reg_write = config->reg_write; + nvmem->cell_post_process = config->cell_post_process; nvmem->keepout = config->keepout; nvmem->nkeepout = config->nkeepout; if (config->of_node) @@ -1440,6 +1442,13 @@ static int __nvmem_cell_read(struct nvmem_device *nvmem, if (cell->bit_offset || cell->nbits) nvmem_shift_read_buffer_in_place(cell, buf); + if (nvmem->cell_post_process) { + rc = nvmem->cell_post_process(nvmem->priv, id, + cell->offset, buf, cell->bytes); + if (rc) + return rc; + } + if (len) *len = cell->bytes; diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h index 104505e9028f..98efb7b5660d 100644 --- a/include/linux/nvmem-provider.h +++ b/include/linux/nvmem-provider.h @@ -19,6 +19,9 @@ typedef int (*nvmem_reg_read_t)(void *priv, unsigned int offset, void *val, size_t bytes); typedef int (*nvmem_reg_write_t)(void *priv, unsigned int offset, void *val, size_t bytes); +/* used for vendor specific post processing of cell data */ +typedef int (*nvmem_cell_post_process_t)(void *priv, const char *id, unsigned int offset, + void *buf, size_t bytes); enum nvmem_type { NVMEM_TYPE_UNKNOWN = 0, @@ -62,6 +65,7 @@ struct nvmem_keepout { * @no_of_node: Device should not use the parent's of_node even if it's !NULL. * @reg_read: Callback to read data. * @reg_write: Callback to write data. + * @cell_post_process: Callback for vendor specific post processing of cell data * @size: Device size. * @word_size: Minimum read/write access granularity. * @stride: Minimum read/write access stride. @@ -92,6 +96,7 @@ struct nvmem_config { bool no_of_node; nvmem_reg_read_t reg_read; nvmem_reg_write_t reg_write; + nvmem_cell_post_process_t cell_post_process; int size; int word_size; int stride; -- cgit v1.2.3-70-g09d2 From d0221a780cbc99fec6c27a98dba2828dc5735c00 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 13 Oct 2021 14:19:57 +0100 Subject: nvmem: imx-ocotp: add support for post processing Add .cell_post_process callback for imx-ocotp to deal with MAC address, since MAC address need to be reversed byte for some i.MX SoCs. Tested-by: Joakim Zhang Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20211013131957.30271-4-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/imx-ocotp.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c index 08f41328cc71..14284e866f26 100644 --- a/drivers/nvmem/imx-ocotp.c +++ b/drivers/nvmem/imx-ocotp.c @@ -97,6 +97,7 @@ struct ocotp_params { unsigned int bank_address_words; void (*set_timing)(struct ocotp_priv *priv); struct ocotp_ctrl_reg ctrl; + bool reverse_mac_address; }; static int imx_ocotp_wait_for_busy(struct ocotp_priv *priv, u32 flags) @@ -221,6 +222,25 @@ read_end: return ret; } +static int imx_ocotp_cell_pp(void *context, const char *id, unsigned int offset, + void *data, size_t bytes) +{ + struct ocotp_priv *priv = context; + + /* Deal with some post processing of nvmem cell data */ + if (id && !strcmp(id, "mac-address")) { + if (priv->params->reverse_mac_address) { + u8 *buf = data; + int i; + + for (i = 0; i < bytes/2; i++) + swap(buf[i], buf[bytes - i - 1]); + } + } + + return 0; +} + static void imx_ocotp_set_imx6_timing(struct ocotp_priv *priv) { unsigned long clk_rate; @@ -468,6 +488,7 @@ static struct nvmem_config imx_ocotp_nvmem_config = { .stride = 1, .reg_read = imx_ocotp_read, .reg_write = imx_ocotp_write, + .cell_post_process = imx_ocotp_cell_pp, }; static const struct ocotp_params imx6q_params = { @@ -530,6 +551,7 @@ static const struct ocotp_params imx8mq_params = { .bank_address_words = 0, .set_timing = imx_ocotp_set_imx6_timing, .ctrl = IMX_OCOTP_BM_CTRL_DEFAULT, + .reverse_mac_address = true, }; static const struct ocotp_params imx8mm_params = { @@ -537,6 +559,7 @@ static const struct ocotp_params imx8mm_params = { .bank_address_words = 0, .set_timing = imx_ocotp_set_imx6_timing, .ctrl = IMX_OCOTP_BM_CTRL_DEFAULT, + .reverse_mac_address = true, }; static const struct ocotp_params imx8mn_params = { @@ -544,6 +567,7 @@ static const struct ocotp_params imx8mn_params = { .bank_address_words = 0, .set_timing = imx_ocotp_set_imx6_timing, .ctrl = IMX_OCOTP_BM_CTRL_DEFAULT, + .reverse_mac_address = true, }; static const struct ocotp_params imx8mp_params = { @@ -551,6 +575,7 @@ static const struct ocotp_params imx8mp_params = { .bank_address_words = 0, .set_timing = imx_ocotp_set_imx6_timing, .ctrl = IMX_OCOTP_BM_CTRL_8MP, + .reverse_mac_address = true, }; static const struct of_device_id imx_ocotp_dt_ids[] = { -- cgit v1.2.3-70-g09d2 From 62f79f3d0eb9f4c224bcc3c7f6fa758515a0a7fa Mon Sep 17 00:00:00 2001 From: Eddie James Date: Wed, 21 Jul 2021 14:02:29 -0500 Subject: fsi: occ: Force sequence numbering per OCC Set and increment the sequence number during the submit operation. This prevents sequence number conflicts between different users of the interface. A sequence number conflict may result in a user getting an OCC response meant for a different command. Since the sequence number is now modified, the checksum must be calculated and set before submitting the command. Signed-off-by: Eddie James Reviewed-by: Joel Stanley Link: https://lore.kernel.org/r/20210721190231.117185-2-eajames@linux.ibm.com Signed-off-by: Joel Stanley --- drivers/fsi/fsi-occ.c | 54 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/drivers/fsi/fsi-occ.c b/drivers/fsi/fsi-occ.c index b223f0ef337b..ecf738411fe2 100644 --- a/drivers/fsi/fsi-occ.c +++ b/drivers/fsi/fsi-occ.c @@ -50,6 +50,7 @@ struct occ { struct device *sbefifo; char name[32]; int idx; + u8 sequence_number; enum versions version; struct miscdevice mdev; struct mutex occ_lock; @@ -141,8 +142,7 @@ static ssize_t occ_write(struct file *file, const char __user *buf, { struct occ_client *client = file->private_data; size_t rlen, data_length; - u16 checksum = 0; - ssize_t rc, i; + ssize_t rc; u8 *cmd; if (!client) @@ -156,9 +156,6 @@ static ssize_t occ_write(struct file *file, const char __user *buf, /* Construct the command */ cmd = client->buffer; - /* Sequence number (we could increment and compare with response) */ - cmd[0] = 1; - /* * Copy the user command (assume user data follows the occ command * format) @@ -178,14 +175,7 @@ static ssize_t occ_write(struct file *file, const char __user *buf, goto done; } - /* Calculate checksum */ - for (i = 0; i < data_length + 4; ++i) - checksum += cmd[i]; - - cmd[data_length + 4] = checksum >> 8; - cmd[data_length + 5] = checksum & 0xFF; - - /* Submit command */ + /* Submit command; 4 bytes before the data and 2 bytes after */ rlen = PAGE_SIZE; rc = fsi_occ_submit(client->occ->dev, cmd, data_length + 6, cmd, &rlen); @@ -314,11 +304,13 @@ free: return rc; } -static int occ_putsram(struct occ *occ, const void *data, ssize_t len) +static int occ_putsram(struct occ *occ, const void *data, ssize_t len, + u8 seq_no, u16 checksum) { size_t cmd_len, buf_len, resp_len, resp_data_len; u32 data_len = ((len + 7) / 8) * 8; /* must be multiples of 8 B */ __be32 *buf; + u8 *byte_buf; int idx = 0, rc; cmd_len = (occ->version == occ_p10) ? 6 : 5; @@ -358,6 +350,15 @@ static int occ_putsram(struct occ *occ, const void *data, ssize_t len) buf[4 + idx] = cpu_to_be32(data_len); memcpy(&buf[5 + idx], data, len); + byte_buf = (u8 *)&buf[5 + idx]; + /* + * Overwrite the first byte with our sequence number and the last two + * bytes with the checksum. + */ + byte_buf[0] = seq_no; + byte_buf[len - 2] = checksum >> 8; + byte_buf[len - 1] = checksum & 0xff; + rc = sbefifo_submit(occ->sbefifo, buf, cmd_len, buf, &resp_len); if (rc) goto free; @@ -467,9 +468,12 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len, struct occ *occ = dev_get_drvdata(dev); struct occ_response *resp = response; u8 seq_no; + u16 checksum = 0; u16 resp_data_length; + const u8 *byte_request = (const u8 *)request; unsigned long start; int rc; + size_t i; if (!occ) return -ENODEV; @@ -479,11 +483,26 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len, return -EINVAL; } + /* Checksum the request, ignoring first byte (sequence number). */ + for (i = 1; i < req_len - 2; ++i) + checksum += byte_request[i]; + mutex_lock(&occ->occ_lock); - /* Extract the seq_no from the command (first byte) */ - seq_no = *(const u8 *)request; - rc = occ_putsram(occ, request, req_len); + /* + * Get a sequence number and update the counter. Avoid a sequence + * number of 0 which would pass the response check below even if the + * OCC response is uninitialized. Any sequence number the user is + * trying to send is overwritten since this function is the only common + * interface to the OCC and therefore the only place we can guarantee + * unique sequence numbers. + */ + seq_no = occ->sequence_number++; + if (!occ->sequence_number) + occ->sequence_number = 1; + checksum += seq_no; + + rc = occ_putsram(occ, request, req_len, seq_no, checksum); if (rc) goto done; @@ -574,6 +593,7 @@ static int occ_probe(struct platform_device *pdev) occ->version = (uintptr_t)of_device_get_match_data(dev); occ->dev = dev; occ->sbefifo = dev->parent; + occ->sequence_number = 1; mutex_init(&occ->occ_lock); if (dev->of_node) { -- cgit v1.2.3-70-g09d2 From 908dbf0242e21dd95c69a1b0935814cd1abfc134 Mon Sep 17 00:00:00 2001 From: Eddie James Date: Wed, 21 Jul 2021 14:02:30 -0500 Subject: hwmon: (occ) Remove sequence numbering and checksum calculation Checksumming of the request and sequence numbering is now done in the OCC interface driver in order to keep unique sequence numbers. So remove those in the hwmon driver. Also, add the command length to the send_cmd function pointer, since the checksum must be placed in the last two bytes of the command. The submit interface must receive the exact size of the command - previously it could be rounded to the nearest 8 bytes with no consequence. Signed-off-by: Eddie James Acked-by: Guenter Roeck Link: https://lore.kernel.org/r/20210721190231.117185-3-eajames@linux.ibm.com Signed-off-by: Joel Stanley --- drivers/hwmon/occ/common.c | 30 ++++++++++++------------------ drivers/hwmon/occ/common.h | 3 +-- drivers/hwmon/occ/p8_i2c.c | 15 +++++++++------ drivers/hwmon/occ/p9_sbe.c | 4 ++-- 4 files changed, 24 insertions(+), 28 deletions(-) diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c index 0d68a78be980..fc298268c89e 100644 --- a/drivers/hwmon/occ/common.c +++ b/drivers/hwmon/occ/common.c @@ -132,22 +132,20 @@ struct extended_sensor { static int occ_poll(struct occ *occ) { int rc; - u16 checksum = occ->poll_cmd_data + occ->seq_no + 1; - u8 cmd[8]; + u8 cmd[7]; struct occ_poll_response_header *header; /* big endian */ - cmd[0] = occ->seq_no++; /* sequence number */ + cmd[0] = 0; /* sequence number */ cmd[1] = 0; /* cmd type */ cmd[2] = 0; /* data length msb */ cmd[3] = 1; /* data length lsb */ cmd[4] = occ->poll_cmd_data; /* data */ - cmd[5] = checksum >> 8; /* checksum msb */ - cmd[6] = checksum & 0xFF; /* checksum lsb */ - cmd[7] = 0; + cmd[5] = 0; /* checksum msb */ + cmd[6] = 0; /* checksum lsb */ /* mutex should already be locked if necessary */ - rc = occ->send_cmd(occ, cmd); + rc = occ->send_cmd(occ, cmd, sizeof(cmd)); if (rc) { occ->last_error = rc; if (occ->error_count++ > OCC_ERROR_COUNT_THRESHOLD) @@ -184,25 +182,23 @@ static int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap) { int rc; u8 cmd[8]; - u16 checksum = 0x24; __be16 user_power_cap_be = cpu_to_be16(user_power_cap); - cmd[0] = 0; - cmd[1] = 0x22; - cmd[2] = 0; - cmd[3] = 2; + cmd[0] = 0; /* sequence number */ + cmd[1] = 0x22; /* cmd type */ + cmd[2] = 0; /* data length msb */ + cmd[3] = 2; /* data length lsb */ memcpy(&cmd[4], &user_power_cap_be, 2); - checksum += cmd[4] + cmd[5]; - cmd[6] = checksum >> 8; - cmd[7] = checksum & 0xFF; + cmd[6] = 0; /* checksum msb */ + cmd[7] = 0; /* checksum lsb */ rc = mutex_lock_interruptible(&occ->lock); if (rc) return rc; - rc = occ->send_cmd(occ, cmd); + rc = occ->send_cmd(occ, cmd, sizeof(cmd)); mutex_unlock(&occ->lock); @@ -1151,8 +1147,6 @@ int occ_setup(struct occ *occ, const char *name) { int rc; - /* start with 1 to avoid false match with zero-initialized SRAM buffer */ - occ->seq_no = 1; mutex_init(&occ->lock); occ->groups[0] = &occ->group; diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h index e6df719770e8..5020117be740 100644 --- a/drivers/hwmon/occ/common.h +++ b/drivers/hwmon/occ/common.h @@ -95,9 +95,8 @@ struct occ { struct occ_sensors sensors; int powr_sample_time_us; /* average power sample time */ - u8 seq_no; u8 poll_cmd_data; /* to perform OCC poll command */ - int (*send_cmd)(struct occ *occ, u8 *cmd); + int (*send_cmd)(struct occ *occ, u8 *cmd, size_t len); unsigned long next_update; struct mutex lock; /* lock OCC access */ diff --git a/drivers/hwmon/occ/p8_i2c.c b/drivers/hwmon/occ/p8_i2c.c index 0cf8588be35a..9e61e1fb5142 100644 --- a/drivers/hwmon/occ/p8_i2c.c +++ b/drivers/hwmon/occ/p8_i2c.c @@ -97,18 +97,21 @@ static int p8_i2c_occ_putscom_u32(struct i2c_client *client, u32 address, } static int p8_i2c_occ_putscom_be(struct i2c_client *client, u32 address, - u8 *data) + u8 *data, size_t len) { - __be32 data0, data1; + __be32 data0 = 0, data1 = 0; - memcpy(&data0, data, 4); - memcpy(&data1, data + 4, 4); + memcpy(&data0, data, min_t(size_t, len, 4)); + if (len > 4) { + len -= 4; + memcpy(&data1, data + 4, min_t(size_t, len, 4)); + } return p8_i2c_occ_putscom_u32(client, address, be32_to_cpu(data0), be32_to_cpu(data1)); } -static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd) +static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd, size_t len) { int i, rc; unsigned long start; @@ -127,7 +130,7 @@ static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd) return rc; /* write command (expected to already be BE), we need bus-endian... */ - rc = p8_i2c_occ_putscom_be(client, OCB_DATA3, cmd); + rc = p8_i2c_occ_putscom_be(client, OCB_DATA3, cmd, len); if (rc) return rc; diff --git a/drivers/hwmon/occ/p9_sbe.c b/drivers/hwmon/occ/p9_sbe.c index f6387cc0b754..9709f2b9c052 100644 --- a/drivers/hwmon/occ/p9_sbe.c +++ b/drivers/hwmon/occ/p9_sbe.c @@ -16,14 +16,14 @@ struct p9_sbe_occ { #define to_p9_sbe_occ(x) container_of((x), struct p9_sbe_occ, occ) -static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd) +static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd, size_t len) { struct occ_response *resp = &occ->resp; struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ); size_t resp_len = sizeof(*resp); int rc; - rc = fsi_occ_submit(ctx->sbe, cmd, 8, resp, &resp_len); + rc = fsi_occ_submit(ctx->sbe, cmd, len, resp, &resp_len); if (rc < 0) return rc; -- cgit v1.2.3-70-g09d2 From 05593a3fd1037b5fee85d3c8c28112f19e7baa06 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Fri, 27 Aug 2021 12:47:45 +0900 Subject: counter: stm32-lptimer-cnt: Provide defines for clock polarities The STM32 low-power timer permits configuration of the clock polarity via the LPTIMX_CFGR register CKPOL bits. This patch provides preprocessor defines for the supported clock polarities. Cc: Fabrice Gasnier Signed-off-by: William Breathitt Gray Reviewed-by: Fabrice Gasnier Link: https://lore.kernel.org/r/a111c8905c467805ca530728f88189b59430f27e.1630031207.git.vilhelm.gray@gmail.com Signed-off-by: Jonathan Cameron --- drivers/counter/stm32-lptimer-cnt.c | 6 +++--- include/linux/mfd/stm32-lptimer.h | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/counter/stm32-lptimer-cnt.c b/drivers/counter/stm32-lptimer-cnt.c index 13656957c45f..7367f46c6f91 100644 --- a/drivers/counter/stm32-lptimer-cnt.c +++ b/drivers/counter/stm32-lptimer-cnt.c @@ -140,9 +140,9 @@ static const enum counter_function stm32_lptim_cnt_functions[] = { }; enum stm32_lptim_synapse_action { - STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE, - STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE, - STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES, + STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE = STM32_LPTIM_CKPOL_RISING_EDGE, + STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE = STM32_LPTIM_CKPOL_FALLING_EDGE, + STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES = STM32_LPTIM_CKPOL_BOTH_EDGES, STM32_LPTIM_SYNAPSE_ACTION_NONE, }; diff --git a/include/linux/mfd/stm32-lptimer.h b/include/linux/mfd/stm32-lptimer.h index 90b20550c1c8..06d3f11dc3c9 100644 --- a/include/linux/mfd/stm32-lptimer.h +++ b/include/linux/mfd/stm32-lptimer.h @@ -45,6 +45,11 @@ #define STM32_LPTIM_PRESC GENMASK(11, 9) #define STM32_LPTIM_CKPOL GENMASK(2, 1) +/* STM32_LPTIM_CKPOL */ +#define STM32_LPTIM_CKPOL_RISING_EDGE 0 +#define STM32_LPTIM_CKPOL_FALLING_EDGE 1 +#define STM32_LPTIM_CKPOL_BOTH_EDGES 2 + /* STM32_LPTIM_ARR */ #define STM32_LPTIM_MAX_ARR 0xFFFF -- cgit v1.2.3-70-g09d2 From ea434ff82649111de4fcabd76187270f8abdb63a Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Fri, 27 Aug 2021 12:47:46 +0900 Subject: counter: stm32-timer-cnt: Provide defines for slave mode selection The STM32 timer permits configuration of the counter encoder mode via the slave mode control register (SMCR) slave mode selection (SMS) bits. This patch provides preprocessor defines for the supported encoder modes. Cc: Fabrice Gasnier Signed-off-by: William Breathitt Gray Reviewed-by: Fabrice Gasnier Link: https://lore.kernel.org/r/ad3d9cd7af580d586316d368f74964cbc394f981.1630031207.git.vilhelm.gray@gmail.com Signed-off-by: Jonathan Cameron --- drivers/counter/stm32-timer-cnt.c | 16 ++++++++-------- include/linux/mfd/stm32-timers.h | 4 ++++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c index 3fb0debd7425..1fbc46f4ee66 100644 --- a/drivers/counter/stm32-timer-cnt.c +++ b/drivers/counter/stm32-timer-cnt.c @@ -93,16 +93,16 @@ static int stm32_count_function_get(struct counter_device *counter, regmap_read(priv->regmap, TIM_SMCR, &smcr); switch (smcr & TIM_SMCR_SMS) { - case 0: + case TIM_SMCR_SMS_SLAVE_MODE_DISABLED: *function = STM32_COUNT_SLAVE_MODE_DISABLED; return 0; - case 1: + case TIM_SMCR_SMS_ENCODER_MODE_1: *function = STM32_COUNT_ENCODER_MODE_1; return 0; - case 2: + case TIM_SMCR_SMS_ENCODER_MODE_2: *function = STM32_COUNT_ENCODER_MODE_2; return 0; - case 3: + case TIM_SMCR_SMS_ENCODER_MODE_3: *function = STM32_COUNT_ENCODER_MODE_3; return 0; default: @@ -119,16 +119,16 @@ static int stm32_count_function_set(struct counter_device *counter, switch (function) { case STM32_COUNT_SLAVE_MODE_DISABLED: - sms = 0; + sms = TIM_SMCR_SMS_SLAVE_MODE_DISABLED; break; case STM32_COUNT_ENCODER_MODE_1: - sms = 1; + sms = TIM_SMCR_SMS_ENCODER_MODE_1; break; case STM32_COUNT_ENCODER_MODE_2: - sms = 2; + sms = TIM_SMCR_SMS_ENCODER_MODE_2; break; case STM32_COUNT_ENCODER_MODE_3: - sms = 3; + sms = TIM_SMCR_SMS_ENCODER_MODE_3; break; default: return -EINVAL; diff --git a/include/linux/mfd/stm32-timers.h b/include/linux/mfd/stm32-timers.h index f8db83aedb2b..5f5c43fd69dd 100644 --- a/include/linux/mfd/stm32-timers.h +++ b/include/linux/mfd/stm32-timers.h @@ -82,6 +82,10 @@ #define MAX_TIM_ICPSC 0x3 #define TIM_CR2_MMS_SHIFT 4 #define TIM_CR2_MMS2_SHIFT 20 +#define TIM_SMCR_SMS_SLAVE_MODE_DISABLED 0 /* counts on internal clock when CEN=1 */ +#define TIM_SMCR_SMS_ENCODER_MODE_1 1 /* counts TI1FP1 edges, depending on TI2FP2 level */ +#define TIM_SMCR_SMS_ENCODER_MODE_2 2 /* counts TI2FP2 edges, depending on TI1FP1 level */ +#define TIM_SMCR_SMS_ENCODER_MODE_3 3 /* counts on both TI1FP1 and TI2FP2 edges */ #define TIM_SMCR_TS_SHIFT 4 #define TIM_BDTR_BKF_MASK 0xF #define TIM_BDTR_BKF_SHIFT(x) (16 + (x) * 4) -- cgit v1.2.3-70-g09d2 From aaec1a0f76ec25f46bbb17b81487c4b0e1c318c5 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Fri, 27 Aug 2021 12:47:47 +0900 Subject: counter: Internalize sysfs interface code This is a reimplementation of the Generic Counter driver interface. There are no modifications to the Counter subsystem userspace interface, so existing userspace applications should continue to run seamlessly. The purpose of this patch is to internalize the sysfs interface code among the various counter drivers into a shared module. Counter drivers pass and take data natively (i.e. u8, u64, etc.) and the shared counter module handles the translation between the sysfs interface and the device drivers. This guarantees a standard userspace interface for all counter drivers, and helps generalize the Generic Counter driver ABI in order to support the Generic Counter chrdev interface (introduced in a subsequent patch) without significant changes to the existing counter drivers. Note, Counter device registration is the same as before: drivers populate a struct counter_device with components and callbacks, then pass the structure to the devm_counter_register function. However, what's different now is how the Counter subsystem code handles this registration internally. Whereas before callbacks would interact directly with sysfs data, this interaction is now abstracted and instead callbacks interact with native C data types. The counter_comp structure forms the basis for Counter extensions. The counter-sysfs.c file contains the code to parse through the counter_device structure and register the requested components and extensions. Attributes are created and populated based on type, with respective translation functions to handle the mapping between sysfs and the counter driver callbacks. The translation performed for each attribute is straightforward: the attribute type and data is parsed from the counter_attribute structure, the respective counter driver read/write callback is called, and sysfs I/O is handled before or after the driver read/write function is called. Cc: Jarkko Nikula Cc: Patrick Havelange Cc: Kamel Bouhara Cc: Maxime Coquelin Cc: Alexandre Torgue Cc: Dan Carpenter Acked-by: Syed Nayyar Waris Reviewed-by: David Lechner Tested-by: David Lechner Signed-off-by: William Breathitt Gray Reviewed-by: Fabrice Gasnier # for stm32 Link: https://lore.kernel.org/r/c68b4a1ffb195c1a2f65e8dd5ad7b7c14e79c6ef.1630031207.git.vilhelm.gray@gmail.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 1 - drivers/counter/104-quad-8.c | 449 ++++------ drivers/counter/Makefile | 1 + drivers/counter/counter-core.c | 142 +++ drivers/counter/counter-sysfs.c | 849 ++++++++++++++++++ drivers/counter/counter-sysfs.h | 13 + drivers/counter/counter.c | 1496 ------------------------------- drivers/counter/ftm-quaddec.c | 60 +- drivers/counter/intel-qep.c | 146 ++- drivers/counter/interrupt-cnt.c | 62 +- drivers/counter/microchip-tcb-capture.c | 91 +- drivers/counter/stm32-lptimer-cnt.c | 212 +++-- drivers/counter/stm32-timer-cnt.c | 179 ++-- drivers/counter/ti-eqep.c | 180 ++-- include/linux/counter.h | 658 +++++++------- include/linux/counter_enum.h | 45 - 16 files changed, 1950 insertions(+), 2634 deletions(-) create mode 100644 drivers/counter/counter-core.c create mode 100644 drivers/counter/counter-sysfs.c create mode 100644 drivers/counter/counter-sysfs.h delete mode 100644 drivers/counter/counter.c delete mode 100644 include/linux/counter_enum.h diff --git a/MAINTAINERS b/MAINTAINERS index eeb4c70b3d5b..b98a34fcec4b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4804,7 +4804,6 @@ F: Documentation/ABI/testing/sysfs-bus-counter F: Documentation/driver-api/generic-counter.rst F: drivers/counter/ F: include/linux/counter.h -F: include/linux/counter_enum.h CP2615 I2C DRIVER M: Bence Csókás diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c index 0caa60537b14..c587f295d720 100644 --- a/drivers/counter/104-quad-8.c +++ b/drivers/counter/104-quad-8.c @@ -117,7 +117,7 @@ static int quad8_signal_read(struct counter_device *counter, } static int quad8_count_read(struct counter_device *counter, - struct counter_count *count, unsigned long *val) + struct counter_count *count, u64 *val) { struct quad8 *const priv = counter->priv; const int base_offset = priv->base + 2 * count->id; @@ -148,7 +148,7 @@ static int quad8_count_read(struct counter_device *counter, } static int quad8_count_write(struct counter_device *counter, - struct counter_count *count, unsigned long val) + struct counter_count *count, u64 val) { struct quad8 *const priv = counter->priv; const int base_offset = priv->base + 2 * count->id; @@ -188,22 +188,16 @@ static int quad8_count_write(struct counter_device *counter, return 0; } -enum quad8_count_function { - QUAD8_COUNT_FUNCTION_PULSE_DIRECTION = 0, - QUAD8_COUNT_FUNCTION_QUADRATURE_X1, - QUAD8_COUNT_FUNCTION_QUADRATURE_X2, - QUAD8_COUNT_FUNCTION_QUADRATURE_X4 -}; - static const enum counter_function quad8_count_functions_list[] = { - [QUAD8_COUNT_FUNCTION_PULSE_DIRECTION] = COUNTER_FUNCTION_PULSE_DIRECTION, - [QUAD8_COUNT_FUNCTION_QUADRATURE_X1] = COUNTER_FUNCTION_QUADRATURE_X1_A, - [QUAD8_COUNT_FUNCTION_QUADRATURE_X2] = COUNTER_FUNCTION_QUADRATURE_X2_A, - [QUAD8_COUNT_FUNCTION_QUADRATURE_X4] = COUNTER_FUNCTION_QUADRATURE_X4 + COUNTER_FUNCTION_PULSE_DIRECTION, + COUNTER_FUNCTION_QUADRATURE_X1_A, + COUNTER_FUNCTION_QUADRATURE_X2_A, + COUNTER_FUNCTION_QUADRATURE_X4, }; -static int quad8_function_get(struct counter_device *counter, - struct counter_count *count, size_t *function) +static int quad8_function_read(struct counter_device *counter, + struct counter_count *count, + enum counter_function *function) { struct quad8 *const priv = counter->priv; const int id = count->id; @@ -213,25 +207,26 @@ static int quad8_function_get(struct counter_device *counter, if (priv->quadrature_mode[id]) switch (priv->quadrature_scale[id]) { case 0: - *function = QUAD8_COUNT_FUNCTION_QUADRATURE_X1; + *function = COUNTER_FUNCTION_QUADRATURE_X1_A; break; case 1: - *function = QUAD8_COUNT_FUNCTION_QUADRATURE_X2; + *function = COUNTER_FUNCTION_QUADRATURE_X2_A; break; case 2: - *function = QUAD8_COUNT_FUNCTION_QUADRATURE_X4; + *function = COUNTER_FUNCTION_QUADRATURE_X4; break; } else - *function = QUAD8_COUNT_FUNCTION_PULSE_DIRECTION; + *function = COUNTER_FUNCTION_PULSE_DIRECTION; mutex_unlock(&priv->lock); return 0; } -static int quad8_function_set(struct counter_device *counter, - struct counter_count *count, size_t function) +static int quad8_function_write(struct counter_device *counter, + struct counter_count *count, + enum counter_function function) { struct quad8 *const priv = counter->priv; const int id = count->id; @@ -247,7 +242,7 @@ static int quad8_function_set(struct counter_device *counter, mode_cfg = priv->count_mode[id] << 1; idr_cfg = priv->index_polarity[id] << 1; - if (function == QUAD8_COUNT_FUNCTION_PULSE_DIRECTION) { + if (function == COUNTER_FUNCTION_PULSE_DIRECTION) { *quadrature_mode = 0; /* Quadrature scaling only available in quadrature mode */ @@ -263,15 +258,15 @@ static int quad8_function_set(struct counter_device *counter, *quadrature_mode = 1; switch (function) { - case QUAD8_COUNT_FUNCTION_QUADRATURE_X1: + case COUNTER_FUNCTION_QUADRATURE_X1_A: *scale = 0; mode_cfg |= QUAD8_CMR_QUADRATURE_X1; break; - case QUAD8_COUNT_FUNCTION_QUADRATURE_X2: + case COUNTER_FUNCTION_QUADRATURE_X2_A: *scale = 1; mode_cfg |= QUAD8_CMR_QUADRATURE_X2; break; - case QUAD8_COUNT_FUNCTION_QUADRATURE_X4: + case COUNTER_FUNCTION_QUADRATURE_X4: *scale = 2; mode_cfg |= QUAD8_CMR_QUADRATURE_X4; break; @@ -290,8 +285,9 @@ static int quad8_function_set(struct counter_device *counter, return 0; } -static void quad8_direction_get(struct counter_device *counter, - struct counter_count *count, enum counter_count_direction *direction) +static int quad8_direction_read(struct counter_device *counter, + struct counter_count *count, + enum counter_count_direction *direction) { const struct quad8 *const priv = counter->priv; unsigned int ud_flag; @@ -302,76 +298,74 @@ static void quad8_direction_get(struct counter_device *counter, *direction = (ud_flag) ? COUNTER_COUNT_DIRECTION_FORWARD : COUNTER_COUNT_DIRECTION_BACKWARD; -} -enum quad8_synapse_action { - QUAD8_SYNAPSE_ACTION_NONE = 0, - QUAD8_SYNAPSE_ACTION_RISING_EDGE, - QUAD8_SYNAPSE_ACTION_FALLING_EDGE, - QUAD8_SYNAPSE_ACTION_BOTH_EDGES -}; + return 0; +} static const enum counter_synapse_action quad8_index_actions_list[] = { - [QUAD8_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE, - [QUAD8_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE + COUNTER_SYNAPSE_ACTION_NONE, + COUNTER_SYNAPSE_ACTION_RISING_EDGE, }; static const enum counter_synapse_action quad8_synapse_actions_list[] = { - [QUAD8_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE, - [QUAD8_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE, - [QUAD8_SYNAPSE_ACTION_FALLING_EDGE] = COUNTER_SYNAPSE_ACTION_FALLING_EDGE, - [QUAD8_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES + COUNTER_SYNAPSE_ACTION_NONE, + COUNTER_SYNAPSE_ACTION_RISING_EDGE, + COUNTER_SYNAPSE_ACTION_FALLING_EDGE, + COUNTER_SYNAPSE_ACTION_BOTH_EDGES, }; -static int quad8_action_get(struct counter_device *counter, - struct counter_count *count, struct counter_synapse *synapse, - size_t *action) +static int quad8_action_read(struct counter_device *counter, + struct counter_count *count, + struct counter_synapse *synapse, + enum counter_synapse_action *action) { struct quad8 *const priv = counter->priv; int err; - size_t function = 0; + enum counter_function function; const size_t signal_a_id = count->synapses[0].signal->id; enum counter_count_direction direction; /* Handle Index signals */ if (synapse->signal->id >= 16) { if (priv->preset_enable[count->id]) - *action = QUAD8_SYNAPSE_ACTION_RISING_EDGE; + *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE; else - *action = QUAD8_SYNAPSE_ACTION_NONE; + *action = COUNTER_SYNAPSE_ACTION_NONE; return 0; } - err = quad8_function_get(counter, count, &function); + err = quad8_function_read(counter, count, &function); if (err) return err; /* Default action mode */ - *action = QUAD8_SYNAPSE_ACTION_NONE; + *action = COUNTER_SYNAPSE_ACTION_NONE; /* Determine action mode based on current count function mode */ switch (function) { - case QUAD8_COUNT_FUNCTION_PULSE_DIRECTION: + case COUNTER_FUNCTION_PULSE_DIRECTION: if (synapse->signal->id == signal_a_id) - *action = QUAD8_SYNAPSE_ACTION_RISING_EDGE; + *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE; return 0; - case QUAD8_COUNT_FUNCTION_QUADRATURE_X1: + case COUNTER_FUNCTION_QUADRATURE_X1_A: if (synapse->signal->id == signal_a_id) { - quad8_direction_get(counter, count, &direction); + err = quad8_direction_read(counter, count, &direction); + if (err) + return err; if (direction == COUNTER_COUNT_DIRECTION_FORWARD) - *action = QUAD8_SYNAPSE_ACTION_RISING_EDGE; + *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE; else - *action = QUAD8_SYNAPSE_ACTION_FALLING_EDGE; + *action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE; } return 0; - case QUAD8_COUNT_FUNCTION_QUADRATURE_X2: + case COUNTER_FUNCTION_QUADRATURE_X2_A: if (synapse->signal->id == signal_a_id) - *action = QUAD8_SYNAPSE_ACTION_BOTH_EDGES; + *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; return 0; - case QUAD8_COUNT_FUNCTION_QUADRATURE_X4: - *action = QUAD8_SYNAPSE_ACTION_BOTH_EDGES; + case COUNTER_FUNCTION_QUADRATURE_X4: + *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; return 0; default: /* should never reach this path */ @@ -383,9 +377,9 @@ static const struct counter_ops quad8_ops = { .signal_read = quad8_signal_read, .count_read = quad8_count_read, .count_write = quad8_count_write, - .function_get = quad8_function_get, - .function_set = quad8_function_set, - .action_get = quad8_action_get + .function_read = quad8_function_read, + .function_write = quad8_function_write, + .action_read = quad8_action_read }; static const char *const quad8_index_polarity_modes[] = { @@ -394,7 +388,8 @@ static const char *const quad8_index_polarity_modes[] = { }; static int quad8_index_polarity_get(struct counter_device *counter, - struct counter_signal *signal, size_t *index_polarity) + struct counter_signal *signal, + u32 *index_polarity) { const struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id - 16; @@ -405,7 +400,8 @@ static int quad8_index_polarity_get(struct counter_device *counter, } static int quad8_index_polarity_set(struct counter_device *counter, - struct counter_signal *signal, size_t index_polarity) + struct counter_signal *signal, + u32 index_polarity) { struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id - 16; @@ -426,20 +422,14 @@ static int quad8_index_polarity_set(struct counter_device *counter, return 0; } -static struct counter_signal_enum_ext quad8_index_pol_enum = { - .items = quad8_index_polarity_modes, - .num_items = ARRAY_SIZE(quad8_index_polarity_modes), - .get = quad8_index_polarity_get, - .set = quad8_index_polarity_set -}; - static const char *const quad8_synchronous_modes[] = { "non-synchronous", "synchronous" }; static int quad8_synchronous_mode_get(struct counter_device *counter, - struct counter_signal *signal, size_t *synchronous_mode) + struct counter_signal *signal, + u32 *synchronous_mode) { const struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id - 16; @@ -450,7 +440,8 @@ static int quad8_synchronous_mode_get(struct counter_device *counter, } static int quad8_synchronous_mode_set(struct counter_device *counter, - struct counter_signal *signal, size_t synchronous_mode) + struct counter_signal *signal, + u32 synchronous_mode) { struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id - 16; @@ -477,22 +468,18 @@ static int quad8_synchronous_mode_set(struct counter_device *counter, return 0; } -static struct counter_signal_enum_ext quad8_syn_mode_enum = { - .items = quad8_synchronous_modes, - .num_items = ARRAY_SIZE(quad8_synchronous_modes), - .get = quad8_synchronous_mode_get, - .set = quad8_synchronous_mode_set -}; - -static ssize_t quad8_count_floor_read(struct counter_device *counter, - struct counter_count *count, void *private, char *buf) +static int quad8_count_floor_read(struct counter_device *counter, + struct counter_count *count, u64 *floor) { /* Only a floor of 0 is supported */ - return sprintf(buf, "0\n"); + *floor = 0; + + return 0; } -static int quad8_count_mode_get(struct counter_device *counter, - struct counter_count *count, size_t *cnt_mode) +static int quad8_count_mode_read(struct counter_device *counter, + struct counter_count *count, + enum counter_count_mode *cnt_mode) { const struct quad8 *const priv = counter->priv; @@ -515,26 +502,28 @@ static int quad8_count_mode_get(struct counter_device *counter, return 0; } -static int quad8_count_mode_set(struct counter_device *counter, - struct counter_count *count, size_t cnt_mode) +static int quad8_count_mode_write(struct counter_device *counter, + struct counter_count *count, + enum counter_count_mode cnt_mode) { struct quad8 *const priv = counter->priv; + unsigned int count_mode; unsigned int mode_cfg; const int base_offset = priv->base + 2 * count->id + 1; /* Map Generic Counter count mode to 104-QUAD-8 count mode */ switch (cnt_mode) { case COUNTER_COUNT_MODE_NORMAL: - cnt_mode = 0; + count_mode = 0; break; case COUNTER_COUNT_MODE_RANGE_LIMIT: - cnt_mode = 1; + count_mode = 1; break; case COUNTER_COUNT_MODE_NON_RECYCLE: - cnt_mode = 2; + count_mode = 2; break; case COUNTER_COUNT_MODE_MODULO_N: - cnt_mode = 3; + count_mode = 3; break; default: /* should never reach this path */ @@ -543,10 +532,10 @@ static int quad8_count_mode_set(struct counter_device *counter, mutex_lock(&priv->lock); - priv->count_mode[count->id] = cnt_mode; + priv->count_mode[count->id] = count_mode; /* Set count mode configuration value */ - mode_cfg = cnt_mode << 1; + mode_cfg = count_mode << 1; /* Add quadrature mode configuration */ if (priv->quadrature_mode[count->id]) @@ -560,56 +549,35 @@ static int quad8_count_mode_set(struct counter_device *counter, return 0; } -static struct counter_count_enum_ext quad8_cnt_mode_enum = { - .items = counter_count_mode_str, - .num_items = ARRAY_SIZE(counter_count_mode_str), - .get = quad8_count_mode_get, - .set = quad8_count_mode_set -}; - -static ssize_t quad8_count_direction_read(struct counter_device *counter, - struct counter_count *count, void *priv, char *buf) -{ - enum counter_count_direction dir; - - quad8_direction_get(counter, count, &dir); - - return sprintf(buf, "%s\n", counter_count_direction_str[dir]); -} - -static ssize_t quad8_count_enable_read(struct counter_device *counter, - struct counter_count *count, void *private, char *buf) +static int quad8_count_enable_read(struct counter_device *counter, + struct counter_count *count, u8 *enable) { const struct quad8 *const priv = counter->priv; - return sprintf(buf, "%u\n", priv->ab_enable[count->id]); + *enable = priv->ab_enable[count->id]; + + return 0; } -static ssize_t quad8_count_enable_write(struct counter_device *counter, - struct counter_count *count, void *private, const char *buf, size_t len) +static int quad8_count_enable_write(struct counter_device *counter, + struct counter_count *count, u8 enable) { struct quad8 *const priv = counter->priv; const int base_offset = priv->base + 2 * count->id; - int err; - bool ab_enable; unsigned int ior_cfg; - err = kstrtobool(buf, &ab_enable); - if (err) - return err; - mutex_lock(&priv->lock); - priv->ab_enable[count->id] = ab_enable; + priv->ab_enable[count->id] = enable; - ior_cfg = ab_enable | priv->preset_enable[count->id] << 1; + ior_cfg = enable | priv->preset_enable[count->id] << 1; /* Load I/O control configuration */ outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1); mutex_unlock(&priv->lock); - return len; + return 0; } static const char *const quad8_noise_error_states[] = { @@ -618,7 +586,7 @@ static const char *const quad8_noise_error_states[] = { }; static int quad8_error_noise_get(struct counter_device *counter, - struct counter_count *count, size_t *noise_error) + struct counter_count *count, u32 *noise_error) { const struct quad8 *const priv = counter->priv; const int base_offset = priv->base + 2 * count->id + 1; @@ -628,18 +596,14 @@ static int quad8_error_noise_get(struct counter_device *counter, return 0; } -static struct counter_count_enum_ext quad8_error_noise_enum = { - .items = quad8_noise_error_states, - .num_items = ARRAY_SIZE(quad8_noise_error_states), - .get = quad8_error_noise_get -}; - -static ssize_t quad8_count_preset_read(struct counter_device *counter, - struct counter_count *count, void *private, char *buf) +static int quad8_count_preset_read(struct counter_device *counter, + struct counter_count *count, u64 *preset) { const struct quad8 *const priv = counter->priv; - return sprintf(buf, "%u\n", priv->preset[count->id]); + *preset = priv->preset[count->id]; + + return 0; } static void quad8_preset_register_set(struct quad8 *const priv, const int id, @@ -658,16 +622,10 @@ static void quad8_preset_register_set(struct quad8 *const priv, const int id, outb(preset >> (8 * i), base_offset); } -static ssize_t quad8_count_preset_write(struct counter_device *counter, - struct counter_count *count, void *private, const char *buf, size_t len) +static int quad8_count_preset_write(struct counter_device *counter, + struct counter_count *count, u64 preset) { struct quad8 *const priv = counter->priv; - unsigned int preset; - int ret; - - ret = kstrtouint(buf, 0, &preset); - if (ret) - return ret; /* Only 24-bit values are supported */ if (preset > 0xFFFFFF) @@ -679,11 +637,11 @@ static ssize_t quad8_count_preset_write(struct counter_device *counter, mutex_unlock(&priv->lock); - return len; + return 0; } -static ssize_t quad8_count_ceiling_read(struct counter_device *counter, - struct counter_count *count, void *private, char *buf) +static int quad8_count_ceiling_read(struct counter_device *counter, + struct counter_count *count, u64 *ceiling) { struct quad8 *const priv = counter->priv; @@ -693,26 +651,23 @@ static ssize_t quad8_count_ceiling_read(struct counter_device *counter, switch (priv->count_mode[count->id]) { case 1: case 3: - mutex_unlock(&priv->lock); - return sprintf(buf, "%u\n", priv->preset[count->id]); + *ceiling = priv->preset[count->id]; + break; + default: + /* By default 0x1FFFFFF (25 bits unsigned) is maximum count */ + *ceiling = 0x1FFFFFF; + break; } mutex_unlock(&priv->lock); - /* By default 0x1FFFFFF (25 bits unsigned) is maximum count */ - return sprintf(buf, "33554431\n"); + return 0; } -static ssize_t quad8_count_ceiling_write(struct counter_device *counter, - struct counter_count *count, void *private, const char *buf, size_t len) +static int quad8_count_ceiling_write(struct counter_device *counter, + struct counter_count *count, u64 ceiling) { struct quad8 *const priv = counter->priv; - unsigned int ceiling; - int ret; - - ret = kstrtouint(buf, 0, &ceiling); - if (ret) - return ret; /* Only 24-bit values are supported */ if (ceiling > 0xFFFFFF) @@ -726,7 +681,7 @@ static ssize_t quad8_count_ceiling_write(struct counter_device *counter, case 3: quad8_preset_register_set(priv, count->id, ceiling); mutex_unlock(&priv->lock); - return len; + return 0; } mutex_unlock(&priv->lock); @@ -734,27 +689,25 @@ static ssize_t quad8_count_ceiling_write(struct counter_device *counter, return -EINVAL; } -static ssize_t quad8_count_preset_enable_read(struct counter_device *counter, - struct counter_count *count, void *private, char *buf) +static int quad8_count_preset_enable_read(struct counter_device *counter, + struct counter_count *count, + u8 *preset_enable) { const struct quad8 *const priv = counter->priv; - return sprintf(buf, "%u\n", !priv->preset_enable[count->id]); + *preset_enable = !priv->preset_enable[count->id]; + + return 0; } -static ssize_t quad8_count_preset_enable_write(struct counter_device *counter, - struct counter_count *count, void *private, const char *buf, size_t len) +static int quad8_count_preset_enable_write(struct counter_device *counter, + struct counter_count *count, + u8 preset_enable) { struct quad8 *const priv = counter->priv; const int base_offset = priv->base + 2 * count->id + 1; - bool preset_enable; - int ret; unsigned int ior_cfg; - ret = kstrtobool(buf, &preset_enable); - if (ret) - return ret; - /* Preset enable is active low in Input/Output Control register */ preset_enable = !preset_enable; @@ -762,25 +715,24 @@ static ssize_t quad8_count_preset_enable_write(struct counter_device *counter, priv->preset_enable[count->id] = preset_enable; - ior_cfg = priv->ab_enable[count->id] | (unsigned int)preset_enable << 1; + ior_cfg = priv->ab_enable[count->id] | preset_enable << 1; /* Load I/O control configuration to Input / Output Control Register */ outb(QUAD8_CTR_IOR | ior_cfg, base_offset); mutex_unlock(&priv->lock); - return len; + return 0; } -static ssize_t quad8_signal_cable_fault_read(struct counter_device *counter, - struct counter_signal *signal, - void *private, char *buf) +static int quad8_signal_cable_fault_read(struct counter_device *counter, + struct counter_signal *signal, + u8 *cable_fault) { struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id / 2; bool disabled; unsigned int status; - unsigned int fault; mutex_lock(&priv->lock); @@ -797,36 +749,31 @@ static ssize_t quad8_signal_cable_fault_read(struct counter_device *counter, mutex_unlock(&priv->lock); /* Mask respective channel and invert logic */ - fault = !(status & BIT(channel_id)); + *cable_fault = !(status & BIT(channel_id)); - return sprintf(buf, "%u\n", fault); + return 0; } -static ssize_t quad8_signal_cable_fault_enable_read( - struct counter_device *counter, struct counter_signal *signal, - void *private, char *buf) +static int quad8_signal_cable_fault_enable_read(struct counter_device *counter, + struct counter_signal *signal, + u8 *enable) { const struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id / 2; - const unsigned int enb = !!(priv->cable_fault_enable & BIT(channel_id)); - return sprintf(buf, "%u\n", enb); + *enable = !!(priv->cable_fault_enable & BIT(channel_id)); + + return 0; } -static ssize_t quad8_signal_cable_fault_enable_write( - struct counter_device *counter, struct counter_signal *signal, - void *private, const char *buf, size_t len) +static int quad8_signal_cable_fault_enable_write(struct counter_device *counter, + struct counter_signal *signal, + u8 enable) { struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id / 2; - bool enable; - int ret; unsigned int cable_fault_enable; - ret = kstrtobool(buf, &enable); - if (ret) - return ret; - mutex_lock(&priv->lock); if (enable) @@ -841,31 +788,27 @@ static ssize_t quad8_signal_cable_fault_enable_write( mutex_unlock(&priv->lock); - return len; + return 0; } -static ssize_t quad8_signal_fck_prescaler_read(struct counter_device *counter, - struct counter_signal *signal, void *private, char *buf) +static int quad8_signal_fck_prescaler_read(struct counter_device *counter, + struct counter_signal *signal, + u8 *prescaler) { const struct quad8 *const priv = counter->priv; - const size_t channel_id = signal->id / 2; - return sprintf(buf, "%u\n", priv->fck_prescaler[channel_id]); + *prescaler = priv->fck_prescaler[signal->id / 2]; + + return 0; } -static ssize_t quad8_signal_fck_prescaler_write(struct counter_device *counter, - struct counter_signal *signal, void *private, const char *buf, - size_t len) +static int quad8_signal_fck_prescaler_write(struct counter_device *counter, + struct counter_signal *signal, + u8 prescaler) { struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id / 2; const int base_offset = priv->base + 2 * channel_id; - u8 prescaler; - int ret; - - ret = kstrtou8(buf, 0, &prescaler); - if (ret) - return ret; mutex_lock(&priv->lock); @@ -881,31 +824,30 @@ static ssize_t quad8_signal_fck_prescaler_write(struct counter_device *counter, mutex_unlock(&priv->lock); - return len; + return 0; } -static const struct counter_signal_ext quad8_signal_ext[] = { - { - .name = "cable_fault", - .read = quad8_signal_cable_fault_read - }, - { - .name = "cable_fault_enable", - .read = quad8_signal_cable_fault_enable_read, - .write = quad8_signal_cable_fault_enable_write - }, - { - .name = "filter_clock_prescaler", - .read = quad8_signal_fck_prescaler_read, - .write = quad8_signal_fck_prescaler_write - } +static struct counter_comp quad8_signal_ext[] = { + COUNTER_COMP_SIGNAL_BOOL("cable_fault", quad8_signal_cable_fault_read, + NULL), + COUNTER_COMP_SIGNAL_BOOL("cable_fault_enable", + quad8_signal_cable_fault_enable_read, + quad8_signal_cable_fault_enable_write), + COUNTER_COMP_SIGNAL_U8("filter_clock_prescaler", + quad8_signal_fck_prescaler_read, + quad8_signal_fck_prescaler_write) }; -static const struct counter_signal_ext quad8_index_ext[] = { - COUNTER_SIGNAL_ENUM("index_polarity", &quad8_index_pol_enum), - COUNTER_SIGNAL_ENUM_AVAILABLE("index_polarity", &quad8_index_pol_enum), - COUNTER_SIGNAL_ENUM("synchronous_mode", &quad8_syn_mode_enum), - COUNTER_SIGNAL_ENUM_AVAILABLE("synchronous_mode", &quad8_syn_mode_enum) +static DEFINE_COUNTER_ENUM(quad8_index_pol_enum, quad8_index_polarity_modes); +static DEFINE_COUNTER_ENUM(quad8_synch_mode_enum, quad8_synchronous_modes); + +static struct counter_comp quad8_index_ext[] = { + COUNTER_COMP_SIGNAL_ENUM("index_polarity", quad8_index_polarity_get, + quad8_index_polarity_set, + quad8_index_pol_enum), + COUNTER_COMP_SIGNAL_ENUM("synchronous_mode", quad8_synchronous_mode_get, + quad8_synchronous_mode_set, + quad8_synch_mode_enum), }; #define QUAD8_QUAD_SIGNAL(_id, _name) { \ @@ -974,39 +916,30 @@ static struct counter_synapse quad8_count_synapses[][3] = { QUAD8_COUNT_SYNAPSES(6), QUAD8_COUNT_SYNAPSES(7) }; -static const struct counter_count_ext quad8_count_ext[] = { - { - .name = "ceiling", - .read = quad8_count_ceiling_read, - .write = quad8_count_ceiling_write - }, - { - .name = "floor", - .read = quad8_count_floor_read - }, - COUNTER_COUNT_ENUM("count_mode", &quad8_cnt_mode_enum), - COUNTER_COUNT_ENUM_AVAILABLE("count_mode", &quad8_cnt_mode_enum), - { - .name = "direction", - .read = quad8_count_direction_read - }, - { - .name = "enable", - .read = quad8_count_enable_read, - .write = quad8_count_enable_write - }, - COUNTER_COUNT_ENUM("error_noise", &quad8_error_noise_enum), - COUNTER_COUNT_ENUM_AVAILABLE("error_noise", &quad8_error_noise_enum), - { - .name = "preset", - .read = quad8_count_preset_read, - .write = quad8_count_preset_write - }, - { - .name = "preset_enable", - .read = quad8_count_preset_enable_read, - .write = quad8_count_preset_enable_write - } +static const enum counter_count_mode quad8_cnt_modes[] = { + COUNTER_COUNT_MODE_NORMAL, + COUNTER_COUNT_MODE_RANGE_LIMIT, + COUNTER_COUNT_MODE_NON_RECYCLE, + COUNTER_COUNT_MODE_MODULO_N, +}; + +static DEFINE_COUNTER_AVAILABLE(quad8_count_mode_available, quad8_cnt_modes); + +static DEFINE_COUNTER_ENUM(quad8_error_noise_enum, quad8_noise_error_states); + +static struct counter_comp quad8_count_ext[] = { + COUNTER_COMP_CEILING(quad8_count_ceiling_read, + quad8_count_ceiling_write), + COUNTER_COMP_FLOOR(quad8_count_floor_read, NULL), + COUNTER_COMP_COUNT_MODE(quad8_count_mode_read, quad8_count_mode_write, + quad8_count_mode_available), + COUNTER_COMP_DIRECTION(quad8_direction_read), + COUNTER_COMP_ENABLE(quad8_count_enable_read, quad8_count_enable_write), + COUNTER_COMP_COUNT_ENUM("error_noise", quad8_error_noise_get, NULL, + quad8_error_noise_enum), + COUNTER_COMP_PRESET(quad8_count_preset_read, quad8_count_preset_write), + COUNTER_COMP_PRESET_ENABLE(quad8_count_preset_enable_read, + quad8_count_preset_enable_write), }; #define QUAD8_COUNT(_id, _cntname) { \ diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile index 19742e6f5e3e..1ab7e087fdc2 100644 --- a/drivers/counter/Makefile +++ b/drivers/counter/Makefile @@ -4,6 +4,7 @@ # obj-$(CONFIG_COUNTER) += counter.o +counter-y := counter-core.o counter-sysfs.o obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o obj-$(CONFIG_INTERRUPT_CNT) += interrupt-cnt.o diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c new file mode 100644 index 000000000000..c533a6ff12cf --- /dev/null +++ b/drivers/counter/counter-core.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Generic Counter interface + * Copyright (C) 2020 William Breathitt Gray + */ +#include +#include +#include +#include +#include +#include +#include + +#include "counter-sysfs.h" + +/* Provides a unique ID for each counter device */ +static DEFINE_IDA(counter_ida); + +static void counter_device_release(struct device *dev) +{ + ida_free(&counter_ida, dev->id); +} + +static struct device_type counter_device_type = { + .name = "counter_device", + .release = counter_device_release, +}; + +static struct bus_type counter_bus_type = { + .name = "counter", + .dev_name = "counter", +}; + +/** + * counter_register - register Counter to the system + * @counter: pointer to Counter to register + * + * This function registers a Counter to the system. A sysfs "counter" directory + * will be created and populated with sysfs attributes correlating with the + * Counter Signals, Synapses, and Counts respectively. + */ +int counter_register(struct counter_device *const counter) +{ + struct device *const dev = &counter->dev; + int id; + int err; + + /* Acquire unique ID */ + id = ida_alloc(&counter_ida, GFP_KERNEL); + if (id < 0) + return id; + + /* Configure device structure for Counter */ + dev->id = id; + dev->type = &counter_device_type; + dev->bus = &counter_bus_type; + if (counter->parent) { + dev->parent = counter->parent; + dev->of_node = counter->parent->of_node; + } + device_initialize(dev); + dev_set_drvdata(dev, counter); + + /* Add Counter sysfs attributes */ + err = counter_sysfs_add(counter); + if (err < 0) + goto err_free_id; + + /* Add device to system */ + err = device_add(dev); + if (err < 0) + goto err_free_id; + + return 0; + +err_free_id: + put_device(dev); + return err; +} +EXPORT_SYMBOL_GPL(counter_register); + +/** + * counter_unregister - unregister Counter from the system + * @counter: pointer to Counter to unregister + * + * The Counter is unregistered from the system. + */ +void counter_unregister(struct counter_device *const counter) +{ + if (!counter) + return; + + device_unregister(&counter->dev); +} +EXPORT_SYMBOL_GPL(counter_unregister); + +static void devm_counter_release(void *counter) +{ + counter_unregister(counter); +} + +/** + * devm_counter_register - Resource-managed counter_register + * @dev: device to allocate counter_device for + * @counter: pointer to Counter to register + * + * Managed counter_register. The Counter registered with this function is + * automatically unregistered on driver detach. This function calls + * counter_register internally. Refer to that function for more information. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int devm_counter_register(struct device *dev, + struct counter_device *const counter) +{ + int err; + + err = counter_register(counter); + if (err < 0) + return err; + + return devm_add_action_or_reset(dev, devm_counter_release, counter); +} +EXPORT_SYMBOL_GPL(devm_counter_register); + +static int __init counter_init(void) +{ + return bus_register(&counter_bus_type); +} + +static void __exit counter_exit(void) +{ + bus_unregister(&counter_bus_type); +} + +subsys_initcall(counter_init); +module_exit(counter_exit); + +MODULE_AUTHOR("William Breathitt Gray "); +MODULE_DESCRIPTION("Generic Counter interface"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c new file mode 100644 index 000000000000..108cbd838eb9 --- /dev/null +++ b/drivers/counter/counter-sysfs.c @@ -0,0 +1,849 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Generic Counter sysfs interface + * Copyright (C) 2020 William Breathitt Gray + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "counter-sysfs.h" + +/** + * struct counter_attribute - Counter sysfs attribute + * @dev_attr: device attribute for sysfs + * @l: node to add Counter attribute to attribute group list + * @comp: Counter component callbacks and data + * @scope: Counter scope of the attribute + * @parent: pointer to the parent component + */ +struct counter_attribute { + struct device_attribute dev_attr; + struct list_head l; + + struct counter_comp comp; + enum counter_scope scope; + void *parent; +}; + +#define to_counter_attribute(_dev_attr) \ + container_of(_dev_attr, struct counter_attribute, dev_attr) + +/** + * struct counter_attribute_group - container for attribute group + * @name: name of the attribute group + * @attr_list: list to keep track of created attributes + * @num_attr: number of attributes + */ +struct counter_attribute_group { + const char *name; + struct list_head attr_list; + size_t num_attr; +}; + +static const char *const counter_function_str[] = { + [COUNTER_FUNCTION_INCREASE] = "increase", + [COUNTER_FUNCTION_DECREASE] = "decrease", + [COUNTER_FUNCTION_PULSE_DIRECTION] = "pulse-direction", + [COUNTER_FUNCTION_QUADRATURE_X1_A] = "quadrature x1 a", + [COUNTER_FUNCTION_QUADRATURE_X1_B] = "quadrature x1 b", + [COUNTER_FUNCTION_QUADRATURE_X2_A] = "quadrature x2 a", + [COUNTER_FUNCTION_QUADRATURE_X2_B] = "quadrature x2 b", + [COUNTER_FUNCTION_QUADRATURE_X4] = "quadrature x4" +}; + +static const char *const counter_signal_value_str[] = { + [COUNTER_SIGNAL_LEVEL_LOW] = "low", + [COUNTER_SIGNAL_LEVEL_HIGH] = "high" +}; + +static const char *const counter_synapse_action_str[] = { + [COUNTER_SYNAPSE_ACTION_NONE] = "none", + [COUNTER_SYNAPSE_ACTION_RISING_EDGE] = "rising edge", + [COUNTER_SYNAPSE_ACTION_FALLING_EDGE] = "falling edge", + [COUNTER_SYNAPSE_ACTION_BOTH_EDGES] = "both edges" +}; + +static const char *const counter_count_direction_str[] = { + [COUNTER_COUNT_DIRECTION_FORWARD] = "forward", + [COUNTER_COUNT_DIRECTION_BACKWARD] = "backward" +}; + +static const char *const counter_count_mode_str[] = { + [COUNTER_COUNT_MODE_NORMAL] = "normal", + [COUNTER_COUNT_MODE_RANGE_LIMIT] = "range limit", + [COUNTER_COUNT_MODE_NON_RECYCLE] = "non-recycle", + [COUNTER_COUNT_MODE_MODULO_N] = "modulo-n" +}; + +static ssize_t counter_comp_u8_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct counter_attribute *const a = to_counter_attribute(attr); + struct counter_device *const counter = dev_get_drvdata(dev); + int err; + u8 data = 0; + + switch (a->scope) { + case COUNTER_SCOPE_DEVICE: + err = a->comp.device_u8_read(counter, &data); + break; + case COUNTER_SCOPE_SIGNAL: + err = a->comp.signal_u8_read(counter, a->parent, &data); + break; + case COUNTER_SCOPE_COUNT: + err = a->comp.count_u8_read(counter, a->parent, &data); + break; + default: + return -EINVAL; + } + if (err < 0) + return err; + + if (a->comp.type == COUNTER_COMP_BOOL) + /* data should already be boolean but ensure just to be safe */ + data = !!data; + + return sprintf(buf, "%u\n", (unsigned int)data); +} + +static ssize_t counter_comp_u8_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + const struct counter_attribute *const a = to_counter_attribute(attr); + struct counter_device *const counter = dev_get_drvdata(dev); + int err; + bool bool_data = 0; + u8 data = 0; + + if (a->comp.type == COUNTER_COMP_BOOL) { + err = kstrtobool(buf, &bool_data); + data = bool_data; + } else + err = kstrtou8(buf, 0, &data); + if (err < 0) + return err; + + switch (a->scope) { + case COUNTER_SCOPE_DEVICE: + err = a->comp.device_u8_write(counter, data); + break; + case COUNTER_SCOPE_SIGNAL: + err = a->comp.signal_u8_write(counter, a->parent, data); + break; + case COUNTER_SCOPE_COUNT: + err = a->comp.count_u8_write(counter, a->parent, data); + break; + default: + return -EINVAL; + } + if (err < 0) + return err; + + return len; +} + +static ssize_t counter_comp_u32_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct counter_attribute *const a = to_counter_attribute(attr); + struct counter_device *const counter = dev_get_drvdata(dev); + const struct counter_available *const avail = a->comp.priv; + int err; + u32 data = 0; + + switch (a->scope) { + case COUNTER_SCOPE_DEVICE: + err = a->comp.device_u32_read(counter, &data); + break; + case COUNTER_SCOPE_SIGNAL: + err = a->comp.signal_u32_read(counter, a->parent, &data); + break; + case COUNTER_SCOPE_COUNT: + if (a->comp.type == COUNTER_COMP_SYNAPSE_ACTION) + err = a->comp.action_read(counter, a->parent, + a->comp.priv, &data); + else + err = a->comp.count_u32_read(counter, a->parent, &data); + break; + default: + return -EINVAL; + } + if (err < 0) + return err; + + switch (a->comp.type) { + case COUNTER_COMP_FUNCTION: + return sysfs_emit(buf, "%s\n", counter_function_str[data]); + case COUNTER_COMP_SIGNAL_LEVEL: + return sysfs_emit(buf, "%s\n", counter_signal_value_str[data]); + case COUNTER_COMP_SYNAPSE_ACTION: + return sysfs_emit(buf, "%s\n", counter_synapse_action_str[data]); + case COUNTER_COMP_ENUM: + return sysfs_emit(buf, "%s\n", avail->strs[data]); + case COUNTER_COMP_COUNT_DIRECTION: + return sysfs_emit(buf, "%s\n", counter_count_direction_str[data]); + case COUNTER_COMP_COUNT_MODE: + return sysfs_emit(buf, "%s\n", counter_count_mode_str[data]); + default: + return sprintf(buf, "%u\n", (unsigned int)data); + } +} + +static int counter_find_enum(u32 *const enum_item, const u32 *const enums, + const size_t num_enums, const char *const buf, + const char *const string_array[]) +{ + size_t index; + + for (index = 0; index < num_enums; index++) { + *enum_item = enums[index]; + if (sysfs_streq(buf, string_array[*enum_item])) + return 0; + } + + return -EINVAL; +} + +static ssize_t counter_comp_u32_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + const struct counter_attribute *const a = to_counter_attribute(attr); + struct counter_device *const counter = dev_get_drvdata(dev); + struct counter_count *const count = a->parent; + struct counter_synapse *const synapse = a->comp.priv; + const struct counter_available *const avail = a->comp.priv; + int err; + u32 data = 0; + + switch (a->comp.type) { + case COUNTER_COMP_FUNCTION: + err = counter_find_enum(&data, count->functions_list, + count->num_functions, buf, + counter_function_str); + break; + case COUNTER_COMP_SYNAPSE_ACTION: + err = counter_find_enum(&data, synapse->actions_list, + synapse->num_actions, buf, + counter_synapse_action_str); + break; + case COUNTER_COMP_ENUM: + err = __sysfs_match_string(avail->strs, avail->num_items, buf); + data = err; + break; + case COUNTER_COMP_COUNT_MODE: + err = counter_find_enum(&data, avail->enums, avail->num_items, + buf, counter_count_mode_str); + break; + default: + err = kstrtou32(buf, 0, &data); + break; + } + if (err < 0) + return err; + + switch (a->scope) { + case COUNTER_SCOPE_DEVICE: + err = a->comp.device_u32_write(counter, data); + break; + case COUNTER_SCOPE_SIGNAL: + err = a->comp.signal_u32_write(counter, a->parent, data); + break; + case COUNTER_SCOPE_COUNT: + if (a->comp.type == COUNTER_COMP_SYNAPSE_ACTION) + err = a->comp.action_write(counter, count, synapse, + data); + else + err = a->comp.count_u32_write(counter, count, data); + break; + default: + return -EINVAL; + } + if (err < 0) + return err; + + return len; +} + +static ssize_t counter_comp_u64_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct counter_attribute *const a = to_counter_attribute(attr); + struct counter_device *const counter = dev_get_drvdata(dev); + int err; + u64 data = 0; + + switch (a->scope) { + case COUNTER_SCOPE_DEVICE: + err = a->comp.device_u64_read(counter, &data); + break; + case COUNTER_SCOPE_SIGNAL: + err = a->comp.signal_u64_read(counter, a->parent, &data); + break; + case COUNTER_SCOPE_COUNT: + err = a->comp.count_u64_read(counter, a->parent, &data); + break; + default: + return -EINVAL; + } + if (err < 0) + return err; + + return sprintf(buf, "%llu\n", (unsigned long long)data); +} + +static ssize_t counter_comp_u64_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + const struct counter_attribute *const a = to_counter_attribute(attr); + struct counter_device *const counter = dev_get_drvdata(dev); + int err; + u64 data = 0; + + err = kstrtou64(buf, 0, &data); + if (err < 0) + return err; + + switch (a->scope) { + case COUNTER_SCOPE_DEVICE: + err = a->comp.device_u64_write(counter, data); + break; + case COUNTER_SCOPE_SIGNAL: + err = a->comp.signal_u64_write(counter, a->parent, data); + break; + case COUNTER_SCOPE_COUNT: + err = a->comp.count_u64_write(counter, a->parent, data); + break; + default: + return -EINVAL; + } + if (err < 0) + return err; + + return len; +} + +static ssize_t enums_available_show(const u32 *const enums, + const size_t num_enums, + const char *const strs[], char *buf) +{ + size_t len = 0; + size_t index; + + for (index = 0; index < num_enums; index++) + len += sysfs_emit_at(buf, len, "%s\n", strs[enums[index]]); + + return len; +} + +static ssize_t strs_available_show(const struct counter_available *const avail, + char *buf) +{ + size_t len = 0; + size_t index; + + for (index = 0; index < avail->num_items; index++) + len += sysfs_emit_at(buf, len, "%s\n", avail->strs[index]); + + return len; +} + +static ssize_t counter_comp_available_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + const struct counter_attribute *const a = to_counter_attribute(attr); + const struct counter_count *const count = a->parent; + const struct counter_synapse *const synapse = a->comp.priv; + const struct counter_available *const avail = a->comp.priv; + + switch (a->comp.type) { + case COUNTER_COMP_FUNCTION: + return enums_available_show(count->functions_list, + count->num_functions, + counter_function_str, buf); + case COUNTER_COMP_SYNAPSE_ACTION: + return enums_available_show(synapse->actions_list, + synapse->num_actions, + counter_synapse_action_str, buf); + case COUNTER_COMP_ENUM: + return strs_available_show(avail, buf); + case COUNTER_COMP_COUNT_MODE: + return enums_available_show(avail->enums, avail->num_items, + counter_count_mode_str, buf); + default: + return -EINVAL; + } +} + +static int counter_avail_attr_create(struct device *const dev, + struct counter_attribute_group *const group, + const struct counter_comp *const comp, void *const parent) +{ + struct counter_attribute *counter_attr; + struct device_attribute *dev_attr; + + counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL); + if (!counter_attr) + return -ENOMEM; + + /* Configure Counter attribute */ + counter_attr->comp.type = comp->type; + counter_attr->comp.priv = comp->priv; + counter_attr->parent = parent; + + /* Initialize sysfs attribute */ + dev_attr = &counter_attr->dev_attr; + sysfs_attr_init(&dev_attr->attr); + + /* Configure device attribute */ + dev_attr->attr.name = devm_kasprintf(dev, GFP_KERNEL, "%s_available", + comp->name); + if (!dev_attr->attr.name) + return -ENOMEM; + dev_attr->attr.mode = 0444; + dev_attr->show = counter_comp_available_show; + + /* Store list node */ + list_add(&counter_attr->l, &group->attr_list); + group->num_attr++; + + return 0; +} + +static int counter_attr_create(struct device *const dev, + struct counter_attribute_group *const group, + const struct counter_comp *const comp, + const enum counter_scope scope, + void *const parent) +{ + struct counter_attribute *counter_attr; + struct device_attribute *dev_attr; + + counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL); + if (!counter_attr) + return -ENOMEM; + + /* Configure Counter attribute */ + counter_attr->comp = *comp; + counter_attr->scope = scope; + counter_attr->parent = parent; + + /* Configure device attribute */ + dev_attr = &counter_attr->dev_attr; + sysfs_attr_init(&dev_attr->attr); + dev_attr->attr.name = comp->name; + switch (comp->type) { + case COUNTER_COMP_U8: + case COUNTER_COMP_BOOL: + if (comp->device_u8_read) { + dev_attr->attr.mode |= 0444; + dev_attr->show = counter_comp_u8_show; + } + if (comp->device_u8_write) { + dev_attr->attr.mode |= 0200; + dev_attr->store = counter_comp_u8_store; + } + break; + case COUNTER_COMP_SIGNAL_LEVEL: + case COUNTER_COMP_FUNCTION: + case COUNTER_COMP_SYNAPSE_ACTION: + case COUNTER_COMP_ENUM: + case COUNTER_COMP_COUNT_DIRECTION: + case COUNTER_COMP_COUNT_MODE: + if (comp->device_u32_read) { + dev_attr->attr.mode |= 0444; + dev_attr->show = counter_comp_u32_show; + } + if (comp->device_u32_write) { + dev_attr->attr.mode |= 0200; + dev_attr->store = counter_comp_u32_store; + } + break; + case COUNTER_COMP_U64: + if (comp->device_u64_read) { + dev_attr->attr.mode |= 0444; + dev_attr->show = counter_comp_u64_show; + } + if (comp->device_u64_write) { + dev_attr->attr.mode |= 0200; + dev_attr->store = counter_comp_u64_store; + } + break; + default: + return -EINVAL; + } + + /* Store list node */ + list_add(&counter_attr->l, &group->attr_list); + group->num_attr++; + + /* Create "*_available" attribute if needed */ + switch (comp->type) { + case COUNTER_COMP_FUNCTION: + case COUNTER_COMP_SYNAPSE_ACTION: + case COUNTER_COMP_ENUM: + case COUNTER_COMP_COUNT_MODE: + return counter_avail_attr_create(dev, group, comp, parent); + default: + return 0; + } +} + +static ssize_t counter_comp_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%s\n", to_counter_attribute(attr)->comp.name); +} + +static int counter_name_attr_create(struct device *const dev, + struct counter_attribute_group *const group, + const char *const name) +{ + struct counter_attribute *counter_attr; + + counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL); + if (!counter_attr) + return -ENOMEM; + + /* Configure Counter attribute */ + counter_attr->comp.name = name; + + /* Configure device attribute */ + sysfs_attr_init(&counter_attr->dev_attr.attr); + counter_attr->dev_attr.attr.name = "name"; + counter_attr->dev_attr.attr.mode = 0444; + counter_attr->dev_attr.show = counter_comp_name_show; + + /* Store list node */ + list_add(&counter_attr->l, &group->attr_list); + group->num_attr++; + + return 0; +} + +static struct counter_comp counter_signal_comp = { + .type = COUNTER_COMP_SIGNAL_LEVEL, + .name = "signal", +}; + +static int counter_signal_attrs_create(struct counter_device *const counter, + struct counter_attribute_group *const cattr_group, + struct counter_signal *const signal) +{ + const enum counter_scope scope = COUNTER_SCOPE_SIGNAL; + struct device *const dev = &counter->dev; + int err; + struct counter_comp comp; + size_t i; + + /* Create main Signal attribute */ + comp = counter_signal_comp; + comp.signal_u32_read = counter->ops->signal_read; + err = counter_attr_create(dev, cattr_group, &comp, scope, signal); + if (err < 0) + return err; + + /* Create Signal name attribute */ + err = counter_name_attr_create(dev, cattr_group, signal->name); + if (err < 0) + return err; + + /* Create an attribute for each extension */ + for (i = 0; i < signal->num_ext; i++) { + err = counter_attr_create(dev, cattr_group, signal->ext + i, + scope, signal); + if (err < 0) + return err; + } + + return 0; +} + +static int counter_sysfs_signals_add(struct counter_device *const counter, + struct counter_attribute_group *const groups) +{ + size_t i; + int err; + + /* Add each Signal */ + for (i = 0; i < counter->num_signals; i++) { + /* Generate Signal attribute directory name */ + groups[i].name = devm_kasprintf(&counter->dev, GFP_KERNEL, + "signal%zu", i); + if (!groups[i].name) + return -ENOMEM; + + /* Create all attributes associated with Signal */ + err = counter_signal_attrs_create(counter, groups + i, + counter->signals + i); + if (err < 0) + return err; + } + + return 0; +} + +static int counter_sysfs_synapses_add(struct counter_device *const counter, + struct counter_attribute_group *const group, + struct counter_count *const count) +{ + size_t i; + + /* Add each Synapse */ + for (i = 0; i < count->num_synapses; i++) { + struct device *const dev = &counter->dev; + struct counter_synapse *synapse; + size_t id; + struct counter_comp comp; + int err; + + synapse = count->synapses + i; + + /* Generate Synapse action name */ + id = synapse->signal - counter->signals; + comp.name = devm_kasprintf(dev, GFP_KERNEL, "signal%zu_action", + id); + if (!comp.name) + return -ENOMEM; + + /* Create action attribute */ + comp.type = COUNTER_COMP_SYNAPSE_ACTION; + comp.action_read = counter->ops->action_read; + comp.action_write = counter->ops->action_write; + comp.priv = synapse; + err = counter_attr_create(dev, group, &comp, + COUNTER_SCOPE_COUNT, count); + if (err < 0) + return err; + } + + return 0; +} + +static struct counter_comp counter_count_comp = + COUNTER_COMP_COUNT_U64("count", NULL, NULL); + +static struct counter_comp counter_function_comp = { + .type = COUNTER_COMP_FUNCTION, + .name = "function", +}; + +static int counter_count_attrs_create(struct counter_device *const counter, + struct counter_attribute_group *const cattr_group, + struct counter_count *const count) +{ + const enum counter_scope scope = COUNTER_SCOPE_COUNT; + struct device *const dev = &counter->dev; + int err; + struct counter_comp comp; + size_t i; + + /* Create main Count attribute */ + comp = counter_count_comp; + comp.count_u64_read = counter->ops->count_read; + comp.count_u64_write = counter->ops->count_write; + err = counter_attr_create(dev, cattr_group, &comp, scope, count); + if (err < 0) + return err; + + /* Create Count name attribute */ + err = counter_name_attr_create(dev, cattr_group, count->name); + if (err < 0) + return err; + + /* Create Count function attribute */ + comp = counter_function_comp; + comp.count_u32_read = counter->ops->function_read; + comp.count_u32_write = counter->ops->function_write; + err = counter_attr_create(dev, cattr_group, &comp, scope, count); + if (err < 0) + return err; + + /* Create an attribute for each extension */ + for (i = 0; i < count->num_ext; i++) { + err = counter_attr_create(dev, cattr_group, count->ext + i, + scope, count); + if (err < 0) + return err; + } + + return 0; +} + +static int counter_sysfs_counts_add(struct counter_device *const counter, + struct counter_attribute_group *const groups) +{ + size_t i; + struct counter_count *count; + int err; + + /* Add each Count */ + for (i = 0; i < counter->num_counts; i++) { + count = counter->counts + i; + + /* Generate Count attribute directory name */ + groups[i].name = devm_kasprintf(&counter->dev, GFP_KERNEL, + "count%zu", i); + if (!groups[i].name) + return -ENOMEM; + + /* Add sysfs attributes of the Synapses */ + err = counter_sysfs_synapses_add(counter, groups + i, count); + if (err < 0) + return err; + + /* Create all attributes associated with Count */ + err = counter_count_attrs_create(counter, groups + i, count); + if (err < 0) + return err; + } + + return 0; +} + +static int counter_num_signals_read(struct counter_device *counter, u8 *val) +{ + *val = counter->num_signals; + return 0; +} + +static int counter_num_counts_read(struct counter_device *counter, u8 *val) +{ + *val = counter->num_counts; + return 0; +} + +static struct counter_comp counter_num_signals_comp = + COUNTER_COMP_DEVICE_U8("num_signals", counter_num_signals_read, NULL); + +static struct counter_comp counter_num_counts_comp = + COUNTER_COMP_DEVICE_U8("num_counts", counter_num_counts_read, NULL); + +static int counter_sysfs_attr_add(struct counter_device *const counter, + struct counter_attribute_group *cattr_group) +{ + const enum counter_scope scope = COUNTER_SCOPE_DEVICE; + struct device *const dev = &counter->dev; + int err; + size_t i; + + /* Add Signals sysfs attributes */ + err = counter_sysfs_signals_add(counter, cattr_group); + if (err < 0) + return err; + cattr_group += counter->num_signals; + + /* Add Counts sysfs attributes */ + err = counter_sysfs_counts_add(counter, cattr_group); + if (err < 0) + return err; + cattr_group += counter->num_counts; + + /* Create name attribute */ + err = counter_name_attr_create(dev, cattr_group, counter->name); + if (err < 0) + return err; + + /* Create num_signals attribute */ + err = counter_attr_create(dev, cattr_group, &counter_num_signals_comp, + scope, NULL); + if (err < 0) + return err; + + /* Create num_counts attribute */ + err = counter_attr_create(dev, cattr_group, &counter_num_counts_comp, + scope, NULL); + if (err < 0) + return err; + + /* Create an attribute for each extension */ + for (i = 0; i < counter->num_ext; i++) { + err = counter_attr_create(dev, cattr_group, counter->ext + i, + scope, NULL); + if (err < 0) + return err; + } + + return 0; +} + +/** + * counter_sysfs_add - Adds Counter sysfs attributes to the device structure + * @counter: Pointer to the Counter device structure + * + * Counter sysfs attributes are created and added to the respective device + * structure for later registration to the system. Resource-managed memory + * allocation is performed by this function, and this memory should be freed + * when no longer needed (automatically by a device_unregister call, or + * manually by a devres_release_all call). + */ +int counter_sysfs_add(struct counter_device *const counter) +{ + struct device *const dev = &counter->dev; + const size_t num_groups = counter->num_signals + counter->num_counts + 1; + struct counter_attribute_group *cattr_groups; + size_t i, j; + int err; + struct attribute_group *groups; + struct counter_attribute *p; + + /* Allocate space for attribute groups (signals, counts, and ext) */ + cattr_groups = devm_kcalloc(dev, num_groups, sizeof(*cattr_groups), + GFP_KERNEL); + if (!cattr_groups) + return -ENOMEM; + + /* Initialize attribute lists */ + for (i = 0; i < num_groups; i++) + INIT_LIST_HEAD(&cattr_groups[i].attr_list); + + /* Add Counter device sysfs attributes */ + err = counter_sysfs_attr_add(counter, cattr_groups); + if (err < 0) + return err; + + /* Allocate attribute group pointers for association with device */ + dev->groups = devm_kcalloc(dev, num_groups + 1, sizeof(*dev->groups), + GFP_KERNEL); + if (!dev->groups) + return -ENOMEM; + + /* Allocate space for attribute groups */ + groups = devm_kcalloc(dev, num_groups, sizeof(*groups), GFP_KERNEL); + if (!groups) + return -ENOMEM; + + /* Prepare each group of attributes for association */ + for (i = 0; i < num_groups; i++) { + groups[i].name = cattr_groups[i].name; + + /* Allocate space for attribute pointers */ + groups[i].attrs = devm_kcalloc(dev, + cattr_groups[i].num_attr + 1, + sizeof(*groups[i].attrs), + GFP_KERNEL); + if (!groups[i].attrs) + return -ENOMEM; + + /* Add attribute pointers to attribute group */ + j = 0; + list_for_each_entry(p, &cattr_groups[i].attr_list, l) + groups[i].attrs[j++] = &p->dev_attr.attr; + + /* Associate attribute group */ + dev->groups[i] = &groups[i]; + } + + return 0; +} diff --git a/drivers/counter/counter-sysfs.h b/drivers/counter/counter-sysfs.h new file mode 100644 index 000000000000..14fe566aca0e --- /dev/null +++ b/drivers/counter/counter-sysfs.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Counter sysfs interface + * Copyright (C) 2020 William Breathitt Gray + */ +#ifndef _COUNTER_SYSFS_H_ +#define _COUNTER_SYSFS_H_ + +#include + +int counter_sysfs_add(struct counter_device *const counter); + +#endif /* _COUNTER_SYSFS_H_ */ diff --git a/drivers/counter/counter.c b/drivers/counter/counter.c deleted file mode 100644 index de921e8a3f72..000000000000 --- a/drivers/counter/counter.c +++ /dev/null @@ -1,1496 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Generic Counter interface - * Copyright (C) 2018 William Breathitt Gray - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -const char *const counter_count_direction_str[2] = { - [COUNTER_COUNT_DIRECTION_FORWARD] = "forward", - [COUNTER_COUNT_DIRECTION_BACKWARD] = "backward" -}; -EXPORT_SYMBOL_GPL(counter_count_direction_str); - -const char *const counter_count_mode_str[4] = { - [COUNTER_COUNT_MODE_NORMAL] = "normal", - [COUNTER_COUNT_MODE_RANGE_LIMIT] = "range limit", - [COUNTER_COUNT_MODE_NON_RECYCLE] = "non-recycle", - [COUNTER_COUNT_MODE_MODULO_N] = "modulo-n" -}; -EXPORT_SYMBOL_GPL(counter_count_mode_str); - -ssize_t counter_signal_enum_read(struct counter_device *counter, - struct counter_signal *signal, void *priv, - char *buf) -{ - const struct counter_signal_enum_ext *const e = priv; - int err; - size_t index; - - if (!e->get) - return -EINVAL; - - err = e->get(counter, signal, &index); - if (err) - return err; - - if (index >= e->num_items) - return -EINVAL; - - return sprintf(buf, "%s\n", e->items[index]); -} -EXPORT_SYMBOL_GPL(counter_signal_enum_read); - -ssize_t counter_signal_enum_write(struct counter_device *counter, - struct counter_signal *signal, void *priv, - const char *buf, size_t len) -{ - const struct counter_signal_enum_ext *const e = priv; - ssize_t index; - int err; - - if (!e->set) - return -EINVAL; - - index = __sysfs_match_string(e->items, e->num_items, buf); - if (index < 0) - return index; - - err = e->set(counter, signal, index); - if (err) - return err; - - return len; -} -EXPORT_SYMBOL_GPL(counter_signal_enum_write); - -ssize_t counter_signal_enum_available_read(struct counter_device *counter, - struct counter_signal *signal, - void *priv, char *buf) -{ - const struct counter_signal_enum_ext *const e = priv; - size_t i; - size_t len = 0; - - if (!e->num_items) - return 0; - - for (i = 0; i < e->num_items; i++) - len += sprintf(buf + len, "%s\n", e->items[i]); - - return len; -} -EXPORT_SYMBOL_GPL(counter_signal_enum_available_read); - -ssize_t counter_count_enum_read(struct counter_device *counter, - struct counter_count *count, void *priv, - char *buf) -{ - const struct counter_count_enum_ext *const e = priv; - int err; - size_t index; - - if (!e->get) - return -EINVAL; - - err = e->get(counter, count, &index); - if (err) - return err; - - if (index >= e->num_items) - return -EINVAL; - - return sprintf(buf, "%s\n", e->items[index]); -} -EXPORT_SYMBOL_GPL(counter_count_enum_read); - -ssize_t counter_count_enum_write(struct counter_device *counter, - struct counter_count *count, void *priv, - const char *buf, size_t len) -{ - const struct counter_count_enum_ext *const e = priv; - ssize_t index; - int err; - - if (!e->set) - return -EINVAL; - - index = __sysfs_match_string(e->items, e->num_items, buf); - if (index < 0) - return index; - - err = e->set(counter, count, index); - if (err) - return err; - - return len; -} -EXPORT_SYMBOL_GPL(counter_count_enum_write); - -ssize_t counter_count_enum_available_read(struct counter_device *counter, - struct counter_count *count, - void *priv, char *buf) -{ - const struct counter_count_enum_ext *const e = priv; - size_t i; - size_t len = 0; - - if (!e->num_items) - return 0; - - for (i = 0; i < e->num_items; i++) - len += sprintf(buf + len, "%s\n", e->items[i]); - - return len; -} -EXPORT_SYMBOL_GPL(counter_count_enum_available_read); - -ssize_t counter_device_enum_read(struct counter_device *counter, void *priv, - char *buf) -{ - const struct counter_device_enum_ext *const e = priv; - int err; - size_t index; - - if (!e->get) - return -EINVAL; - - err = e->get(counter, &index); - if (err) - return err; - - if (index >= e->num_items) - return -EINVAL; - - return sprintf(buf, "%s\n", e->items[index]); -} -EXPORT_SYMBOL_GPL(counter_device_enum_read); - -ssize_t counter_device_enum_write(struct counter_device *counter, void *priv, - const char *buf, size_t len) -{ - const struct counter_device_enum_ext *const e = priv; - ssize_t index; - int err; - - if (!e->set) - return -EINVAL; - - index = __sysfs_match_string(e->items, e->num_items, buf); - if (index < 0) - return index; - - err = e->set(counter, index); - if (err) - return err; - - return len; -} -EXPORT_SYMBOL_GPL(counter_device_enum_write); - -ssize_t counter_device_enum_available_read(struct counter_device *counter, - void *priv, char *buf) -{ - const struct counter_device_enum_ext *const e = priv; - size_t i; - size_t len = 0; - - if (!e->num_items) - return 0; - - for (i = 0; i < e->num_items; i++) - len += sprintf(buf + len, "%s\n", e->items[i]); - - return len; -} -EXPORT_SYMBOL_GPL(counter_device_enum_available_read); - -struct counter_attr_parm { - struct counter_device_attr_group *group; - const char *prefix; - const char *name; - ssize_t (*show)(struct device *dev, struct device_attribute *attr, - char *buf); - ssize_t (*store)(struct device *dev, struct device_attribute *attr, - const char *buf, size_t len); - void *component; -}; - -struct counter_device_attr { - struct device_attribute dev_attr; - struct list_head l; - void *component; -}; - -static int counter_attribute_create(const struct counter_attr_parm *const parm) -{ - struct counter_device_attr *counter_attr; - struct device_attribute *dev_attr; - int err; - struct list_head *const attr_list = &parm->group->attr_list; - - /* Allocate a Counter device attribute */ - counter_attr = kzalloc(sizeof(*counter_attr), GFP_KERNEL); - if (!counter_attr) - return -ENOMEM; - dev_attr = &counter_attr->dev_attr; - - sysfs_attr_init(&dev_attr->attr); - - /* Configure device attribute */ - dev_attr->attr.name = kasprintf(GFP_KERNEL, "%s%s", parm->prefix, - parm->name); - if (!dev_attr->attr.name) { - err = -ENOMEM; - goto err_free_counter_attr; - } - if (parm->show) { - dev_attr->attr.mode |= 0444; - dev_attr->show = parm->show; - } - if (parm->store) { - dev_attr->attr.mode |= 0200; - dev_attr->store = parm->store; - } - - /* Store associated Counter component with attribute */ - counter_attr->component = parm->component; - - /* Keep track of the attribute for later cleanup */ - list_add(&counter_attr->l, attr_list); - parm->group->num_attr++; - - return 0; - -err_free_counter_attr: - kfree(counter_attr); - return err; -} - -#define to_counter_attr(_dev_attr) \ - container_of(_dev_attr, struct counter_device_attr, dev_attr) - -struct counter_signal_unit { - struct counter_signal *signal; -}; - -static const char *const counter_signal_level_str[] = { - [COUNTER_SIGNAL_LEVEL_LOW] = "low", - [COUNTER_SIGNAL_LEVEL_HIGH] = "high" -}; - -static ssize_t counter_signal_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct counter_device *const counter = dev_get_drvdata(dev); - const struct counter_device_attr *const devattr = to_counter_attr(attr); - const struct counter_signal_unit *const component = devattr->component; - struct counter_signal *const signal = component->signal; - int err; - enum counter_signal_level level; - - err = counter->ops->signal_read(counter, signal, &level); - if (err) - return err; - - return sprintf(buf, "%s\n", counter_signal_level_str[level]); -} - -struct counter_name_unit { - const char *name; -}; - -static ssize_t counter_device_attr_name_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - const struct counter_name_unit *const comp = to_counter_attr(attr)->component; - - return sprintf(buf, "%s\n", comp->name); -} - -static int counter_name_attribute_create( - struct counter_device_attr_group *const group, - const char *const name) -{ - struct counter_name_unit *name_comp; - struct counter_attr_parm parm; - int err; - - /* Skip if no name */ - if (!name) - return 0; - - /* Allocate name attribute component */ - name_comp = kmalloc(sizeof(*name_comp), GFP_KERNEL); - if (!name_comp) - return -ENOMEM; - name_comp->name = name; - - /* Allocate Signal name attribute */ - parm.group = group; - parm.prefix = ""; - parm.name = "name"; - parm.show = counter_device_attr_name_show; - parm.store = NULL; - parm.component = name_comp; - err = counter_attribute_create(&parm); - if (err) - goto err_free_name_comp; - - return 0; - -err_free_name_comp: - kfree(name_comp); - return err; -} - -struct counter_signal_ext_unit { - struct counter_signal *signal; - const struct counter_signal_ext *ext; -}; - -static ssize_t counter_signal_ext_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - const struct counter_device_attr *const devattr = to_counter_attr(attr); - const struct counter_signal_ext_unit *const comp = devattr->component; - const struct counter_signal_ext *const ext = comp->ext; - - return ext->read(dev_get_drvdata(dev), comp->signal, ext->priv, buf); -} - -static ssize_t counter_signal_ext_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - const struct counter_device_attr *const devattr = to_counter_attr(attr); - const struct counter_signal_ext_unit *const comp = devattr->component; - const struct counter_signal_ext *const ext = comp->ext; - - return ext->write(dev_get_drvdata(dev), comp->signal, ext->priv, buf, - len); -} - -static void counter_device_attr_list_free(struct list_head *attr_list) -{ - struct counter_device_attr *p, *n; - - list_for_each_entry_safe(p, n, attr_list, l) { - /* free attribute name and associated component memory */ - kfree(p->dev_attr.attr.name); - kfree(p->component); - list_del(&p->l); - kfree(p); - } -} - -static int counter_signal_ext_register( - struct counter_device_attr_group *const group, - struct counter_signal *const signal) -{ - const size_t num_ext = signal->num_ext; - size_t i; - const struct counter_signal_ext *ext; - struct counter_signal_ext_unit *signal_ext_comp; - struct counter_attr_parm parm; - int err; - - /* Create an attribute for each extension */ - for (i = 0 ; i < num_ext; i++) { - ext = signal->ext + i; - - /* Allocate signal_ext attribute component */ - signal_ext_comp = kmalloc(sizeof(*signal_ext_comp), GFP_KERNEL); - if (!signal_ext_comp) { - err = -ENOMEM; - goto err_free_attr_list; - } - signal_ext_comp->signal = signal; - signal_ext_comp->ext = ext; - - /* Allocate a Counter device attribute */ - parm.group = group; - parm.prefix = ""; - parm.name = ext->name; - parm.show = (ext->read) ? counter_signal_ext_show : NULL; - parm.store = (ext->write) ? counter_signal_ext_store : NULL; - parm.component = signal_ext_comp; - err = counter_attribute_create(&parm); - if (err) { - kfree(signal_ext_comp); - goto err_free_attr_list; - } - } - - return 0; - -err_free_attr_list: - counter_device_attr_list_free(&group->attr_list); - return err; -} - -static int counter_signal_attributes_create( - struct counter_device_attr_group *const group, - const struct counter_device *const counter, - struct counter_signal *const signal) -{ - struct counter_signal_unit *signal_comp; - struct counter_attr_parm parm; - int err; - - /* Allocate Signal attribute component */ - signal_comp = kmalloc(sizeof(*signal_comp), GFP_KERNEL); - if (!signal_comp) - return -ENOMEM; - signal_comp->signal = signal; - - /* Create main Signal attribute */ - parm.group = group; - parm.prefix = ""; - parm.name = "signal"; - parm.show = (counter->ops->signal_read) ? counter_signal_show : NULL; - parm.store = NULL; - parm.component = signal_comp; - err = counter_attribute_create(&parm); - if (err) { - kfree(signal_comp); - return err; - } - - /* Create Signal name attribute */ - err = counter_name_attribute_create(group, signal->name); - if (err) - goto err_free_attr_list; - - /* Register Signal extension attributes */ - err = counter_signal_ext_register(group, signal); - if (err) - goto err_free_attr_list; - - return 0; - -err_free_attr_list: - counter_device_attr_list_free(&group->attr_list); - return err; -} - -static int counter_signals_register( - struct counter_device_attr_group *const groups_list, - const struct counter_device *const counter) -{ - const size_t num_signals = counter->num_signals; - size_t i; - struct counter_signal *signal; - const char *name; - int err; - - /* Register each Signal */ - for (i = 0; i < num_signals; i++) { - signal = counter->signals + i; - - /* Generate Signal attribute directory name */ - name = kasprintf(GFP_KERNEL, "signal%d", signal->id); - if (!name) { - err = -ENOMEM; - goto err_free_attr_groups; - } - groups_list[i].attr_group.name = name; - - /* Create all attributes associated with Signal */ - err = counter_signal_attributes_create(groups_list + i, counter, - signal); - if (err) - goto err_free_attr_groups; - } - - return 0; - -err_free_attr_groups: - do { - kfree(groups_list[i].attr_group.name); - counter_device_attr_list_free(&groups_list[i].attr_list); - } while (i--); - return err; -} - -static const char *const counter_synapse_action_str[] = { - [COUNTER_SYNAPSE_ACTION_NONE] = "none", - [COUNTER_SYNAPSE_ACTION_RISING_EDGE] = "rising edge", - [COUNTER_SYNAPSE_ACTION_FALLING_EDGE] = "falling edge", - [COUNTER_SYNAPSE_ACTION_BOTH_EDGES] = "both edges" -}; - -struct counter_action_unit { - struct counter_synapse *synapse; - struct counter_count *count; -}; - -static ssize_t counter_action_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - const struct counter_device_attr *const devattr = to_counter_attr(attr); - int err; - struct counter_device *const counter = dev_get_drvdata(dev); - const struct counter_action_unit *const component = devattr->component; - struct counter_count *const count = component->count; - struct counter_synapse *const synapse = component->synapse; - size_t action_index; - enum counter_synapse_action action; - - err = counter->ops->action_get(counter, count, synapse, &action_index); - if (err) - return err; - - synapse->action = action_index; - - action = synapse->actions_list[action_index]; - return sprintf(buf, "%s\n", counter_synapse_action_str[action]); -} - -static ssize_t counter_action_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - const struct counter_device_attr *const devattr = to_counter_attr(attr); - const struct counter_action_unit *const component = devattr->component; - struct counter_synapse *const synapse = component->synapse; - size_t action_index; - const size_t num_actions = synapse->num_actions; - enum counter_synapse_action action; - int err; - struct counter_device *const counter = dev_get_drvdata(dev); - struct counter_count *const count = component->count; - - /* Find requested action mode */ - for (action_index = 0; action_index < num_actions; action_index++) { - action = synapse->actions_list[action_index]; - if (sysfs_streq(buf, counter_synapse_action_str[action])) - break; - } - /* If requested action mode not found */ - if (action_index >= num_actions) - return -EINVAL; - - err = counter->ops->action_set(counter, count, synapse, action_index); - if (err) - return err; - - synapse->action = action_index; - - return len; -} - -struct counter_action_avail_unit { - const enum counter_synapse_action *actions_list; - size_t num_actions; -}; - -static ssize_t counter_synapse_action_available_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - const struct counter_device_attr *const devattr = to_counter_attr(attr); - const struct counter_action_avail_unit *const component = devattr->component; - size_t i; - enum counter_synapse_action action; - ssize_t len = 0; - - for (i = 0; i < component->num_actions; i++) { - action = component->actions_list[i]; - len += sprintf(buf + len, "%s\n", - counter_synapse_action_str[action]); - } - - return len; -} - -static int counter_synapses_register( - struct counter_device_attr_group *const group, - const struct counter_device *const counter, - struct counter_count *const count, const char *const count_attr_name) -{ - size_t i; - struct counter_synapse *synapse; - const char *prefix; - struct counter_action_unit *action_comp; - struct counter_attr_parm parm; - int err; - struct counter_action_avail_unit *avail_comp; - - /* Register each Synapse */ - for (i = 0; i < count->num_synapses; i++) { - synapse = count->synapses + i; - - /* Generate attribute prefix */ - prefix = kasprintf(GFP_KERNEL, "signal%d_", - synapse->signal->id); - if (!prefix) { - err = -ENOMEM; - goto err_free_attr_list; - } - - /* Allocate action attribute component */ - action_comp = kmalloc(sizeof(*action_comp), GFP_KERNEL); - if (!action_comp) { - err = -ENOMEM; - goto err_free_prefix; - } - action_comp->synapse = synapse; - action_comp->count = count; - - /* Create action attribute */ - parm.group = group; - parm.prefix = prefix; - parm.name = "action"; - parm.show = (counter->ops->action_get) ? counter_action_show : NULL; - parm.store = (counter->ops->action_set) ? counter_action_store : NULL; - parm.component = action_comp; - err = counter_attribute_create(&parm); - if (err) { - kfree(action_comp); - goto err_free_prefix; - } - - /* Allocate action available attribute component */ - avail_comp = kmalloc(sizeof(*avail_comp), GFP_KERNEL); - if (!avail_comp) { - err = -ENOMEM; - goto err_free_prefix; - } - avail_comp->actions_list = synapse->actions_list; - avail_comp->num_actions = synapse->num_actions; - - /* Create action_available attribute */ - parm.group = group; - parm.prefix = prefix; - parm.name = "action_available"; - parm.show = counter_synapse_action_available_show; - parm.store = NULL; - parm.component = avail_comp; - err = counter_attribute_create(&parm); - if (err) { - kfree(avail_comp); - goto err_free_prefix; - } - - kfree(prefix); - } - - return 0; - -err_free_prefix: - kfree(prefix); -err_free_attr_list: - counter_device_attr_list_free(&group->attr_list); - return err; -} - -struct counter_count_unit { - struct counter_count *count; -}; - -static ssize_t counter_count_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct counter_device *const counter = dev_get_drvdata(dev); - const struct counter_device_attr *const devattr = to_counter_attr(attr); - const struct counter_count_unit *const component = devattr->component; - struct counter_count *const count = component->count; - int err; - unsigned long val; - - err = counter->ops->count_read(counter, count, &val); - if (err) - return err; - - return sprintf(buf, "%lu\n", val); -} - -static ssize_t counter_count_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct counter_device *const counter = dev_get_drvdata(dev); - const struct counter_device_attr *const devattr = to_counter_attr(attr); - const struct counter_count_unit *const component = devattr->component; - struct counter_count *const count = component->count; - int err; - unsigned long val; - - err = kstrtoul(buf, 0, &val); - if (err) - return err; - - err = counter->ops->count_write(counter, count, val); - if (err) - return err; - - return len; -} - -static const char *const counter_function_str[] = { - [COUNTER_FUNCTION_INCREASE] = "increase", - [COUNTER_FUNCTION_DECREASE] = "decrease", - [COUNTER_FUNCTION_PULSE_DIRECTION] = "pulse-direction", - [COUNTER_FUNCTION_QUADRATURE_X1_A] = "quadrature x1 a", - [COUNTER_FUNCTION_QUADRATURE_X1_B] = "quadrature x1 b", - [COUNTER_FUNCTION_QUADRATURE_X2_A] = "quadrature x2 a", - [COUNTER_FUNCTION_QUADRATURE_X2_B] = "quadrature x2 b", - [COUNTER_FUNCTION_QUADRATURE_X4] = "quadrature x4" -}; - -static ssize_t counter_function_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int err; - struct counter_device *const counter = dev_get_drvdata(dev); - const struct counter_device_attr *const devattr = to_counter_attr(attr); - const struct counter_count_unit *const component = devattr->component; - struct counter_count *const count = component->count; - size_t func_index; - enum counter_function function; - - err = counter->ops->function_get(counter, count, &func_index); - if (err) - return err; - - count->function = func_index; - - function = count->functions_list[func_index]; - return sprintf(buf, "%s\n", counter_function_str[function]); -} - -static ssize_t counter_function_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - const struct counter_device_attr *const devattr = to_counter_attr(attr); - const struct counter_count_unit *const component = devattr->component; - struct counter_count *const count = component->count; - const size_t num_functions = count->num_functions; - size_t func_index; - enum counter_function function; - int err; - struct counter_device *const counter = dev_get_drvdata(dev); - - /* Find requested Count function mode */ - for (func_index = 0; func_index < num_functions; func_index++) { - function = count->functions_list[func_index]; - if (sysfs_streq(buf, counter_function_str[function])) - break; - } - /* Return error if requested Count function mode not found */ - if (func_index >= num_functions) - return -EINVAL; - - err = counter->ops->function_set(counter, count, func_index); - if (err) - return err; - - count->function = func_index; - - return len; -} - -struct counter_count_ext_unit { - struct counter_count *count; - const struct counter_count_ext *ext; -}; - -static ssize_t counter_count_ext_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - const struct counter_device_attr *const devattr = to_counter_attr(attr); - const struct counter_count_ext_unit *const comp = devattr->component; - const struct counter_count_ext *const ext = comp->ext; - - return ext->read(dev_get_drvdata(dev), comp->count, ext->priv, buf); -} - -static ssize_t counter_count_ext_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - const struct counter_device_attr *const devattr = to_counter_attr(attr); - const struct counter_count_ext_unit *const comp = devattr->component; - const struct counter_count_ext *const ext = comp->ext; - - return ext->write(dev_get_drvdata(dev), comp->count, ext->priv, buf, - len); -} - -static int counter_count_ext_register( - struct counter_device_attr_group *const group, - struct counter_count *const count) -{ - size_t i; - const struct counter_count_ext *ext; - struct counter_count_ext_unit *count_ext_comp; - struct counter_attr_parm parm; - int err; - - /* Create an attribute for each extension */ - for (i = 0 ; i < count->num_ext; i++) { - ext = count->ext + i; - - /* Allocate count_ext attribute component */ - count_ext_comp = kmalloc(sizeof(*count_ext_comp), GFP_KERNEL); - if (!count_ext_comp) { - err = -ENOMEM; - goto err_free_attr_list; - } - count_ext_comp->count = count; - count_ext_comp->ext = ext; - - /* Allocate count_ext attribute */ - parm.group = group; - parm.prefix = ""; - parm.name = ext->name; - parm.show = (ext->read) ? counter_count_ext_show : NULL; - parm.store = (ext->write) ? counter_count_ext_store : NULL; - parm.component = count_ext_comp; - err = counter_attribute_create(&parm); - if (err) { - kfree(count_ext_comp); - goto err_free_attr_list; - } - } - - return 0; - -err_free_attr_list: - counter_device_attr_list_free(&group->attr_list); - return err; -} - -struct counter_func_avail_unit { - const enum counter_function *functions_list; - size_t num_functions; -}; - -static ssize_t counter_function_available_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - const struct counter_device_attr *const devattr = to_counter_attr(attr); - const struct counter_func_avail_unit *const component = devattr->component; - const enum counter_function *const func_list = component->functions_list; - const size_t num_functions = component->num_functions; - size_t i; - enum counter_function function; - ssize_t len = 0; - - for (i = 0; i < num_functions; i++) { - function = func_list[i]; - len += sprintf(buf + len, "%s\n", - counter_function_str[function]); - } - - return len; -} - -static int counter_count_attributes_create( - struct counter_device_attr_group *const group, - const struct counter_device *const counter, - struct counter_count *const count) -{ - struct counter_count_unit *count_comp; - struct counter_attr_parm parm; - int err; - struct counter_count_unit *func_comp; - struct counter_func_avail_unit *avail_comp; - - /* Allocate count attribute component */ - count_comp = kmalloc(sizeof(*count_comp), GFP_KERNEL); - if (!count_comp) - return -ENOMEM; - count_comp->count = count; - - /* Create main Count attribute */ - parm.group = group; - parm.prefix = ""; - parm.name = "count"; - parm.show = (counter->ops->count_read) ? counter_count_show : NULL; - parm.store = (counter->ops->count_write) ? counter_count_store : NULL; - parm.component = count_comp; - err = counter_attribute_create(&parm); - if (err) { - kfree(count_comp); - return err; - } - - /* Allocate function attribute component */ - func_comp = kmalloc(sizeof(*func_comp), GFP_KERNEL); - if (!func_comp) { - err = -ENOMEM; - goto err_free_attr_list; - } - func_comp->count = count; - - /* Create Count function attribute */ - parm.group = group; - parm.prefix = ""; - parm.name = "function"; - parm.show = (counter->ops->function_get) ? counter_function_show : NULL; - parm.store = (counter->ops->function_set) ? counter_function_store : NULL; - parm.component = func_comp; - err = counter_attribute_create(&parm); - if (err) { - kfree(func_comp); - goto err_free_attr_list; - } - - /* Allocate function available attribute component */ - avail_comp = kmalloc(sizeof(*avail_comp), GFP_KERNEL); - if (!avail_comp) { - err = -ENOMEM; - goto err_free_attr_list; - } - avail_comp->functions_list = count->functions_list; - avail_comp->num_functions = count->num_functions; - - /* Create Count function_available attribute */ - parm.group = group; - parm.prefix = ""; - parm.name = "function_available"; - parm.show = counter_function_available_show; - parm.store = NULL; - parm.component = avail_comp; - err = counter_attribute_create(&parm); - if (err) { - kfree(avail_comp); - goto err_free_attr_list; - } - - /* Create Count name attribute */ - err = counter_name_attribute_create(group, count->name); - if (err) - goto err_free_attr_list; - - /* Register Count extension attributes */ - err = counter_count_ext_register(group, count); - if (err) - goto err_free_attr_list; - - return 0; - -err_free_attr_list: - counter_device_attr_list_free(&group->attr_list); - return err; -} - -static int counter_counts_register( - struct counter_device_attr_group *const groups_list, - const struct counter_device *const counter) -{ - size_t i; - struct counter_count *count; - const char *name; - int err; - - /* Register each Count */ - for (i = 0; i < counter->num_counts; i++) { - count = counter->counts + i; - - /* Generate Count attribute directory name */ - name = kasprintf(GFP_KERNEL, "count%d", count->id); - if (!name) { - err = -ENOMEM; - goto err_free_attr_groups; - } - groups_list[i].attr_group.name = name; - - /* Register the Synapses associated with each Count */ - err = counter_synapses_register(groups_list + i, counter, count, - name); - if (err) - goto err_free_attr_groups; - - /* Create all attributes associated with Count */ - err = counter_count_attributes_create(groups_list + i, counter, - count); - if (err) - goto err_free_attr_groups; - } - - return 0; - -err_free_attr_groups: - do { - kfree(groups_list[i].attr_group.name); - counter_device_attr_list_free(&groups_list[i].attr_list); - } while (i--); - return err; -} - -struct counter_size_unit { - size_t size; -}; - -static ssize_t counter_device_attr_size_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - const struct counter_size_unit *const comp = to_counter_attr(attr)->component; - - return sprintf(buf, "%zu\n", comp->size); -} - -static int counter_size_attribute_create( - struct counter_device_attr_group *const group, - const size_t size, const char *const name) -{ - struct counter_size_unit *size_comp; - struct counter_attr_parm parm; - int err; - - /* Allocate size attribute component */ - size_comp = kmalloc(sizeof(*size_comp), GFP_KERNEL); - if (!size_comp) - return -ENOMEM; - size_comp->size = size; - - parm.group = group; - parm.prefix = ""; - parm.name = name; - parm.show = counter_device_attr_size_show; - parm.store = NULL; - parm.component = size_comp; - err = counter_attribute_create(&parm); - if (err) - goto err_free_size_comp; - - return 0; - -err_free_size_comp: - kfree(size_comp); - return err; -} - -struct counter_ext_unit { - const struct counter_device_ext *ext; -}; - -static ssize_t counter_device_ext_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - const struct counter_device_attr *const devattr = to_counter_attr(attr); - const struct counter_ext_unit *const component = devattr->component; - const struct counter_device_ext *const ext = component->ext; - - return ext->read(dev_get_drvdata(dev), ext->priv, buf); -} - -static ssize_t counter_device_ext_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - const struct counter_device_attr *const devattr = to_counter_attr(attr); - const struct counter_ext_unit *const component = devattr->component; - const struct counter_device_ext *const ext = component->ext; - - return ext->write(dev_get_drvdata(dev), ext->priv, buf, len); -} - -static int counter_device_ext_register( - struct counter_device_attr_group *const group, - struct counter_device *const counter) -{ - size_t i; - struct counter_ext_unit *ext_comp; - struct counter_attr_parm parm; - int err; - - /* Create an attribute for each extension */ - for (i = 0 ; i < counter->num_ext; i++) { - /* Allocate extension attribute component */ - ext_comp = kmalloc(sizeof(*ext_comp), GFP_KERNEL); - if (!ext_comp) { - err = -ENOMEM; - goto err_free_attr_list; - } - - ext_comp->ext = counter->ext + i; - - /* Allocate extension attribute */ - parm.group = group; - parm.prefix = ""; - parm.name = counter->ext[i].name; - parm.show = (counter->ext[i].read) ? counter_device_ext_show : NULL; - parm.store = (counter->ext[i].write) ? counter_device_ext_store : NULL; - parm.component = ext_comp; - err = counter_attribute_create(&parm); - if (err) { - kfree(ext_comp); - goto err_free_attr_list; - } - } - - return 0; - -err_free_attr_list: - counter_device_attr_list_free(&group->attr_list); - return err; -} - -static int counter_global_attr_register( - struct counter_device_attr_group *const group, - struct counter_device *const counter) -{ - int err; - - /* Create name attribute */ - err = counter_name_attribute_create(group, counter->name); - if (err) - return err; - - /* Create num_counts attribute */ - err = counter_size_attribute_create(group, counter->num_counts, - "num_counts"); - if (err) - goto err_free_attr_list; - - /* Create num_signals attribute */ - err = counter_size_attribute_create(group, counter->num_signals, - "num_signals"); - if (err) - goto err_free_attr_list; - - /* Register Counter device extension attributes */ - err = counter_device_ext_register(group, counter); - if (err) - goto err_free_attr_list; - - return 0; - -err_free_attr_list: - counter_device_attr_list_free(&group->attr_list); - return err; -} - -static void counter_device_groups_list_free( - struct counter_device_attr_group *const groups_list, - const size_t num_groups) -{ - struct counter_device_attr_group *group; - size_t i; - - /* loop through all attribute groups (signals, counts, global, etc.) */ - for (i = 0; i < num_groups; i++) { - group = groups_list + i; - - /* free all attribute group and associated attributes memory */ - kfree(group->attr_group.name); - kfree(group->attr_group.attrs); - counter_device_attr_list_free(&group->attr_list); - } - - kfree(groups_list); -} - -static int counter_device_groups_list_prepare( - struct counter_device *const counter) -{ - const size_t total_num_groups = - counter->num_signals + counter->num_counts + 1; - struct counter_device_attr_group *groups_list; - size_t i; - int err; - size_t num_groups = 0; - - /* Allocate space for attribute groups (signals, counts, and ext) */ - groups_list = kcalloc(total_num_groups, sizeof(*groups_list), - GFP_KERNEL); - if (!groups_list) - return -ENOMEM; - - /* Initialize attribute lists */ - for (i = 0; i < total_num_groups; i++) - INIT_LIST_HEAD(&groups_list[i].attr_list); - - /* Register Signals */ - err = counter_signals_register(groups_list, counter); - if (err) - goto err_free_groups_list; - num_groups += counter->num_signals; - - /* Register Counts and respective Synapses */ - err = counter_counts_register(groups_list + num_groups, counter); - if (err) - goto err_free_groups_list; - num_groups += counter->num_counts; - - /* Register Counter global attributes */ - err = counter_global_attr_register(groups_list + num_groups, counter); - if (err) - goto err_free_groups_list; - num_groups++; - - /* Store groups_list in device_state */ - counter->device_state->groups_list = groups_list; - counter->device_state->num_groups = num_groups; - - return 0; - -err_free_groups_list: - counter_device_groups_list_free(groups_list, num_groups); - return err; -} - -static int counter_device_groups_prepare( - struct counter_device_state *const device_state) -{ - size_t i, j; - struct counter_device_attr_group *group; - int err; - struct counter_device_attr *p; - - /* Allocate attribute groups for association with device */ - device_state->groups = kcalloc(device_state->num_groups + 1, - sizeof(*device_state->groups), - GFP_KERNEL); - if (!device_state->groups) - return -ENOMEM; - - /* Prepare each group of attributes for association */ - for (i = 0; i < device_state->num_groups; i++) { - group = device_state->groups_list + i; - - /* Allocate space for attribute pointers in attribute group */ - group->attr_group.attrs = kcalloc(group->num_attr + 1, - sizeof(*group->attr_group.attrs), GFP_KERNEL); - if (!group->attr_group.attrs) { - err = -ENOMEM; - goto err_free_groups; - } - - /* Add attribute pointers to attribute group */ - j = 0; - list_for_each_entry(p, &group->attr_list, l) - group->attr_group.attrs[j++] = &p->dev_attr.attr; - - /* Group attributes in attribute group */ - device_state->groups[i] = &group->attr_group; - } - /* Associate attributes with device */ - device_state->dev.groups = device_state->groups; - - return 0; - -err_free_groups: - do { - group = device_state->groups_list + i; - kfree(group->attr_group.attrs); - group->attr_group.attrs = NULL; - } while (i--); - kfree(device_state->groups); - return err; -} - -/* Provides a unique ID for each counter device */ -static DEFINE_IDA(counter_ida); - -static void counter_device_release(struct device *dev) -{ - struct counter_device *const counter = dev_get_drvdata(dev); - struct counter_device_state *const device_state = counter->device_state; - - kfree(device_state->groups); - counter_device_groups_list_free(device_state->groups_list, - device_state->num_groups); - ida_simple_remove(&counter_ida, device_state->id); - kfree(device_state); -} - -static struct device_type counter_device_type = { - .name = "counter_device", - .release = counter_device_release -}; - -static struct bus_type counter_bus_type = { - .name = "counter" -}; - -/** - * counter_register - register Counter to the system - * @counter: pointer to Counter to register - * - * This function registers a Counter to the system. A sysfs "counter" directory - * will be created and populated with sysfs attributes correlating with the - * Counter Signals, Synapses, and Counts respectively. - */ -int counter_register(struct counter_device *const counter) -{ - struct counter_device_state *device_state; - int err; - - /* Allocate internal state container for Counter device */ - device_state = kzalloc(sizeof(*device_state), GFP_KERNEL); - if (!device_state) - return -ENOMEM; - counter->device_state = device_state; - - /* Acquire unique ID */ - device_state->id = ida_simple_get(&counter_ida, 0, 0, GFP_KERNEL); - if (device_state->id < 0) { - err = device_state->id; - goto err_free_device_state; - } - - /* Configure device structure for Counter */ - device_state->dev.type = &counter_device_type; - device_state->dev.bus = &counter_bus_type; - if (counter->parent) { - device_state->dev.parent = counter->parent; - device_state->dev.of_node = counter->parent->of_node; - } - dev_set_name(&device_state->dev, "counter%d", device_state->id); - device_initialize(&device_state->dev); - dev_set_drvdata(&device_state->dev, counter); - - /* Prepare device attributes */ - err = counter_device_groups_list_prepare(counter); - if (err) - goto err_free_id; - - /* Organize device attributes to groups and match to device */ - err = counter_device_groups_prepare(device_state); - if (err) - goto err_free_groups_list; - - /* Add device to system */ - err = device_add(&device_state->dev); - if (err) - goto err_free_groups; - - return 0; - -err_free_groups: - kfree(device_state->groups); -err_free_groups_list: - counter_device_groups_list_free(device_state->groups_list, - device_state->num_groups); -err_free_id: - ida_simple_remove(&counter_ida, device_state->id); -err_free_device_state: - kfree(device_state); - return err; -} -EXPORT_SYMBOL_GPL(counter_register); - -/** - * counter_unregister - unregister Counter from the system - * @counter: pointer to Counter to unregister - * - * The Counter is unregistered from the system; all allocated memory is freed. - */ -void counter_unregister(struct counter_device *const counter) -{ - if (counter) - device_del(&counter->device_state->dev); -} -EXPORT_SYMBOL_GPL(counter_unregister); - -static void devm_counter_unreg(struct device *dev, void *res) -{ - counter_unregister(*(struct counter_device **)res); -} - -/** - * devm_counter_register - Resource-managed counter_register - * @dev: device to allocate counter_device for - * @counter: pointer to Counter to register - * - * Managed counter_register. The Counter registered with this function is - * automatically unregistered on driver detach. This function calls - * counter_register internally. Refer to that function for more information. - * - * If an Counter registered with this function needs to be unregistered - * separately, devm_counter_unregister must be used. - * - * RETURNS: - * 0 on success, negative error number on failure. - */ -int devm_counter_register(struct device *dev, - struct counter_device *const counter) -{ - struct counter_device **ptr; - int ret; - - ptr = devres_alloc(devm_counter_unreg, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return -ENOMEM; - - ret = counter_register(counter); - if (!ret) { - *ptr = counter; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } - - return ret; -} -EXPORT_SYMBOL_GPL(devm_counter_register); - -static int devm_counter_match(struct device *dev, void *res, void *data) -{ - struct counter_device **r = res; - - if (!r || !*r) { - WARN_ON(!r || !*r); - return 0; - } - - return *r == data; -} - -/** - * devm_counter_unregister - Resource-managed counter_unregister - * @dev: device this counter_device belongs to - * @counter: pointer to Counter associated with the device - * - * Unregister Counter registered with devm_counter_register. - */ -void devm_counter_unregister(struct device *dev, - struct counter_device *const counter) -{ - int rc; - - rc = devres_release(dev, devm_counter_unreg, devm_counter_match, - counter); - WARN_ON(rc); -} -EXPORT_SYMBOL_GPL(devm_counter_unregister); - -static int __init counter_init(void) -{ - return bus_register(&counter_bus_type); -} - -static void __exit counter_exit(void) -{ - bus_unregister(&counter_bus_type); -} - -subsys_initcall(counter_init); -module_exit(counter_exit); - -MODULE_AUTHOR("William Breathitt Gray "); -MODULE_DESCRIPTION("Generic Counter interface"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/counter/ftm-quaddec.c b/drivers/counter/ftm-quaddec.c index 53c15f84909b..5ef0478709cd 100644 --- a/drivers/counter/ftm-quaddec.c +++ b/drivers/counter/ftm-quaddec.c @@ -14,6 +14,7 @@ #include #include #include +#include #define FTM_FIELD_UPDATE(ftm, offset, mask, val) \ ({ \ @@ -115,8 +116,7 @@ static void ftm_quaddec_disable(void *ftm) } static int ftm_quaddec_get_prescaler(struct counter_device *counter, - struct counter_count *count, - size_t *cnt_mode) + struct counter_count *count, u32 *cnt_mode) { struct ftm_quaddec *ftm = counter->priv; uint32_t scflags; @@ -129,8 +129,7 @@ static int ftm_quaddec_get_prescaler(struct counter_device *counter, } static int ftm_quaddec_set_prescaler(struct counter_device *counter, - struct counter_count *count, - size_t cnt_mode) + struct counter_count *count, u32 cnt_mode) { struct ftm_quaddec *ftm = counter->priv; @@ -151,33 +150,17 @@ static const char * const ftm_quaddec_prescaler[] = { "1", "2", "4", "8", "16", "32", "64", "128" }; -static struct counter_count_enum_ext ftm_quaddec_prescaler_enum = { - .items = ftm_quaddec_prescaler, - .num_items = ARRAY_SIZE(ftm_quaddec_prescaler), - .get = ftm_quaddec_get_prescaler, - .set = ftm_quaddec_set_prescaler -}; - -enum ftm_quaddec_synapse_action { - FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES, -}; - static const enum counter_synapse_action ftm_quaddec_synapse_actions[] = { - [FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES }; -enum ftm_quaddec_count_function { - FTM_QUADDEC_COUNT_ENCODER_MODE_1, -}; - static const enum counter_function ftm_quaddec_count_functions[] = { - [FTM_QUADDEC_COUNT_ENCODER_MODE_1] = COUNTER_FUNCTION_QUADRATURE_X4 + COUNTER_FUNCTION_QUADRATURE_X4 }; static int ftm_quaddec_count_read(struct counter_device *counter, struct counter_count *count, - unsigned long *val) + u64 *val) { struct ftm_quaddec *const ftm = counter->priv; uint32_t cntval; @@ -191,7 +174,7 @@ static int ftm_quaddec_count_read(struct counter_device *counter, static int ftm_quaddec_count_write(struct counter_device *counter, struct counter_count *count, - const unsigned long val) + const u64 val) { struct ftm_quaddec *const ftm = counter->priv; @@ -205,21 +188,21 @@ static int ftm_quaddec_count_write(struct counter_device *counter, return 0; } -static int ftm_quaddec_count_function_get(struct counter_device *counter, - struct counter_count *count, - size_t *function) +static int ftm_quaddec_count_function_read(struct counter_device *counter, + struct counter_count *count, + enum counter_function *function) { - *function = FTM_QUADDEC_COUNT_ENCODER_MODE_1; + *function = COUNTER_FUNCTION_QUADRATURE_X4; return 0; } -static int ftm_quaddec_action_get(struct counter_device *counter, - struct counter_count *count, - struct counter_synapse *synapse, - size_t *action) +static int ftm_quaddec_action_read(struct counter_device *counter, + struct counter_count *count, + struct counter_synapse *synapse, + enum counter_synapse_action *action) { - *action = FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES; + *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; return 0; } @@ -227,8 +210,8 @@ static int ftm_quaddec_action_get(struct counter_device *counter, static const struct counter_ops ftm_quaddec_cnt_ops = { .count_read = ftm_quaddec_count_read, .count_write = ftm_quaddec_count_write, - .function_get = ftm_quaddec_count_function_get, - .action_get = ftm_quaddec_action_get, + .function_read = ftm_quaddec_count_function_read, + .action_read = ftm_quaddec_action_read, }; static struct counter_signal ftm_quaddec_signals[] = { @@ -255,9 +238,12 @@ static struct counter_synapse ftm_quaddec_count_synapses[] = { } }; -static const struct counter_count_ext ftm_quaddec_count_ext[] = { - COUNTER_COUNT_ENUM("prescaler", &ftm_quaddec_prescaler_enum), - COUNTER_COUNT_ENUM_AVAILABLE("prescaler", &ftm_quaddec_prescaler_enum), +static DEFINE_COUNTER_ENUM(ftm_quaddec_prescaler_enum, ftm_quaddec_prescaler); + +static struct counter_comp ftm_quaddec_count_ext[] = { + COUNTER_COMP_COUNT_ENUM("prescaler", ftm_quaddec_get_prescaler, + ftm_quaddec_set_prescaler, + ftm_quaddec_prescaler_enum), }; static struct counter_count ftm_quaddec_counts = { diff --git a/drivers/counter/intel-qep.c b/drivers/counter/intel-qep.c index 8a6847d5fb2b..0924d16de6e2 100644 --- a/drivers/counter/intel-qep.c +++ b/drivers/counter/intel-qep.c @@ -62,13 +62,6 @@ #define INTEL_QEP_CLK_PERIOD_NS 10 -#define INTEL_QEP_COUNTER_EXT_RW(_name) \ -{ \ - .name = #_name, \ - .read = _name##_read, \ - .write = _name##_write, \ -} - struct intel_qep { struct counter_device counter; struct mutex lock; @@ -114,8 +107,7 @@ static void intel_qep_init(struct intel_qep *qep) } static int intel_qep_count_read(struct counter_device *counter, - struct counter_count *count, - unsigned long *val) + struct counter_count *count, u64 *val) { struct intel_qep *const qep = counter->priv; @@ -130,11 +122,11 @@ static const enum counter_function intel_qep_count_functions[] = { COUNTER_FUNCTION_QUADRATURE_X4, }; -static int intel_qep_function_get(struct counter_device *counter, - struct counter_count *count, - size_t *function) +static int intel_qep_function_read(struct counter_device *counter, + struct counter_count *count, + enum counter_function *function) { - *function = 0; + *function = COUNTER_FUNCTION_QUADRATURE_X4; return 0; } @@ -143,19 +135,19 @@ static const enum counter_synapse_action intel_qep_synapse_actions[] = { COUNTER_SYNAPSE_ACTION_BOTH_EDGES, }; -static int intel_qep_action_get(struct counter_device *counter, - struct counter_count *count, - struct counter_synapse *synapse, - size_t *action) +static int intel_qep_action_read(struct counter_device *counter, + struct counter_count *count, + struct counter_synapse *synapse, + enum counter_synapse_action *action) { - *action = 0; + *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; return 0; } static const struct counter_ops intel_qep_counter_ops = { .count_read = intel_qep_count_read, - .function_get = intel_qep_function_get, - .action_get = intel_qep_action_get, + .function_read = intel_qep_function_read, + .action_read = intel_qep_action_read, }; #define INTEL_QEP_SIGNAL(_id, _name) { \ @@ -181,31 +173,27 @@ static struct counter_synapse intel_qep_count_synapses[] = { INTEL_QEP_SYNAPSE(2), }; -static ssize_t ceiling_read(struct counter_device *counter, - struct counter_count *count, - void *priv, char *buf) +static int intel_qep_ceiling_read(struct counter_device *counter, + struct counter_count *count, u64 *ceiling) { struct intel_qep *qep = counter->priv; - u32 reg; pm_runtime_get_sync(qep->dev); - reg = intel_qep_readl(qep, INTEL_QEPMAX); + *ceiling = intel_qep_readl(qep, INTEL_QEPMAX); pm_runtime_put(qep->dev); - return sysfs_emit(buf, "%u\n", reg); + return 0; } -static ssize_t ceiling_write(struct counter_device *counter, - struct counter_count *count, - void *priv, const char *buf, size_t len) +static int intel_qep_ceiling_write(struct counter_device *counter, + struct counter_count *count, u64 max) { struct intel_qep *qep = counter->priv; - u32 max; - int ret; + int ret = 0; - ret = kstrtou32(buf, 0, &max); - if (ret < 0) - return ret; + /* Intel QEP ceiling configuration only supports 32-bit values */ + if (max != (u32)max) + return -ERANGE; mutex_lock(&qep->lock); if (qep->enabled) { @@ -216,34 +204,28 @@ static ssize_t ceiling_write(struct counter_device *counter, pm_runtime_get_sync(qep->dev); intel_qep_writel(qep, INTEL_QEPMAX, max); pm_runtime_put(qep->dev); - ret = len; out: mutex_unlock(&qep->lock); return ret; } -static ssize_t enable_read(struct counter_device *counter, - struct counter_count *count, - void *priv, char *buf) +static int intel_qep_enable_read(struct counter_device *counter, + struct counter_count *count, u8 *enable) { struct intel_qep *qep = counter->priv; - return sysfs_emit(buf, "%u\n", qep->enabled); + *enable = qep->enabled; + + return 0; } -static ssize_t enable_write(struct counter_device *counter, - struct counter_count *count, - void *priv, const char *buf, size_t len) +static int intel_qep_enable_write(struct counter_device *counter, + struct counter_count *count, u8 val) { struct intel_qep *qep = counter->priv; u32 reg; - bool val, changed; - int ret; - - ret = kstrtobool(buf, &val); - if (ret) - return ret; + bool changed; mutex_lock(&qep->lock); changed = val ^ qep->enabled; @@ -267,12 +249,12 @@ static ssize_t enable_write(struct counter_device *counter, out: mutex_unlock(&qep->lock); - return len; + return 0; } -static ssize_t spike_filter_ns_read(struct counter_device *counter, - struct counter_count *count, - void *priv, char *buf) +static int intel_qep_spike_filter_ns_read(struct counter_device *counter, + struct counter_count *count, + u64 *length) { struct intel_qep *qep = counter->priv; u32 reg; @@ -281,33 +263,31 @@ static ssize_t spike_filter_ns_read(struct counter_device *counter, reg = intel_qep_readl(qep, INTEL_QEPCON); if (!(reg & INTEL_QEPCON_FLT_EN)) { pm_runtime_put(qep->dev); - return sysfs_emit(buf, "0\n"); + return 0; } reg = INTEL_QEPFLT_MAX_COUNT(intel_qep_readl(qep, INTEL_QEPFLT)); pm_runtime_put(qep->dev); - return sysfs_emit(buf, "%u\n", (reg + 2) * INTEL_QEP_CLK_PERIOD_NS); + *length = (reg + 2) * INTEL_QEP_CLK_PERIOD_NS; + + return 0; } -static ssize_t spike_filter_ns_write(struct counter_device *counter, - struct counter_count *count, - void *priv, const char *buf, size_t len) +static int intel_qep_spike_filter_ns_write(struct counter_device *counter, + struct counter_count *count, + u64 length) { struct intel_qep *qep = counter->priv; - u32 reg, length; + u32 reg; bool enable; - int ret; - - ret = kstrtou32(buf, 0, &length); - if (ret < 0) - return ret; + int ret = 0; /* * Spike filter length is (MAX_COUNT + 2) clock periods. * Disable filter when userspace writes 0, enable for valid * nanoseconds values and error out otherwise. */ - length /= INTEL_QEP_CLK_PERIOD_NS; + do_div(length, INTEL_QEP_CLK_PERIOD_NS); if (length == 0) { enable = false; length = 0; @@ -336,16 +316,15 @@ static ssize_t spike_filter_ns_write(struct counter_device *counter, intel_qep_writel(qep, INTEL_QEPFLT, length); intel_qep_writel(qep, INTEL_QEPCON, reg); pm_runtime_put(qep->dev); - ret = len; out: mutex_unlock(&qep->lock); return ret; } -static ssize_t preset_enable_read(struct counter_device *counter, - struct counter_count *count, - void *priv, char *buf) +static int intel_qep_preset_enable_read(struct counter_device *counter, + struct counter_count *count, + u8 *preset_enable) { struct intel_qep *qep = counter->priv; u32 reg; @@ -353,21 +332,18 @@ static ssize_t preset_enable_read(struct counter_device *counter, pm_runtime_get_sync(qep->dev); reg = intel_qep_readl(qep, INTEL_QEPCON); pm_runtime_put(qep->dev); - return sysfs_emit(buf, "%u\n", !(reg & INTEL_QEPCON_COUNT_RST_MODE)); + + *preset_enable = !(reg & INTEL_QEPCON_COUNT_RST_MODE); + + return 0; } -static ssize_t preset_enable_write(struct counter_device *counter, - struct counter_count *count, - void *priv, const char *buf, size_t len) +static int intel_qep_preset_enable_write(struct counter_device *counter, + struct counter_count *count, u8 val) { struct intel_qep *qep = counter->priv; u32 reg; - bool val; - int ret; - - ret = kstrtobool(buf, &val); - if (ret) - return ret; + int ret = 0; mutex_lock(&qep->lock); if (qep->enabled) { @@ -384,7 +360,6 @@ static ssize_t preset_enable_write(struct counter_device *counter, intel_qep_writel(qep, INTEL_QEPCON, reg); pm_runtime_put(qep->dev); - ret = len; out: mutex_unlock(&qep->lock); @@ -392,11 +367,14 @@ out: return ret; } -static const struct counter_count_ext intel_qep_count_ext[] = { - INTEL_QEP_COUNTER_EXT_RW(ceiling), - INTEL_QEP_COUNTER_EXT_RW(enable), - INTEL_QEP_COUNTER_EXT_RW(spike_filter_ns), - INTEL_QEP_COUNTER_EXT_RW(preset_enable) +static struct counter_comp intel_qep_count_ext[] = { + COUNTER_COMP_ENABLE(intel_qep_enable_read, intel_qep_enable_write), + COUNTER_COMP_CEILING(intel_qep_ceiling_read, intel_qep_ceiling_write), + COUNTER_COMP_PRESET_ENABLE(intel_qep_preset_enable_read, + intel_qep_preset_enable_write), + COUNTER_COMP_COUNT_U64("spike_filter_ns", + intel_qep_spike_filter_ns_read, + intel_qep_spike_filter_ns_write), }; static struct counter_count intel_qep_counter_count[] = { diff --git a/drivers/counter/interrupt-cnt.c b/drivers/counter/interrupt-cnt.c index 1de4243db488..8514a87fcbee 100644 --- a/drivers/counter/interrupt-cnt.c +++ b/drivers/counter/interrupt-cnt.c @@ -10,6 +10,7 @@ #include #include #include +#include #define INTERRUPT_CNT_NAME "interrupt-cnt" @@ -33,30 +34,23 @@ static irqreturn_t interrupt_cnt_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static ssize_t interrupt_cnt_enable_read(struct counter_device *counter, - struct counter_count *count, - void *private, char *buf) +static int interrupt_cnt_enable_read(struct counter_device *counter, + struct counter_count *count, u8 *enable) { struct interrupt_cnt_priv *priv = counter->priv; - return sysfs_emit(buf, "%d\n", priv->enabled); + *enable = priv->enabled; + + return 0; } -static ssize_t interrupt_cnt_enable_write(struct counter_device *counter, - struct counter_count *count, - void *private, const char *buf, - size_t len) +static int interrupt_cnt_enable_write(struct counter_device *counter, + struct counter_count *count, u8 enable) { struct interrupt_cnt_priv *priv = counter->priv; - bool enable; - ssize_t ret; - - ret = kstrtobool(buf, &enable); - if (ret) - return ret; if (priv->enabled == enable) - return len; + return 0; if (enable) { priv->enabled = true; @@ -66,33 +60,30 @@ static ssize_t interrupt_cnt_enable_write(struct counter_device *counter, priv->enabled = false; } - return len; + return 0; } -static const struct counter_count_ext interrupt_cnt_ext[] = { - { - .name = "enable", - .read = interrupt_cnt_enable_read, - .write = interrupt_cnt_enable_write, - }, +static struct counter_comp interrupt_cnt_ext[] = { + COUNTER_COMP_ENABLE(interrupt_cnt_enable_read, + interrupt_cnt_enable_write), }; static const enum counter_synapse_action interrupt_cnt_synapse_actions[] = { COUNTER_SYNAPSE_ACTION_RISING_EDGE, }; -static int interrupt_cnt_action_get(struct counter_device *counter, - struct counter_count *count, - struct counter_synapse *synapse, - size_t *action) +static int interrupt_cnt_action_read(struct counter_device *counter, + struct counter_count *count, + struct counter_synapse *synapse, + enum counter_synapse_action *action) { - *action = 0; + *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE; return 0; } static int interrupt_cnt_read(struct counter_device *counter, - struct counter_count *count, unsigned long *val) + struct counter_count *count, u64 *val) { struct interrupt_cnt_priv *priv = counter->priv; @@ -102,8 +93,7 @@ static int interrupt_cnt_read(struct counter_device *counter, } static int interrupt_cnt_write(struct counter_device *counter, - struct counter_count *count, - const unsigned long val) + struct counter_count *count, const u64 val) { struct interrupt_cnt_priv *priv = counter->priv; @@ -119,11 +109,11 @@ static const enum counter_function interrupt_cnt_functions[] = { COUNTER_FUNCTION_INCREASE, }; -static int interrupt_cnt_function_get(struct counter_device *counter, - struct counter_count *count, - size_t *function) +static int interrupt_cnt_function_read(struct counter_device *counter, + struct counter_count *count, + enum counter_function *function) { - *function = 0; + *function = COUNTER_FUNCTION_INCREASE; return 0; } @@ -148,10 +138,10 @@ static int interrupt_cnt_signal_read(struct counter_device *counter, } static const struct counter_ops interrupt_cnt_ops = { - .action_get = interrupt_cnt_action_get, + .action_read = interrupt_cnt_action_read, .count_read = interrupt_cnt_read, .count_write = interrupt_cnt_write, - .function_get = interrupt_cnt_function_get, + .function_read = interrupt_cnt_function_read, .signal_read = interrupt_cnt_signal_read, }; diff --git a/drivers/counter/microchip-tcb-capture.c b/drivers/counter/microchip-tcb-capture.c index 1aa70b9c4833..79e0c84a3b81 100644 --- a/drivers/counter/microchip-tcb-capture.c +++ b/drivers/counter/microchip-tcb-capture.c @@ -32,28 +32,16 @@ struct mchp_tc_data { bool trig_inverted; }; -enum mchp_tc_count_function { - MCHP_TC_FUNCTION_INCREASE, - MCHP_TC_FUNCTION_QUADRATURE, -}; - static const enum counter_function mchp_tc_count_functions[] = { - [MCHP_TC_FUNCTION_INCREASE] = COUNTER_FUNCTION_INCREASE, - [MCHP_TC_FUNCTION_QUADRATURE] = COUNTER_FUNCTION_QUADRATURE_X4, -}; - -enum mchp_tc_synapse_action { - MCHP_TC_SYNAPSE_ACTION_NONE = 0, - MCHP_TC_SYNAPSE_ACTION_RISING_EDGE, - MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE, - MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE + COUNTER_FUNCTION_INCREASE, + COUNTER_FUNCTION_QUADRATURE_X4, }; static const enum counter_synapse_action mchp_tc_synapse_actions[] = { - [MCHP_TC_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE, - [MCHP_TC_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE, - [MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE] = COUNTER_SYNAPSE_ACTION_FALLING_EDGE, - [MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES, + COUNTER_SYNAPSE_ACTION_NONE, + COUNTER_SYNAPSE_ACTION_RISING_EDGE, + COUNTER_SYNAPSE_ACTION_FALLING_EDGE, + COUNTER_SYNAPSE_ACTION_BOTH_EDGES, }; static struct counter_signal mchp_tc_count_signals[] = { @@ -80,23 +68,23 @@ static struct counter_synapse mchp_tc_count_synapses[] = { } }; -static int mchp_tc_count_function_get(struct counter_device *counter, - struct counter_count *count, - size_t *function) +static int mchp_tc_count_function_read(struct counter_device *counter, + struct counter_count *count, + enum counter_function *function) { struct mchp_tc_data *const priv = counter->priv; if (priv->qdec_mode) - *function = MCHP_TC_FUNCTION_QUADRATURE; + *function = COUNTER_FUNCTION_QUADRATURE_X4; else - *function = MCHP_TC_FUNCTION_INCREASE; + *function = COUNTER_FUNCTION_INCREASE; return 0; } -static int mchp_tc_count_function_set(struct counter_device *counter, - struct counter_count *count, - size_t function) +static int mchp_tc_count_function_write(struct counter_device *counter, + struct counter_count *count, + enum counter_function function) { struct mchp_tc_data *const priv = counter->priv; u32 bmr, cmr; @@ -108,7 +96,7 @@ static int mchp_tc_count_function_set(struct counter_device *counter, cmr &= ~ATMEL_TC_WAVE; switch (function) { - case MCHP_TC_FUNCTION_INCREASE: + case COUNTER_FUNCTION_INCREASE: priv->qdec_mode = 0; /* Set highest rate based on whether soc has gclk or not */ bmr &= ~(ATMEL_TC_QDEN | ATMEL_TC_POSEN); @@ -120,7 +108,7 @@ static int mchp_tc_count_function_set(struct counter_device *counter, cmr |= ATMEL_TC_CMR_MASK; cmr &= ~(ATMEL_TC_ABETRG | ATMEL_TC_XC0); break; - case MCHP_TC_FUNCTION_QUADRATURE: + case COUNTER_FUNCTION_QUADRATURE_X4: if (!priv->tc_cfg->has_qdec) return -EINVAL; /* In QDEC mode settings both channels 0 and 1 are required */ @@ -176,10 +164,10 @@ static int mchp_tc_count_signal_read(struct counter_device *counter, return 0; } -static int mchp_tc_count_action_get(struct counter_device *counter, - struct counter_count *count, - struct counter_synapse *synapse, - size_t *action) +static int mchp_tc_count_action_read(struct counter_device *counter, + struct counter_count *count, + struct counter_synapse *synapse, + enum counter_synapse_action *action) { struct mchp_tc_data *const priv = counter->priv; u32 cmr; @@ -188,26 +176,26 @@ static int mchp_tc_count_action_get(struct counter_device *counter, switch (cmr & ATMEL_TC_ETRGEDG) { default: - *action = MCHP_TC_SYNAPSE_ACTION_NONE; + *action = COUNTER_SYNAPSE_ACTION_NONE; break; case ATMEL_TC_ETRGEDG_RISING: - *action = MCHP_TC_SYNAPSE_ACTION_RISING_EDGE; + *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE; break; case ATMEL_TC_ETRGEDG_FALLING: - *action = MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE; + *action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE; break; case ATMEL_TC_ETRGEDG_BOTH: - *action = MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE; + *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; break; } return 0; } -static int mchp_tc_count_action_set(struct counter_device *counter, - struct counter_count *count, - struct counter_synapse *synapse, - size_t action) +static int mchp_tc_count_action_write(struct counter_device *counter, + struct counter_count *count, + struct counter_synapse *synapse, + enum counter_synapse_action action) { struct mchp_tc_data *const priv = counter->priv; u32 edge = ATMEL_TC_ETRGEDG_NONE; @@ -217,16 +205,16 @@ static int mchp_tc_count_action_set(struct counter_device *counter, return -EINVAL; switch (action) { - case MCHP_TC_SYNAPSE_ACTION_NONE: + case COUNTER_SYNAPSE_ACTION_NONE: edge = ATMEL_TC_ETRGEDG_NONE; break; - case MCHP_TC_SYNAPSE_ACTION_RISING_EDGE: + case COUNTER_SYNAPSE_ACTION_RISING_EDGE: edge = ATMEL_TC_ETRGEDG_RISING; break; - case MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE: + case COUNTER_SYNAPSE_ACTION_FALLING_EDGE: edge = ATMEL_TC_ETRGEDG_FALLING; break; - case MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE: + case COUNTER_SYNAPSE_ACTION_BOTH_EDGES: edge = ATMEL_TC_ETRGEDG_BOTH; break; default: @@ -240,8 +228,7 @@ static int mchp_tc_count_action_set(struct counter_device *counter, } static int mchp_tc_count_read(struct counter_device *counter, - struct counter_count *count, - unsigned long *val) + struct counter_count *count, u64 *val) { struct mchp_tc_data *const priv = counter->priv; u32 cnt; @@ -264,12 +251,12 @@ static struct counter_count mchp_tc_counts[] = { }; static const struct counter_ops mchp_tc_ops = { - .signal_read = mchp_tc_count_signal_read, - .count_read = mchp_tc_count_read, - .function_get = mchp_tc_count_function_get, - .function_set = mchp_tc_count_function_set, - .action_get = mchp_tc_count_action_get, - .action_set = mchp_tc_count_action_set + .signal_read = mchp_tc_count_signal_read, + .count_read = mchp_tc_count_read, + .function_read = mchp_tc_count_function_read, + .function_write = mchp_tc_count_function_write, + .action_read = mchp_tc_count_action_read, + .action_write = mchp_tc_count_action_write }; static const struct atmel_tcb_config tcb_rm9200_config = { diff --git a/drivers/counter/stm32-lptimer-cnt.c b/drivers/counter/stm32-lptimer-cnt.c index 7367f46c6f91..5168833b1fdf 100644 --- a/drivers/counter/stm32-lptimer-cnt.c +++ b/drivers/counter/stm32-lptimer-cnt.c @@ -17,6 +17,7 @@ #include #include #include +#include struct stm32_lptim_cnt { struct counter_device counter; @@ -107,11 +108,7 @@ static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable) return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val); } -/** - * enum stm32_lptim_cnt_function - enumerates LPTimer counter & encoder modes - * @STM32_LPTIM_COUNTER_INCREASE: up count on IN1 rising, falling or both edges - * @STM32_LPTIM_ENCODER_BOTH_EDGE: count on both edges (IN1 & IN2 quadrature) - * +/* * In non-quadrature mode, device counts up on active edge. * In quadrature mode, encoder counting scenarios are as follows: * +---------+----------+--------------------+--------------------+ @@ -129,33 +126,20 @@ static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable) * | edges | Low -> | Up | Down | Down | Up | * +---------+----------+----------+---------+----------+---------+ */ -enum stm32_lptim_cnt_function { - STM32_LPTIM_COUNTER_INCREASE, - STM32_LPTIM_ENCODER_BOTH_EDGE, -}; - static const enum counter_function stm32_lptim_cnt_functions[] = { - [STM32_LPTIM_COUNTER_INCREASE] = COUNTER_FUNCTION_INCREASE, - [STM32_LPTIM_ENCODER_BOTH_EDGE] = COUNTER_FUNCTION_QUADRATURE_X4, -}; - -enum stm32_lptim_synapse_action { - STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE = STM32_LPTIM_CKPOL_RISING_EDGE, - STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE = STM32_LPTIM_CKPOL_FALLING_EDGE, - STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES = STM32_LPTIM_CKPOL_BOTH_EDGES, - STM32_LPTIM_SYNAPSE_ACTION_NONE, + COUNTER_FUNCTION_INCREASE, + COUNTER_FUNCTION_QUADRATURE_X4, }; static const enum counter_synapse_action stm32_lptim_cnt_synapse_actions[] = { - /* Index must match with stm32_lptim_cnt_polarity[] (priv->polarity) */ - [STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE, - [STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE] = COUNTER_SYNAPSE_ACTION_FALLING_EDGE, - [STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES, - [STM32_LPTIM_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE, + COUNTER_SYNAPSE_ACTION_RISING_EDGE, + COUNTER_SYNAPSE_ACTION_FALLING_EDGE, + COUNTER_SYNAPSE_ACTION_BOTH_EDGES, + COUNTER_SYNAPSE_ACTION_NONE, }; static int stm32_lptim_cnt_read(struct counter_device *counter, - struct counter_count *count, unsigned long *val) + struct counter_count *count, u64 *val) { struct stm32_lptim_cnt *const priv = counter->priv; u32 cnt; @@ -170,28 +154,28 @@ static int stm32_lptim_cnt_read(struct counter_device *counter, return 0; } -static int stm32_lptim_cnt_function_get(struct counter_device *counter, - struct counter_count *count, - size_t *function) +static int stm32_lptim_cnt_function_read(struct counter_device *counter, + struct counter_count *count, + enum counter_function *function) { struct stm32_lptim_cnt *const priv = counter->priv; if (!priv->quadrature_mode) { - *function = STM32_LPTIM_COUNTER_INCREASE; + *function = COUNTER_FUNCTION_INCREASE; return 0; } - if (priv->polarity == STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES) { - *function = STM32_LPTIM_ENCODER_BOTH_EDGE; + if (priv->polarity == STM32_LPTIM_CKPOL_BOTH_EDGES) { + *function = COUNTER_FUNCTION_QUADRATURE_X4; return 0; } return -EINVAL; } -static int stm32_lptim_cnt_function_set(struct counter_device *counter, - struct counter_count *count, - size_t function) +static int stm32_lptim_cnt_function_write(struct counter_device *counter, + struct counter_count *count, + enum counter_function function) { struct stm32_lptim_cnt *const priv = counter->priv; @@ -199,12 +183,12 @@ static int stm32_lptim_cnt_function_set(struct counter_device *counter, return -EBUSY; switch (function) { - case STM32_LPTIM_COUNTER_INCREASE: + case COUNTER_FUNCTION_INCREASE: priv->quadrature_mode = 0; return 0; - case STM32_LPTIM_ENCODER_BOTH_EDGE: + case COUNTER_FUNCTION_QUADRATURE_X4: priv->quadrature_mode = 1; - priv->polarity = STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES; + priv->polarity = STM32_LPTIM_CKPOL_BOTH_EDGES; return 0; default: /* should never reach this path */ @@ -212,9 +196,9 @@ static int stm32_lptim_cnt_function_set(struct counter_device *counter, } } -static ssize_t stm32_lptim_cnt_enable_read(struct counter_device *counter, - struct counter_count *count, - void *private, char *buf) +static int stm32_lptim_cnt_enable_read(struct counter_device *counter, + struct counter_count *count, + u8 *enable) { struct stm32_lptim_cnt *const priv = counter->priv; int ret; @@ -223,22 +207,18 @@ static ssize_t stm32_lptim_cnt_enable_read(struct counter_device *counter, if (ret < 0) return ret; - return scnprintf(buf, PAGE_SIZE, "%u\n", ret); + *enable = ret; + + return 0; } -static ssize_t stm32_lptim_cnt_enable_write(struct counter_device *counter, - struct counter_count *count, - void *private, - const char *buf, size_t len) +static int stm32_lptim_cnt_enable_write(struct counter_device *counter, + struct counter_count *count, + u8 enable) { struct stm32_lptim_cnt *const priv = counter->priv; - bool enable; int ret; - ret = kstrtobool(buf, &enable); - if (ret) - return ret; - /* Check nobody uses the timer, or already disabled/enabled */ ret = stm32_lptim_is_enabled(priv); if ((ret < 0) || (!ret && !enable)) @@ -254,78 +234,81 @@ static ssize_t stm32_lptim_cnt_enable_write(struct counter_device *counter, if (ret) return ret; - return len; + return 0; } -static ssize_t stm32_lptim_cnt_ceiling_read(struct counter_device *counter, - struct counter_count *count, - void *private, char *buf) +static int stm32_lptim_cnt_ceiling_read(struct counter_device *counter, + struct counter_count *count, + u64 *ceiling) { struct stm32_lptim_cnt *const priv = counter->priv; - return snprintf(buf, PAGE_SIZE, "%u\n", priv->ceiling); + *ceiling = priv->ceiling; + + return 0; } -static ssize_t stm32_lptim_cnt_ceiling_write(struct counter_device *counter, - struct counter_count *count, - void *private, - const char *buf, size_t len) +static int stm32_lptim_cnt_ceiling_write(struct counter_device *counter, + struct counter_count *count, + u64 ceiling) { struct stm32_lptim_cnt *const priv = counter->priv; - unsigned int ceiling; - int ret; if (stm32_lptim_is_enabled(priv)) return -EBUSY; - ret = kstrtouint(buf, 0, &ceiling); - if (ret) - return ret; - if (ceiling > STM32_LPTIM_MAX_ARR) return -ERANGE; priv->ceiling = ceiling; - return len; + return 0; } -static const struct counter_count_ext stm32_lptim_cnt_ext[] = { - { - .name = "enable", - .read = stm32_lptim_cnt_enable_read, - .write = stm32_lptim_cnt_enable_write - }, - { - .name = "ceiling", - .read = stm32_lptim_cnt_ceiling_read, - .write = stm32_lptim_cnt_ceiling_write - }, +static struct counter_comp stm32_lptim_cnt_ext[] = { + COUNTER_COMP_ENABLE(stm32_lptim_cnt_enable_read, + stm32_lptim_cnt_enable_write), + COUNTER_COMP_CEILING(stm32_lptim_cnt_ceiling_read, + stm32_lptim_cnt_ceiling_write), }; -static int stm32_lptim_cnt_action_get(struct counter_device *counter, - struct counter_count *count, - struct counter_synapse *synapse, - size_t *action) +static int stm32_lptim_cnt_action_read(struct counter_device *counter, + struct counter_count *count, + struct counter_synapse *synapse, + enum counter_synapse_action *action) { struct stm32_lptim_cnt *const priv = counter->priv; - size_t function; + enum counter_function function; int err; - err = stm32_lptim_cnt_function_get(counter, count, &function); + err = stm32_lptim_cnt_function_read(counter, count, &function); if (err) return err; switch (function) { - case STM32_LPTIM_COUNTER_INCREASE: + case COUNTER_FUNCTION_INCREASE: /* LP Timer acts as up-counter on input 1 */ - if (synapse->signal->id == count->synapses[0].signal->id) - *action = priv->polarity; - else - *action = STM32_LPTIM_SYNAPSE_ACTION_NONE; - return 0; - case STM32_LPTIM_ENCODER_BOTH_EDGE: - *action = priv->polarity; + if (synapse->signal->id != count->synapses[0].signal->id) { + *action = COUNTER_SYNAPSE_ACTION_NONE; + return 0; + } + + switch (priv->polarity) { + case STM32_LPTIM_CKPOL_RISING_EDGE: + *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE; + return 0; + case STM32_LPTIM_CKPOL_FALLING_EDGE: + *action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE; + return 0; + case STM32_LPTIM_CKPOL_BOTH_EDGES: + *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; + return 0; + default: + /* should never reach this path */ + return -EINVAL; + } + case COUNTER_FUNCTION_QUADRATURE_X4: + *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; return 0; default: /* should never reach this path */ @@ -333,43 +316,48 @@ static int stm32_lptim_cnt_action_get(struct counter_device *counter, } } -static int stm32_lptim_cnt_action_set(struct counter_device *counter, - struct counter_count *count, - struct counter_synapse *synapse, - size_t action) +static int stm32_lptim_cnt_action_write(struct counter_device *counter, + struct counter_count *count, + struct counter_synapse *synapse, + enum counter_synapse_action action) { struct stm32_lptim_cnt *const priv = counter->priv; - size_t function; + enum counter_function function; int err; if (stm32_lptim_is_enabled(priv)) return -EBUSY; - err = stm32_lptim_cnt_function_get(counter, count, &function); + err = stm32_lptim_cnt_function_read(counter, count, &function); if (err) return err; /* only set polarity when in counter mode (on input 1) */ - if (function == STM32_LPTIM_COUNTER_INCREASE - && synapse->signal->id == count->synapses[0].signal->id) { - switch (action) { - case STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE: - case STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE: - case STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES: - priv->polarity = action; - return 0; - } - } + if (function != COUNTER_FUNCTION_INCREASE + || synapse->signal->id != count->synapses[0].signal->id) + return -EINVAL; - return -EINVAL; + switch (action) { + case COUNTER_SYNAPSE_ACTION_RISING_EDGE: + priv->polarity = STM32_LPTIM_CKPOL_RISING_EDGE; + return 0; + case COUNTER_SYNAPSE_ACTION_FALLING_EDGE: + priv->polarity = STM32_LPTIM_CKPOL_FALLING_EDGE; + return 0; + case COUNTER_SYNAPSE_ACTION_BOTH_EDGES: + priv->polarity = STM32_LPTIM_CKPOL_BOTH_EDGES; + return 0; + default: + return -EINVAL; + } } static const struct counter_ops stm32_lptim_cnt_ops = { .count_read = stm32_lptim_cnt_read, - .function_get = stm32_lptim_cnt_function_get, - .function_set = stm32_lptim_cnt_function_set, - .action_get = stm32_lptim_cnt_action_get, - .action_set = stm32_lptim_cnt_action_set, + .function_read = stm32_lptim_cnt_function_read, + .function_write = stm32_lptim_cnt_function_write, + .action_read = stm32_lptim_cnt_action_read, + .action_write = stm32_lptim_cnt_action_write, }; static struct counter_signal stm32_lptim_cnt_signals[] = { diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c index 1fbc46f4ee66..0546e932db0c 100644 --- a/drivers/counter/stm32-timer-cnt.c +++ b/drivers/counter/stm32-timer-cnt.c @@ -13,6 +13,7 @@ #include #include #include +#include #define TIM_CCMR_CCXS (BIT(8) | BIT(0)) #define TIM_CCMR_MASK (TIM_CCMR_CC1S | TIM_CCMR_CC2S | \ @@ -36,29 +37,15 @@ struct stm32_timer_cnt { struct stm32_timer_regs bak; }; -/** - * enum stm32_count_function - enumerates stm32 timer counter encoder modes - * @STM32_COUNT_SLAVE_MODE_DISABLED: counts on internal clock when CEN=1 - * @STM32_COUNT_ENCODER_MODE_1: counts TI1FP1 edges, depending on TI2FP2 level - * @STM32_COUNT_ENCODER_MODE_2: counts TI2FP2 edges, depending on TI1FP1 level - * @STM32_COUNT_ENCODER_MODE_3: counts on both TI1FP1 and TI2FP2 edges - */ -enum stm32_count_function { - STM32_COUNT_SLAVE_MODE_DISABLED, - STM32_COUNT_ENCODER_MODE_1, - STM32_COUNT_ENCODER_MODE_2, - STM32_COUNT_ENCODER_MODE_3, -}; - static const enum counter_function stm32_count_functions[] = { - [STM32_COUNT_SLAVE_MODE_DISABLED] = COUNTER_FUNCTION_INCREASE, - [STM32_COUNT_ENCODER_MODE_1] = COUNTER_FUNCTION_QUADRATURE_X2_A, - [STM32_COUNT_ENCODER_MODE_2] = COUNTER_FUNCTION_QUADRATURE_X2_B, - [STM32_COUNT_ENCODER_MODE_3] = COUNTER_FUNCTION_QUADRATURE_X4, + COUNTER_FUNCTION_INCREASE, + COUNTER_FUNCTION_QUADRATURE_X2_A, + COUNTER_FUNCTION_QUADRATURE_X2_B, + COUNTER_FUNCTION_QUADRATURE_X4, }; static int stm32_count_read(struct counter_device *counter, - struct counter_count *count, unsigned long *val) + struct counter_count *count, u64 *val) { struct stm32_timer_cnt *const priv = counter->priv; u32 cnt; @@ -70,8 +57,7 @@ static int stm32_count_read(struct counter_device *counter, } static int stm32_count_write(struct counter_device *counter, - struct counter_count *count, - const unsigned long val) + struct counter_count *count, const u64 val) { struct stm32_timer_cnt *const priv = counter->priv; u32 ceiling; @@ -83,9 +69,9 @@ static int stm32_count_write(struct counter_device *counter, return regmap_write(priv->regmap, TIM_CNT, val); } -static int stm32_count_function_get(struct counter_device *counter, - struct counter_count *count, - size_t *function) +static int stm32_count_function_read(struct counter_device *counter, + struct counter_count *count, + enum counter_function *function) { struct stm32_timer_cnt *const priv = counter->priv; u32 smcr; @@ -94,40 +80,40 @@ static int stm32_count_function_get(struct counter_device *counter, switch (smcr & TIM_SMCR_SMS) { case TIM_SMCR_SMS_SLAVE_MODE_DISABLED: - *function = STM32_COUNT_SLAVE_MODE_DISABLED; + *function = COUNTER_FUNCTION_INCREASE; return 0; case TIM_SMCR_SMS_ENCODER_MODE_1: - *function = STM32_COUNT_ENCODER_MODE_1; + *function = COUNTER_FUNCTION_QUADRATURE_X2_A; return 0; case TIM_SMCR_SMS_ENCODER_MODE_2: - *function = STM32_COUNT_ENCODER_MODE_2; + *function = COUNTER_FUNCTION_QUADRATURE_X2_B; return 0; case TIM_SMCR_SMS_ENCODER_MODE_3: - *function = STM32_COUNT_ENCODER_MODE_3; + *function = COUNTER_FUNCTION_QUADRATURE_X4; return 0; default: return -EINVAL; } } -static int stm32_count_function_set(struct counter_device *counter, - struct counter_count *count, - size_t function) +static int stm32_count_function_write(struct counter_device *counter, + struct counter_count *count, + enum counter_function function) { struct stm32_timer_cnt *const priv = counter->priv; u32 cr1, sms; switch (function) { - case STM32_COUNT_SLAVE_MODE_DISABLED: + case COUNTER_FUNCTION_INCREASE: sms = TIM_SMCR_SMS_SLAVE_MODE_DISABLED; break; - case STM32_COUNT_ENCODER_MODE_1: + case COUNTER_FUNCTION_QUADRATURE_X2_A: sms = TIM_SMCR_SMS_ENCODER_MODE_1; break; - case STM32_COUNT_ENCODER_MODE_2: + case COUNTER_FUNCTION_QUADRATURE_X2_B: sms = TIM_SMCR_SMS_ENCODER_MODE_2; break; - case STM32_COUNT_ENCODER_MODE_3: + case COUNTER_FUNCTION_QUADRATURE_X4: sms = TIM_SMCR_SMS_ENCODER_MODE_3; break; default: @@ -150,44 +136,37 @@ static int stm32_count_function_set(struct counter_device *counter, return 0; } -static ssize_t stm32_count_direction_read(struct counter_device *counter, +static int stm32_count_direction_read(struct counter_device *counter, struct counter_count *count, - void *private, char *buf) + enum counter_count_direction *direction) { struct stm32_timer_cnt *const priv = counter->priv; - const char *direction; u32 cr1; regmap_read(priv->regmap, TIM_CR1, &cr1); - direction = (cr1 & TIM_CR1_DIR) ? "backward" : "forward"; + *direction = (cr1 & TIM_CR1_DIR) ? COUNTER_COUNT_DIRECTION_BACKWARD : + COUNTER_COUNT_DIRECTION_FORWARD; - return scnprintf(buf, PAGE_SIZE, "%s\n", direction); + return 0; } -static ssize_t stm32_count_ceiling_read(struct counter_device *counter, - struct counter_count *count, - void *private, char *buf) +static int stm32_count_ceiling_read(struct counter_device *counter, + struct counter_count *count, u64 *ceiling) { struct stm32_timer_cnt *const priv = counter->priv; u32 arr; regmap_read(priv->regmap, TIM_ARR, &arr); - return snprintf(buf, PAGE_SIZE, "%u\n", arr); + *ceiling = arr; + + return 0; } -static ssize_t stm32_count_ceiling_write(struct counter_device *counter, - struct counter_count *count, - void *private, - const char *buf, size_t len) +static int stm32_count_ceiling_write(struct counter_device *counter, + struct counter_count *count, u64 ceiling) { struct stm32_timer_cnt *const priv = counter->priv; - unsigned int ceiling; - int ret; - - ret = kstrtouint(buf, 0, &ceiling); - if (ret) - return ret; if (ceiling > priv->max_arr) return -ERANGE; @@ -196,34 +175,27 @@ static ssize_t stm32_count_ceiling_write(struct counter_device *counter, regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, 0); regmap_write(priv->regmap, TIM_ARR, ceiling); - return len; + return 0; } -static ssize_t stm32_count_enable_read(struct counter_device *counter, - struct counter_count *count, - void *private, char *buf) +static int stm32_count_enable_read(struct counter_device *counter, + struct counter_count *count, u8 *enable) { struct stm32_timer_cnt *const priv = counter->priv; u32 cr1; regmap_read(priv->regmap, TIM_CR1, &cr1); - return scnprintf(buf, PAGE_SIZE, "%d\n", (bool)(cr1 & TIM_CR1_CEN)); + *enable = cr1 & TIM_CR1_CEN; + + return 0; } -static ssize_t stm32_count_enable_write(struct counter_device *counter, - struct counter_count *count, - void *private, - const char *buf, size_t len) +static int stm32_count_enable_write(struct counter_device *counter, + struct counter_count *count, u8 enable) { struct stm32_timer_cnt *const priv = counter->priv; - int err; u32 cr1; - bool enable; - - err = kstrtobool(buf, &enable); - if (err) - return err; if (enable) { regmap_read(priv->regmap, TIM_CR1, &cr1); @@ -242,70 +214,55 @@ static ssize_t stm32_count_enable_write(struct counter_device *counter, /* Keep enabled state to properly handle low power states */ priv->enabled = enable; - return len; + return 0; } -static const struct counter_count_ext stm32_count_ext[] = { - { - .name = "direction", - .read = stm32_count_direction_read, - }, - { - .name = "enable", - .read = stm32_count_enable_read, - .write = stm32_count_enable_write - }, - { - .name = "ceiling", - .read = stm32_count_ceiling_read, - .write = stm32_count_ceiling_write - }, -}; - -enum stm32_synapse_action { - STM32_SYNAPSE_ACTION_NONE, - STM32_SYNAPSE_ACTION_BOTH_EDGES +static struct counter_comp stm32_count_ext[] = { + COUNTER_COMP_DIRECTION(stm32_count_direction_read), + COUNTER_COMP_ENABLE(stm32_count_enable_read, stm32_count_enable_write), + COUNTER_COMP_CEILING(stm32_count_ceiling_read, + stm32_count_ceiling_write), }; static const enum counter_synapse_action stm32_synapse_actions[] = { - [STM32_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE, - [STM32_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES + COUNTER_SYNAPSE_ACTION_NONE, + COUNTER_SYNAPSE_ACTION_BOTH_EDGES }; -static int stm32_action_get(struct counter_device *counter, - struct counter_count *count, - struct counter_synapse *synapse, - size_t *action) +static int stm32_action_read(struct counter_device *counter, + struct counter_count *count, + struct counter_synapse *synapse, + enum counter_synapse_action *action) { - size_t function; + enum counter_function function; int err; - err = stm32_count_function_get(counter, count, &function); + err = stm32_count_function_read(counter, count, &function); if (err) return err; switch (function) { - case STM32_COUNT_SLAVE_MODE_DISABLED: + case COUNTER_FUNCTION_INCREASE: /* counts on internal clock when CEN=1 */ - *action = STM32_SYNAPSE_ACTION_NONE; + *action = COUNTER_SYNAPSE_ACTION_NONE; return 0; - case STM32_COUNT_ENCODER_MODE_1: + case COUNTER_FUNCTION_QUADRATURE_X2_A: /* counts up/down on TI1FP1 edge depending on TI2FP2 level */ if (synapse->signal->id == count->synapses[0].signal->id) - *action = STM32_SYNAPSE_ACTION_BOTH_EDGES; + *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; else - *action = STM32_SYNAPSE_ACTION_NONE; + *action = COUNTER_SYNAPSE_ACTION_NONE; return 0; - case STM32_COUNT_ENCODER_MODE_2: + case COUNTER_FUNCTION_QUADRATURE_X2_B: /* counts up/down on TI2FP2 edge depending on TI1FP1 level */ if (synapse->signal->id == count->synapses[1].signal->id) - *action = STM32_SYNAPSE_ACTION_BOTH_EDGES; + *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; else - *action = STM32_SYNAPSE_ACTION_NONE; + *action = COUNTER_SYNAPSE_ACTION_NONE; return 0; - case STM32_COUNT_ENCODER_MODE_3: + case COUNTER_FUNCTION_QUADRATURE_X4: /* counts up/down on both TI1FP1 and TI2FP2 edges */ - *action = STM32_SYNAPSE_ACTION_BOTH_EDGES; + *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; return 0; default: return -EINVAL; @@ -315,9 +272,9 @@ static int stm32_action_get(struct counter_device *counter, static const struct counter_ops stm32_timer_cnt_ops = { .count_read = stm32_count_read, .count_write = stm32_count_write, - .function_get = stm32_count_function_get, - .function_set = stm32_count_function_set, - .action_get = stm32_action_get, + .function_read = stm32_count_function_read, + .function_write = stm32_count_function_write, + .action_read = stm32_action_read, }; static struct counter_signal stm32_signals[] = { diff --git a/drivers/counter/ti-eqep.c b/drivers/counter/ti-eqep.c index 94fe58bb3eab..09817c953f9a 100644 --- a/drivers/counter/ti-eqep.c +++ b/drivers/counter/ti-eqep.c @@ -13,6 +13,7 @@ #include #include #include +#include /* 32-bit registers */ #define QPOSCNT 0x0 @@ -73,19 +74,13 @@ enum { }; /* Position Counter Input Modes */ -enum { +enum ti_eqep_count_func { TI_EQEP_COUNT_FUNC_QUAD_COUNT, TI_EQEP_COUNT_FUNC_DIR_COUNT, TI_EQEP_COUNT_FUNC_UP_COUNT, TI_EQEP_COUNT_FUNC_DOWN_COUNT, }; -enum { - TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES, - TI_EQEP_SYNAPSE_ACTION_RISING_EDGE, - TI_EQEP_SYNAPSE_ACTION_NONE, -}; - struct ti_eqep_cnt { struct counter_device counter; struct regmap *regmap32; @@ -93,7 +88,7 @@ struct ti_eqep_cnt { }; static int ti_eqep_count_read(struct counter_device *counter, - struct counter_count *count, unsigned long *val) + struct counter_count *count, u64 *val) { struct ti_eqep_cnt *priv = counter->priv; u32 cnt; @@ -105,7 +100,7 @@ static int ti_eqep_count_read(struct counter_device *counter, } static int ti_eqep_count_write(struct counter_device *counter, - struct counter_count *count, unsigned long val) + struct counter_count *count, u64 val) { struct ti_eqep_cnt *priv = counter->priv; u32 max; @@ -117,64 +112,100 @@ static int ti_eqep_count_write(struct counter_device *counter, return regmap_write(priv->regmap32, QPOSCNT, val); } -static int ti_eqep_function_get(struct counter_device *counter, - struct counter_count *count, size_t *function) +static int ti_eqep_function_read(struct counter_device *counter, + struct counter_count *count, + enum counter_function *function) { struct ti_eqep_cnt *priv = counter->priv; u32 qdecctl; regmap_read(priv->regmap16, QDECCTL, &qdecctl); - *function = (qdecctl & QDECCTL_QSRC) >> QDECCTL_QSRC_SHIFT; + + switch ((qdecctl & QDECCTL_QSRC) >> QDECCTL_QSRC_SHIFT) { + case TI_EQEP_COUNT_FUNC_QUAD_COUNT: + *function = COUNTER_FUNCTION_QUADRATURE_X4; + break; + case TI_EQEP_COUNT_FUNC_DIR_COUNT: + *function = COUNTER_FUNCTION_PULSE_DIRECTION; + break; + case TI_EQEP_COUNT_FUNC_UP_COUNT: + *function = COUNTER_FUNCTION_INCREASE; + break; + case TI_EQEP_COUNT_FUNC_DOWN_COUNT: + *function = COUNTER_FUNCTION_DECREASE; + break; + } return 0; } -static int ti_eqep_function_set(struct counter_device *counter, - struct counter_count *count, size_t function) +static int ti_eqep_function_write(struct counter_device *counter, + struct counter_count *count, + enum counter_function function) { struct ti_eqep_cnt *priv = counter->priv; + enum ti_eqep_count_func qsrc; + + switch (function) { + case COUNTER_FUNCTION_QUADRATURE_X4: + qsrc = TI_EQEP_COUNT_FUNC_QUAD_COUNT; + break; + case COUNTER_FUNCTION_PULSE_DIRECTION: + qsrc = TI_EQEP_COUNT_FUNC_DIR_COUNT; + break; + case COUNTER_FUNCTION_INCREASE: + qsrc = TI_EQEP_COUNT_FUNC_UP_COUNT; + break; + case COUNTER_FUNCTION_DECREASE: + qsrc = TI_EQEP_COUNT_FUNC_DOWN_COUNT; + break; + default: + /* should never reach this path */ + return -EINVAL; + } return regmap_write_bits(priv->regmap16, QDECCTL, QDECCTL_QSRC, - function << QDECCTL_QSRC_SHIFT); + qsrc << QDECCTL_QSRC_SHIFT); } -static int ti_eqep_action_get(struct counter_device *counter, - struct counter_count *count, - struct counter_synapse *synapse, size_t *action) +static int ti_eqep_action_read(struct counter_device *counter, + struct counter_count *count, + struct counter_synapse *synapse, + enum counter_synapse_action *action) { struct ti_eqep_cnt *priv = counter->priv; - size_t function; + enum counter_function function; u32 qdecctl; int err; - err = ti_eqep_function_get(counter, count, &function); + err = ti_eqep_function_read(counter, count, &function); if (err) return err; switch (function) { - case TI_EQEP_COUNT_FUNC_QUAD_COUNT: + case COUNTER_FUNCTION_QUADRATURE_X4: /* In quadrature mode, the rising and falling edge of both * QEPA and QEPB trigger QCLK. */ - *action = TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES; + *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; return 0; - case TI_EQEP_COUNT_FUNC_DIR_COUNT: + case COUNTER_FUNCTION_PULSE_DIRECTION: /* In direction-count mode only rising edge of QEPA is counted * and QEPB gives direction. */ switch (synapse->signal->id) { case TI_EQEP_SIGNAL_QEPA: - *action = TI_EQEP_SYNAPSE_ACTION_RISING_EDGE; + *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE; return 0; case TI_EQEP_SIGNAL_QEPB: - *action = TI_EQEP_SYNAPSE_ACTION_NONE; + *action = COUNTER_SYNAPSE_ACTION_NONE; return 0; default: /* should never reach this path */ return -EINVAL; } - case TI_EQEP_COUNT_FUNC_UP_COUNT: - case TI_EQEP_COUNT_FUNC_DOWN_COUNT: + case COUNTER_FUNCTION_INCREASE: + case COUNTER_FUNCTION_DECREASE: /* In up/down-count modes only QEPA is counted and QEPB is not * used. */ @@ -185,12 +216,12 @@ static int ti_eqep_action_get(struct counter_device *counter, return err; if (qdecctl & QDECCTL_XCR) - *action = TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES; + *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; else - *action = TI_EQEP_SYNAPSE_ACTION_RISING_EDGE; + *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE; return 0; case TI_EQEP_SIGNAL_QEPB: - *action = TI_EQEP_SYNAPSE_ACTION_NONE; + *action = COUNTER_SYNAPSE_ACTION_NONE; return 0; default: /* should never reach this path */ @@ -205,82 +236,67 @@ static int ti_eqep_action_get(struct counter_device *counter, static const struct counter_ops ti_eqep_counter_ops = { .count_read = ti_eqep_count_read, .count_write = ti_eqep_count_write, - .function_get = ti_eqep_function_get, - .function_set = ti_eqep_function_set, - .action_get = ti_eqep_action_get, + .function_read = ti_eqep_function_read, + .function_write = ti_eqep_function_write, + .action_read = ti_eqep_action_read, }; -static ssize_t ti_eqep_position_ceiling_read(struct counter_device *counter, - struct counter_count *count, - void *ext_priv, char *buf) +static int ti_eqep_position_ceiling_read(struct counter_device *counter, + struct counter_count *count, + u64 *ceiling) { struct ti_eqep_cnt *priv = counter->priv; u32 qposmax; regmap_read(priv->regmap32, QPOSMAX, &qposmax); - return sprintf(buf, "%u\n", qposmax); + *ceiling = qposmax; + + return 0; } -static ssize_t ti_eqep_position_ceiling_write(struct counter_device *counter, - struct counter_count *count, - void *ext_priv, const char *buf, - size_t len) +static int ti_eqep_position_ceiling_write(struct counter_device *counter, + struct counter_count *count, + u64 ceiling) { struct ti_eqep_cnt *priv = counter->priv; - int err; - u32 res; - err = kstrtouint(buf, 0, &res); - if (err < 0) - return err; + if (ceiling != (u32)ceiling) + return -ERANGE; - regmap_write(priv->regmap32, QPOSMAX, res); + regmap_write(priv->regmap32, QPOSMAX, ceiling); - return len; + return 0; } -static ssize_t ti_eqep_position_enable_read(struct counter_device *counter, - struct counter_count *count, - void *ext_priv, char *buf) +static int ti_eqep_position_enable_read(struct counter_device *counter, + struct counter_count *count, u8 *enable) { struct ti_eqep_cnt *priv = counter->priv; u32 qepctl; regmap_read(priv->regmap16, QEPCTL, &qepctl); - return sprintf(buf, "%u\n", !!(qepctl & QEPCTL_PHEN)); + *enable = !!(qepctl & QEPCTL_PHEN); + + return 0; } -static ssize_t ti_eqep_position_enable_write(struct counter_device *counter, - struct counter_count *count, - void *ext_priv, const char *buf, - size_t len) +static int ti_eqep_position_enable_write(struct counter_device *counter, + struct counter_count *count, u8 enable) { struct ti_eqep_cnt *priv = counter->priv; - int err; - bool res; - - err = kstrtobool(buf, &res); - if (err < 0) - return err; - regmap_write_bits(priv->regmap16, QEPCTL, QEPCTL_PHEN, res ? -1 : 0); + regmap_write_bits(priv->regmap16, QEPCTL, QEPCTL_PHEN, enable ? -1 : 0); - return len; + return 0; } -static struct counter_count_ext ti_eqep_position_ext[] = { - { - .name = "ceiling", - .read = ti_eqep_position_ceiling_read, - .write = ti_eqep_position_ceiling_write, - }, - { - .name = "enable", - .read = ti_eqep_position_enable_read, - .write = ti_eqep_position_enable_write, - }, +static struct counter_comp ti_eqep_position_ext[] = { + COUNTER_COMP_CEILING(ti_eqep_position_ceiling_read, + ti_eqep_position_ceiling_write), + COUNTER_COMP_ENABLE(ti_eqep_position_enable_read, + ti_eqep_position_enable_write), }; static struct counter_signal ti_eqep_signals[] = { @@ -295,16 +311,16 @@ static struct counter_signal ti_eqep_signals[] = { }; static const enum counter_function ti_eqep_position_functions[] = { - [TI_EQEP_COUNT_FUNC_QUAD_COUNT] = COUNTER_FUNCTION_QUADRATURE_X4, - [TI_EQEP_COUNT_FUNC_DIR_COUNT] = COUNTER_FUNCTION_PULSE_DIRECTION, - [TI_EQEP_COUNT_FUNC_UP_COUNT] = COUNTER_FUNCTION_INCREASE, - [TI_EQEP_COUNT_FUNC_DOWN_COUNT] = COUNTER_FUNCTION_DECREASE, + COUNTER_FUNCTION_QUADRATURE_X4, + COUNTER_FUNCTION_PULSE_DIRECTION, + COUNTER_FUNCTION_INCREASE, + COUNTER_FUNCTION_DECREASE, }; static const enum counter_synapse_action ti_eqep_position_synapse_actions[] = { - [TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES, - [TI_EQEP_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE, - [TI_EQEP_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE, + COUNTER_SYNAPSE_ACTION_BOTH_EDGES, + COUNTER_SYNAPSE_ACTION_RISING_EDGE, + COUNTER_SYNAPSE_ACTION_NONE, }; static struct counter_synapse ti_eqep_position_synapses[] = { diff --git a/include/linux/counter.h b/include/linux/counter.h index d16ce2819b48..b69277f5c4c5 100644 --- a/include/linux/counter.h +++ b/include/linux/counter.h @@ -6,42 +6,184 @@ #ifndef _COUNTER_H_ #define _COUNTER_H_ -#include #include +#include #include +struct counter_device; +struct counter_count; +struct counter_synapse; +struct counter_signal; + +enum counter_comp_type { + COUNTER_COMP_U8, + COUNTER_COMP_U64, + COUNTER_COMP_BOOL, + COUNTER_COMP_SIGNAL_LEVEL, + COUNTER_COMP_FUNCTION, + COUNTER_COMP_SYNAPSE_ACTION, + COUNTER_COMP_ENUM, + COUNTER_COMP_COUNT_DIRECTION, + COUNTER_COMP_COUNT_MODE, +}; + +enum counter_scope { + COUNTER_SCOPE_DEVICE, + COUNTER_SCOPE_SIGNAL, + COUNTER_SCOPE_COUNT, +}; + enum counter_count_direction { - COUNTER_COUNT_DIRECTION_FORWARD = 0, - COUNTER_COUNT_DIRECTION_BACKWARD + COUNTER_COUNT_DIRECTION_FORWARD, + COUNTER_COUNT_DIRECTION_BACKWARD, }; -extern const char *const counter_count_direction_str[2]; enum counter_count_mode { - COUNTER_COUNT_MODE_NORMAL = 0, + COUNTER_COUNT_MODE_NORMAL, COUNTER_COUNT_MODE_RANGE_LIMIT, COUNTER_COUNT_MODE_NON_RECYCLE, - COUNTER_COUNT_MODE_MODULO_N + COUNTER_COUNT_MODE_MODULO_N, }; -extern const char *const counter_count_mode_str[4]; -struct counter_device; -struct counter_signal; +enum counter_function { + COUNTER_FUNCTION_INCREASE, + COUNTER_FUNCTION_DECREASE, + COUNTER_FUNCTION_PULSE_DIRECTION, + COUNTER_FUNCTION_QUADRATURE_X1_A, + COUNTER_FUNCTION_QUADRATURE_X1_B, + COUNTER_FUNCTION_QUADRATURE_X2_A, + COUNTER_FUNCTION_QUADRATURE_X2_B, + COUNTER_FUNCTION_QUADRATURE_X4, +}; + +enum counter_signal_level { + COUNTER_SIGNAL_LEVEL_LOW, + COUNTER_SIGNAL_LEVEL_HIGH, +}; + +enum counter_synapse_action { + COUNTER_SYNAPSE_ACTION_NONE, + COUNTER_SYNAPSE_ACTION_RISING_EDGE, + COUNTER_SYNAPSE_ACTION_FALLING_EDGE, + COUNTER_SYNAPSE_ACTION_BOTH_EDGES, +}; /** - * struct counter_signal_ext - Counter Signal extensions - * @name: attribute name - * @read: read callback for this attribute; may be NULL - * @write: write callback for this attribute; may be NULL - * @priv: data private to the driver + * struct counter_comp - Counter component node + * @type: Counter component data type + * @name: device-specific component name + * @priv: component-relevant data + * @action_read Synapse action mode read callback. The read value of the + * respective Synapse action mode should be passed back via + * the action parameter. + * @device_u8_read Device u8 component read callback. The read value of the + * respective Device u8 component should be passed back via + * the val parameter. + * @count_u8_read Count u8 component read callback. The read value of the + * respective Count u8 component should be passed back via + * the val parameter. + * @signal_u8_read Signal u8 component read callback. The read value of the + * respective Signal u8 component should be passed back via + * the val parameter. + * @device_u32_read Device u32 component read callback. The read value of + * the respective Device u32 component should be passed + * back via the val parameter. + * @count_u32_read Count u32 component read callback. The read value of the + * respective Count u32 component should be passed back via + * the val parameter. + * @signal_u32_read Signal u32 component read callback. The read value of + * the respective Signal u32 component should be passed + * back via the val parameter. + * @device_u64_read Device u64 component read callback. The read value of + * the respective Device u64 component should be passed + * back via the val parameter. + * @count_u64_read Count u64 component read callback. The read value of the + * respective Count u64 component should be passed back via + * the val parameter. + * @signal_u64_read Signal u64 component read callback. The read value of + * the respective Signal u64 component should be passed + * back via the val parameter. + * @action_write Synapse action mode write callback. The write value of + * the respective Synapse action mode is passed via the + * action parameter. + * @device_u8_write Device u8 component write callback. The write value of + * the respective Device u8 component is passed via the val + * parameter. + * @count_u8_write Count u8 component write callback. The write value of + * the respective Count u8 component is passed via the val + * parameter. + * @signal_u8_write Signal u8 component write callback. The write value of + * the respective Signal u8 component is passed via the val + * parameter. + * @device_u32_write Device u32 component write callback. The write value of + * the respective Device u32 component is passed via the + * val parameter. + * @count_u32_write Count u32 component write callback. The write value of + * the respective Count u32 component is passed via the val + * parameter. + * @signal_u32_write Signal u32 component write callback. The write value of + * the respective Signal u32 component is passed via the + * val parameter. + * @device_u64_write Device u64 component write callback. The write value of + * the respective Device u64 component is passed via the + * val parameter. + * @count_u64_write Count u64 component write callback. The write value of + * the respective Count u64 component is passed via the val + * parameter. + * @signal_u64_write Signal u64 component write callback. The write value of + * the respective Signal u64 component is passed via the + * val parameter. */ -struct counter_signal_ext { +struct counter_comp { + enum counter_comp_type type; const char *name; - ssize_t (*read)(struct counter_device *counter, - struct counter_signal *signal, void *priv, char *buf); - ssize_t (*write)(struct counter_device *counter, - struct counter_signal *signal, void *priv, - const char *buf, size_t len); void *priv; + union { + int (*action_read)(struct counter_device *counter, + struct counter_count *count, + struct counter_synapse *synapse, + enum counter_synapse_action *action); + int (*device_u8_read)(struct counter_device *counter, u8 *val); + int (*count_u8_read)(struct counter_device *counter, + struct counter_count *count, u8 *val); + int (*signal_u8_read)(struct counter_device *counter, + struct counter_signal *signal, u8 *val); + int (*device_u32_read)(struct counter_device *counter, + u32 *val); + int (*count_u32_read)(struct counter_device *counter, + struct counter_count *count, u32 *val); + int (*signal_u32_read)(struct counter_device *counter, + struct counter_signal *signal, u32 *val); + int (*device_u64_read)(struct counter_device *counter, + u64 *val); + int (*count_u64_read)(struct counter_device *counter, + struct counter_count *count, u64 *val); + int (*signal_u64_read)(struct counter_device *counter, + struct counter_signal *signal, u64 *val); + }; + union { + int (*action_write)(struct counter_device *counter, + struct counter_count *count, + struct counter_synapse *synapse, + enum counter_synapse_action action); + int (*device_u8_write)(struct counter_device *counter, u8 val); + int (*count_u8_write)(struct counter_device *counter, + struct counter_count *count, u8 val); + int (*signal_u8_write)(struct counter_device *counter, + struct counter_signal *signal, u8 val); + int (*device_u32_write)(struct counter_device *counter, + u32 val); + int (*count_u32_write)(struct counter_device *counter, + struct counter_count *count, u32 val); + int (*signal_u32_write)(struct counter_device *counter, + struct counter_signal *signal, u32 val); + int (*device_u64_write)(struct counter_device *counter, + u64 val); + int (*count_u64_write)(struct counter_device *counter, + struct counter_count *count, u64 val); + int (*signal_u64_write)(struct counter_device *counter, + struct counter_signal *signal, u64 val); + }; }; /** @@ -51,248 +193,52 @@ struct counter_signal_ext { * as it appears in the datasheet documentation * @ext: optional array of Counter Signal extensions * @num_ext: number of Counter Signal extensions specified in @ext - * @priv: optional private data supplied by driver */ struct counter_signal { int id; const char *name; - const struct counter_signal_ext *ext; + struct counter_comp *ext; size_t num_ext; - - void *priv; -}; - -/** - * struct counter_signal_enum_ext - Signal enum extension attribute - * @items: Array of strings - * @num_items: Number of items specified in @items - * @set: Set callback function; may be NULL - * @get: Get callback function; may be NULL - * - * The counter_signal_enum_ext structure can be used to implement enum style - * Signal extension attributes. Enum style attributes are those which have a set - * of strings that map to unsigned integer values. The Generic Counter Signal - * enum extension helper code takes care of mapping between value and string, as - * well as generating a "_available" file which contains a list of all available - * items. The get callback is used to query the currently active item; the index - * of the item within the respective items array is returned via the 'item' - * parameter. The set callback is called when the attribute is updated; the - * 'item' parameter contains the index of the newly activated item within the - * respective items array. - */ -struct counter_signal_enum_ext { - const char * const *items; - size_t num_items; - int (*get)(struct counter_device *counter, - struct counter_signal *signal, size_t *item); - int (*set)(struct counter_device *counter, - struct counter_signal *signal, size_t item); -}; - -/** - * COUNTER_SIGNAL_ENUM() - Initialize Signal enum extension - * @_name: Attribute name - * @_e: Pointer to a counter_signal_enum_ext structure - * - * This should usually be used together with COUNTER_SIGNAL_ENUM_AVAILABLE() - */ -#define COUNTER_SIGNAL_ENUM(_name, _e) \ -{ \ - .name = (_name), \ - .read = counter_signal_enum_read, \ - .write = counter_signal_enum_write, \ - .priv = (_e) \ -} - -/** - * COUNTER_SIGNAL_ENUM_AVAILABLE() - Initialize Signal enum available extension - * @_name: Attribute name ("_available" will be appended to the name) - * @_e: Pointer to a counter_signal_enum_ext structure - * - * Creates a read only attribute that lists all the available enum items in a - * newline separated list. This should usually be used together with - * COUNTER_SIGNAL_ENUM() - */ -#define COUNTER_SIGNAL_ENUM_AVAILABLE(_name, _e) \ -{ \ - .name = (_name "_available"), \ - .read = counter_signal_enum_available_read, \ - .priv = (_e) \ -} - -enum counter_synapse_action { - COUNTER_SYNAPSE_ACTION_NONE = 0, - COUNTER_SYNAPSE_ACTION_RISING_EDGE, - COUNTER_SYNAPSE_ACTION_FALLING_EDGE, - COUNTER_SYNAPSE_ACTION_BOTH_EDGES }; /** * struct counter_synapse - Counter Synapse node - * @action: index of current action mode * @actions_list: array of available action modes * @num_actions: number of action modes specified in @actions_list * @signal: pointer to associated signal */ struct counter_synapse { - size_t action; const enum counter_synapse_action *actions_list; size_t num_actions; struct counter_signal *signal; }; -struct counter_count; - -/** - * struct counter_count_ext - Counter Count extension - * @name: attribute name - * @read: read callback for this attribute; may be NULL - * @write: write callback for this attribute; may be NULL - * @priv: data private to the driver - */ -struct counter_count_ext { - const char *name; - ssize_t (*read)(struct counter_device *counter, - struct counter_count *count, void *priv, char *buf); - ssize_t (*write)(struct counter_device *counter, - struct counter_count *count, void *priv, - const char *buf, size_t len); - void *priv; -}; - -enum counter_function { - COUNTER_FUNCTION_INCREASE = 0, - COUNTER_FUNCTION_DECREASE, - COUNTER_FUNCTION_PULSE_DIRECTION, - COUNTER_FUNCTION_QUADRATURE_X1_A, - COUNTER_FUNCTION_QUADRATURE_X1_B, - COUNTER_FUNCTION_QUADRATURE_X2_A, - COUNTER_FUNCTION_QUADRATURE_X2_B, - COUNTER_FUNCTION_QUADRATURE_X4 -}; - /** * struct counter_count - Counter Count node * @id: unique ID used to identify Count * @name: device-specific Count name; ideally, this should match * the name as it appears in the datasheet documentation - * @function: index of current function mode * @functions_list: array available function modes * @num_functions: number of function modes specified in @functions_list * @synapses: array of synapses for initialization * @num_synapses: number of synapses specified in @synapses * @ext: optional array of Counter Count extensions * @num_ext: number of Counter Count extensions specified in @ext - * @priv: optional private data supplied by driver */ struct counter_count { int id; const char *name; - size_t function; const enum counter_function *functions_list; size_t num_functions; struct counter_synapse *synapses; size_t num_synapses; - const struct counter_count_ext *ext; + struct counter_comp *ext; size_t num_ext; - - void *priv; -}; - -/** - * struct counter_count_enum_ext - Count enum extension attribute - * @items: Array of strings - * @num_items: Number of items specified in @items - * @set: Set callback function; may be NULL - * @get: Get callback function; may be NULL - * - * The counter_count_enum_ext structure can be used to implement enum style - * Count extension attributes. Enum style attributes are those which have a set - * of strings that map to unsigned integer values. The Generic Counter Count - * enum extension helper code takes care of mapping between value and string, as - * well as generating a "_available" file which contains a list of all available - * items. The get callback is used to query the currently active item; the index - * of the item within the respective items array is returned via the 'item' - * parameter. The set callback is called when the attribute is updated; the - * 'item' parameter contains the index of the newly activated item within the - * respective items array. - */ -struct counter_count_enum_ext { - const char * const *items; - size_t num_items; - int (*get)(struct counter_device *counter, struct counter_count *count, - size_t *item); - int (*set)(struct counter_device *counter, struct counter_count *count, - size_t item); -}; - -/** - * COUNTER_COUNT_ENUM() - Initialize Count enum extension - * @_name: Attribute name - * @_e: Pointer to a counter_count_enum_ext structure - * - * This should usually be used together with COUNTER_COUNT_ENUM_AVAILABLE() - */ -#define COUNTER_COUNT_ENUM(_name, _e) \ -{ \ - .name = (_name), \ - .read = counter_count_enum_read, \ - .write = counter_count_enum_write, \ - .priv = (_e) \ -} - -/** - * COUNTER_COUNT_ENUM_AVAILABLE() - Initialize Count enum available extension - * @_name: Attribute name ("_available" will be appended to the name) - * @_e: Pointer to a counter_count_enum_ext structure - * - * Creates a read only attribute that lists all the available enum items in a - * newline separated list. This should usually be used together with - * COUNTER_COUNT_ENUM() - */ -#define COUNTER_COUNT_ENUM_AVAILABLE(_name, _e) \ -{ \ - .name = (_name "_available"), \ - .read = counter_count_enum_available_read, \ - .priv = (_e) \ -} - -/** - * struct counter_device_attr_group - internal container for attribute group - * @attr_group: Counter sysfs attributes group - * @attr_list: list to keep track of created Counter sysfs attributes - * @num_attr: number of Counter sysfs attributes - */ -struct counter_device_attr_group { - struct attribute_group attr_group; - struct list_head attr_list; - size_t num_attr; -}; - -/** - * struct counter_device_state - internal state container for a Counter device - * @id: unique ID used to identify the Counter - * @dev: internal device structure - * @groups_list: attribute groups list (for Signals, Counts, and ext) - * @num_groups: number of attribute groups containers - * @groups: Counter sysfs attribute groups (to populate @dev.groups) - */ -struct counter_device_state { - int id; - struct device dev; - struct counter_device_attr_group *groups_list; - size_t num_groups; - const struct attribute_group **groups; -}; - -enum counter_signal_level { - COUNTER_SIGNAL_LEVEL_LOW, - COUNTER_SIGNAL_LEVEL_HIGH, }; /** @@ -306,117 +252,47 @@ enum counter_signal_level { * @count_write: optional write callback for Count attribute. The write * value for the respective Count is passed in via the val * parameter. - * @function_get: function to get the current count function mode. Returns - * 0 on success and negative error code on error. The index - * of the respective Count's returned function mode should - * be passed back via the function parameter. - * @function_set: function to set the count function mode. function is the - * index of the requested function mode from the respective - * Count's functions_list array. - * @action_get: function to get the current action mode. Returns 0 on - * success and negative error code on error. The index of - * the respective Synapse's returned action mode should be - * passed back via the action parameter. - * @action_set: function to set the action mode. action is the index of - * the requested action mode from the respective Synapse's - * actions_list array. + * @function_read: read callback the Count function modes. The read + * function mode of the respective Count should be passed + * back via the function parameter. + * @function_write: write callback for Count function modes. The function + * mode to write for the respective Count is passed in via + * the function parameter. + * @action_read: read callback the Synapse action modes. The read action + * mode of the respective Synapse should be passed back via + * the action parameter. + * @action_write: write callback for Synapse action modes. The action mode + * to write for the respective Synapse is passed in via the + * action parameter. */ struct counter_ops { int (*signal_read)(struct counter_device *counter, struct counter_signal *signal, enum counter_signal_level *level); int (*count_read)(struct counter_device *counter, - struct counter_count *count, unsigned long *val); + struct counter_count *count, u64 *value); int (*count_write)(struct counter_device *counter, - struct counter_count *count, unsigned long val); - int (*function_get)(struct counter_device *counter, - struct counter_count *count, size_t *function); - int (*function_set)(struct counter_device *counter, - struct counter_count *count, size_t function); - int (*action_get)(struct counter_device *counter, - struct counter_count *count, - struct counter_synapse *synapse, size_t *action); - int (*action_set)(struct counter_device *counter, - struct counter_count *count, - struct counter_synapse *synapse, size_t action); -}; - -/** - * struct counter_device_ext - Counter device extension - * @name: attribute name - * @read: read callback for this attribute; may be NULL - * @write: write callback for this attribute; may be NULL - * @priv: data private to the driver - */ -struct counter_device_ext { - const char *name; - ssize_t (*read)(struct counter_device *counter, void *priv, char *buf); - ssize_t (*write)(struct counter_device *counter, void *priv, - const char *buf, size_t len); - void *priv; + struct counter_count *count, u64 value); + int (*function_read)(struct counter_device *counter, + struct counter_count *count, + enum counter_function *function); + int (*function_write)(struct counter_device *counter, + struct counter_count *count, + enum counter_function function); + int (*action_read)(struct counter_device *counter, + struct counter_count *count, + struct counter_synapse *synapse, + enum counter_synapse_action *action); + int (*action_write)(struct counter_device *counter, + struct counter_count *count, + struct counter_synapse *synapse, + enum counter_synapse_action action); }; -/** - * struct counter_device_enum_ext - Counter enum extension attribute - * @items: Array of strings - * @num_items: Number of items specified in @items - * @set: Set callback function; may be NULL - * @get: Get callback function; may be NULL - * - * The counter_device_enum_ext structure can be used to implement enum style - * Counter extension attributes. Enum style attributes are those which have a - * set of strings that map to unsigned integer values. The Generic Counter enum - * extension helper code takes care of mapping between value and string, as well - * as generating a "_available" file which contains a list of all available - * items. The get callback is used to query the currently active item; the index - * of the item within the respective items array is returned via the 'item' - * parameter. The set callback is called when the attribute is updated; the - * 'item' parameter contains the index of the newly activated item within the - * respective items array. - */ -struct counter_device_enum_ext { - const char * const *items; - size_t num_items; - int (*get)(struct counter_device *counter, size_t *item); - int (*set)(struct counter_device *counter, size_t item); -}; - -/** - * COUNTER_DEVICE_ENUM() - Initialize Counter enum extension - * @_name: Attribute name - * @_e: Pointer to a counter_device_enum_ext structure - * - * This should usually be used together with COUNTER_DEVICE_ENUM_AVAILABLE() - */ -#define COUNTER_DEVICE_ENUM(_name, _e) \ -{ \ - .name = (_name), \ - .read = counter_device_enum_read, \ - .write = counter_device_enum_write, \ - .priv = (_e) \ -} - -/** - * COUNTER_DEVICE_ENUM_AVAILABLE() - Initialize Counter enum available extension - * @_name: Attribute name ("_available" will be appended to the name) - * @_e: Pointer to a counter_device_enum_ext structure - * - * Creates a read only attribute that lists all the available enum items in a - * newline separated list. This should usually be used together with - * COUNTER_DEVICE_ENUM() - */ -#define COUNTER_DEVICE_ENUM_AVAILABLE(_name, _e) \ -{ \ - .name = (_name "_available"), \ - .read = counter_device_enum_available_read, \ - .priv = (_e) \ -} - /** * struct counter_device - Counter data structure * @name: name of the device as it appears in the datasheet * @parent: optional parent device providing the counters - * @device_state: internal device state container * @ops: callbacks from driver * @signals: array of Signals * @num_signals: number of Signals specified in @signals @@ -425,11 +301,11 @@ struct counter_device_enum_ext { * @ext: optional array of Counter device extensions * @num_ext: number of Counter device extensions specified in @ext * @priv: optional private data supplied by driver + * @dev: internal device structure */ struct counter_device { const char *name; struct device *parent; - struct counter_device_state *device_state; const struct counter_ops *ops; @@ -438,17 +314,159 @@ struct counter_device { struct counter_count *counts; size_t num_counts; - const struct counter_device_ext *ext; + struct counter_comp *ext; size_t num_ext; void *priv; + + struct device dev; }; int counter_register(struct counter_device *const counter); void counter_unregister(struct counter_device *const counter); int devm_counter_register(struct device *dev, struct counter_device *const counter); -void devm_counter_unregister(struct device *dev, - struct counter_device *const counter); + +#define COUNTER_COMP_DEVICE_U8(_name, _read, _write) \ +{ \ + .type = COUNTER_COMP_U8, \ + .name = (_name), \ + .device_u8_read = (_read), \ + .device_u8_write = (_write), \ +} +#define COUNTER_COMP_COUNT_U8(_name, _read, _write) \ +{ \ + .type = COUNTER_COMP_U8, \ + .name = (_name), \ + .count_u8_read = (_read), \ + .count_u8_write = (_write), \ +} +#define COUNTER_COMP_SIGNAL_U8(_name, _read, _write) \ +{ \ + .type = COUNTER_COMP_U8, \ + .name = (_name), \ + .signal_u8_read = (_read), \ + .signal_u8_write = (_write), \ +} + +#define COUNTER_COMP_DEVICE_U64(_name, _read, _write) \ +{ \ + .type = COUNTER_COMP_U64, \ + .name = (_name), \ + .device_u64_read = (_read), \ + .device_u64_write = (_write), \ +} +#define COUNTER_COMP_COUNT_U64(_name, _read, _write) \ +{ \ + .type = COUNTER_COMP_U64, \ + .name = (_name), \ + .count_u64_read = (_read), \ + .count_u64_write = (_write), \ +} +#define COUNTER_COMP_SIGNAL_U64(_name, _read, _write) \ +{ \ + .type = COUNTER_COMP_U64, \ + .name = (_name), \ + .signal_u64_read = (_read), \ + .signal_u64_write = (_write), \ +} + +#define COUNTER_COMP_DEVICE_BOOL(_name, _read, _write) \ +{ \ + .type = COUNTER_COMP_BOOL, \ + .name = (_name), \ + .device_u8_read = (_read), \ + .device_u8_write = (_write), \ +} +#define COUNTER_COMP_COUNT_BOOL(_name, _read, _write) \ +{ \ + .type = COUNTER_COMP_BOOL, \ + .name = (_name), \ + .count_u8_read = (_read), \ + .count_u8_write = (_write), \ +} +#define COUNTER_COMP_SIGNAL_BOOL(_name, _read, _write) \ +{ \ + .type = COUNTER_COMP_BOOL, \ + .name = (_name), \ + .signal_u8_read = (_read), \ + .signal_u8_write = (_write), \ +} + +struct counter_available { + union { + const u32 *enums; + const char *const *strs; + }; + size_t num_items; +}; + +#define DEFINE_COUNTER_AVAILABLE(_name, _enums) \ + struct counter_available _name = { \ + .enums = (_enums), \ + .num_items = ARRAY_SIZE(_enums), \ + } + +#define DEFINE_COUNTER_ENUM(_name, _strs) \ + struct counter_available _name = { \ + .strs = (_strs), \ + .num_items = ARRAY_SIZE(_strs), \ + } + +#define COUNTER_COMP_DEVICE_ENUM(_name, _get, _set, _available) \ +{ \ + .type = COUNTER_COMP_ENUM, \ + .name = (_name), \ + .device_u32_read = (_get), \ + .device_u32_write = (_set), \ + .priv = &(_available), \ +} +#define COUNTER_COMP_COUNT_ENUM(_name, _get, _set, _available) \ +{ \ + .type = COUNTER_COMP_ENUM, \ + .name = (_name), \ + .count_u32_read = (_get), \ + .count_u32_write = (_set), \ + .priv = &(_available), \ +} +#define COUNTER_COMP_SIGNAL_ENUM(_name, _get, _set, _available) \ +{ \ + .type = COUNTER_COMP_ENUM, \ + .name = (_name), \ + .signal_u32_read = (_get), \ + .signal_u32_write = (_set), \ + .priv = &(_available), \ +} + +#define COUNTER_COMP_CEILING(_read, _write) \ + COUNTER_COMP_COUNT_U64("ceiling", _read, _write) + +#define COUNTER_COMP_COUNT_MODE(_read, _write, _available) \ +{ \ + .type = COUNTER_COMP_COUNT_MODE, \ + .name = "count_mode", \ + .count_u32_read = (_read), \ + .count_u32_write = (_write), \ + .priv = &(_available), \ +} + +#define COUNTER_COMP_DIRECTION(_read) \ +{ \ + .type = COUNTER_COMP_COUNT_DIRECTION, \ + .name = "direction", \ + .count_u32_read = (_read), \ +} + +#define COUNTER_COMP_ENABLE(_read, _write) \ + COUNTER_COMP_COUNT_BOOL("enable", _read, _write) + +#define COUNTER_COMP_FLOOR(_read, _write) \ + COUNTER_COMP_COUNT_U64("floor", _read, _write) + +#define COUNTER_COMP_PRESET(_read, _write) \ + COUNTER_COMP_COUNT_U64("preset", _read, _write) + +#define COUNTER_COMP_PRESET_ENABLE(_read, _write) \ + COUNTER_COMP_COUNT_BOOL("preset_enable", _read, _write) #endif /* _COUNTER_H_ */ diff --git a/include/linux/counter_enum.h b/include/linux/counter_enum.h deleted file mode 100644 index 9f917298a88f..000000000000 --- a/include/linux/counter_enum.h +++ /dev/null @@ -1,45 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Counter interface enum functions - * Copyright (C) 2018 William Breathitt Gray - */ -#ifndef _COUNTER_ENUM_H_ -#define _COUNTER_ENUM_H_ - -#include - -struct counter_device; -struct counter_signal; -struct counter_count; - -ssize_t counter_signal_enum_read(struct counter_device *counter, - struct counter_signal *signal, void *priv, - char *buf); -ssize_t counter_signal_enum_write(struct counter_device *counter, - struct counter_signal *signal, void *priv, - const char *buf, size_t len); - -ssize_t counter_signal_enum_available_read(struct counter_device *counter, - struct counter_signal *signal, - void *priv, char *buf); - -ssize_t counter_count_enum_read(struct counter_device *counter, - struct counter_count *count, void *priv, - char *buf); -ssize_t counter_count_enum_write(struct counter_device *counter, - struct counter_count *count, void *priv, - const char *buf, size_t len); - -ssize_t counter_count_enum_available_read(struct counter_device *counter, - struct counter_count *count, - void *priv, char *buf); - -ssize_t counter_device_enum_read(struct counter_device *counter, void *priv, - char *buf); -ssize_t counter_device_enum_write(struct counter_device *counter, void *priv, - const char *buf, size_t len); - -ssize_t counter_device_enum_available_read(struct counter_device *counter, - void *priv, char *buf); - -#endif /* _COUNTER_ENUM_H_ */ -- cgit v1.2.3-70-g09d2 From 712392f558ef4280b67659fb2d52854dcfc7c8ba Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Fri, 27 Aug 2021 12:47:48 +0900 Subject: counter: Update counter.h comments to reflect sysfs internalization The Counter subsystem architecture and driver implementations have changed in order to handle Counter sysfs interactions in a more consistent way. This patch updates the Generic Counter interface header file comments to reflect the changes. Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/19da8ae0c05381b0967c8a334b67f86b814eb880.1630031207.git.vilhelm.gray@gmail.com Signed-off-by: Jonathan Cameron --- drivers/counter/counter-core.c | 3 ++ include/linux/counter.h | 62 ++++++++++++++++++++---------------------- 2 files changed, 33 insertions(+), 32 deletions(-) diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c index c533a6ff12cf..3cda2c47bacb 100644 --- a/drivers/counter/counter-core.c +++ b/drivers/counter/counter-core.c @@ -38,6 +38,9 @@ static struct bus_type counter_bus_type = { * This function registers a Counter to the system. A sysfs "counter" directory * will be created and populated with sysfs attributes correlating with the * Counter Signals, Synapses, and Counts respectively. + * + * RETURNS: + * 0 on success, negative error number on failure. */ int counter_register(struct counter_device *const counter) { diff --git a/include/linux/counter.h b/include/linux/counter.h index b69277f5c4c5..445f22d8bfe2 100644 --- a/include/linux/counter.h +++ b/include/linux/counter.h @@ -188,11 +188,10 @@ struct counter_comp { /** * struct counter_signal - Counter Signal node - * @id: unique ID used to identify signal - * @name: device-specific Signal name; ideally, this should match the name - * as it appears in the datasheet documentation - * @ext: optional array of Counter Signal extensions - * @num_ext: number of Counter Signal extensions specified in @ext + * @id: unique ID used to identify the Signal + * @name: device-specific Signal name + * @ext: optional array of Signal extensions + * @num_ext: number of Signal extensions specified in @ext */ struct counter_signal { int id; @@ -206,7 +205,7 @@ struct counter_signal { * struct counter_synapse - Counter Synapse node * @actions_list: array of available action modes * @num_actions: number of action modes specified in @actions_list - * @signal: pointer to associated signal + * @signal: pointer to the associated Signal */ struct counter_synapse { const enum counter_synapse_action *actions_list; @@ -217,15 +216,14 @@ struct counter_synapse { /** * struct counter_count - Counter Count node - * @id: unique ID used to identify Count - * @name: device-specific Count name; ideally, this should match - * the name as it appears in the datasheet documentation - * @functions_list: array available function modes + * @id: unique ID used to identify the Count + * @name: device-specific Count name + * @functions_list: array of available function modes * @num_functions: number of function modes specified in @functions_list - * @synapses: array of synapses for initialization - * @num_synapses: number of synapses specified in @synapses - * @ext: optional array of Counter Count extensions - * @num_ext: number of Counter Count extensions specified in @ext + * @synapses: array of Synapses for initialization + * @num_synapses: number of Synapses specified in @synapses + * @ext: optional array of Count extensions + * @num_ext: number of Count extensions specified in @ext */ struct counter_count { int id; @@ -243,27 +241,27 @@ struct counter_count { /** * struct counter_ops - Callbacks from driver - * @signal_read: optional read callback for Signal attribute. The read - * level of the respective Signal should be passed back via - * the level parameter. - * @count_read: optional read callback for Count attribute. The read - * value of the respective Count should be passed back via - * the val parameter. - * @count_write: optional write callback for Count attribute. The write - * value for the respective Count is passed in via the val + * @signal_read: optional read callback for Signals. The read level of + * the respective Signal should be passed back via the + * level parameter. + * @count_read: read callback for Counts. The read value of the + * respective Count should be passed back via the value + * parameter. + * @count_write: optional write callback for Counts. The write value for + * the respective Count is passed in via the value * parameter. * @function_read: read callback the Count function modes. The read * function mode of the respective Count should be passed * back via the function parameter. - * @function_write: write callback for Count function modes. The function - * mode to write for the respective Count is passed in via - * the function parameter. - * @action_read: read callback the Synapse action modes. The read action - * mode of the respective Synapse should be passed back via - * the action parameter. - * @action_write: write callback for Synapse action modes. The action mode - * to write for the respective Synapse is passed in via the - * action parameter. + * @function_write: optional write callback for Count function modes. The + * function mode to write for the respective Count is + * passed in via the function parameter. + * @action_read: optional read callback the Synapse action modes. The + * read action mode of the respective Synapse should be + * passed back via the action parameter. + * @action_write: optional write callback for Synapse action modes. The + * action mode to write for the respective Synapse is + * passed in via the action parameter. */ struct counter_ops { int (*signal_read)(struct counter_device *counter, @@ -291,7 +289,7 @@ struct counter_ops { /** * struct counter_device - Counter data structure - * @name: name of the device as it appears in the datasheet + * @name: name of the device * @parent: optional parent device providing the counters * @ops: callbacks from driver * @signals: array of Signals -- cgit v1.2.3-70-g09d2 From de8daf30af7b5b699b78d2f3323c963215d41cd7 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Fri, 27 Aug 2021 12:47:49 +0900 Subject: docs: counter: Update to reflect sysfs internalization The Counter subsystem architecture and driver implementations have changed in order to handle Counter sysfs interactions in a more consistent way. This patch updates the Generic Counter interface documentation to reflect the changes. Reviewed-by: David Lechner Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/91cded13c0145c0d3d0acfe765a2ccb6c9af7c3b.1630031207.git.vilhelm.gray@gmail.com Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-counter | 9 +- Documentation/driver-api/generic-counter.rst | 243 +++++++++++++++++++-------- 2 files changed, 185 insertions(+), 67 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-counter b/Documentation/ABI/testing/sysfs-bus-counter index 20fe5afd4f9e..dee79b606847 100644 --- a/Documentation/ABI/testing/sysfs-bus-counter +++ b/Documentation/ABI/testing/sysfs-bus-counter @@ -286,7 +286,14 @@ What: /sys/bus/counter/devices/counterX/signalY/signal KernelVersion: 5.2 Contact: linux-iio@vger.kernel.org Description: - Signal data of Signal Y represented as a string. + Signal level state of Signal Y. The following signal level + states are available: + + low: + Low level state. + + high: + High level state. What: /sys/bus/counter/devices/counterX/signalY/synchronous_mode KernelVersion: 5.2 diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst index 64fe7db080e5..f6397218aa4c 100644 --- a/Documentation/driver-api/generic-counter.rst +++ b/Documentation/driver-api/generic-counter.rst @@ -250,8 +250,8 @@ for defining a counter device. .. kernel-doc:: drivers/counter/counter.c :export: -Implementation -============== +Driver Implementation +===================== To support a counter device, a driver must first allocate the available Counter Signals via counter_signal structures. These Signals should @@ -267,25 +267,61 @@ respective counter_count structure. These counter_count structures are set to the counts array member of an allocated counter_device structure before the Counter is registered to the system. -Driver callbacks should be provided to the counter_device structure via -a constant counter_ops structure in order to communicate with the -device: to read and write various Signals and Counts, and to set and get -the "action mode" and "function mode" for various Synapses and Counts -respectively. +Driver callbacks must be provided to the counter_device structure in +order to communicate with the device: to read and write various Signals +and Counts, and to set and get the "action mode" and "function mode" for +various Synapses and Counts respectively. A defined counter_device structure may be registered to the system by passing it to the counter_register function, and unregistered by passing it to the counter_unregister function. Similarly, the -devm_counter_register and devm_counter_unregister functions may be used -if device memory-managed registration is desired. - -Extension sysfs attributes can be created for auxiliary functionality -and data by passing in defined counter_device_ext, counter_count_ext, -and counter_signal_ext structures. In these cases, the -counter_device_ext structure is used for global/miscellaneous exposure -and configuration of the respective Counter device, while the -counter_count_ext and counter_signal_ext structures allow for auxiliary -exposure and configuration of a specific Count or Signal respectively. +devm_counter_register function may be used if device memory-managed +registration is desired. + +The struct counter_comp structure is used to define counter extensions +for Signals, Synapses, and Counts. + +The "type" member specifies the type of high-level data (e.g. BOOL, +COUNT_DIRECTION, etc.) handled by this extension. The "``*_read``" and +"``*_write``" members can then be set by the counter device driver with +callbacks to handle that data using native C data types (i.e. u8, u64, +etc.). + +Convenience macros such as ``COUNTER_COMP_COUNT_U64`` are provided for +use by driver authors. In particular, driver authors are expected to use +the provided macros for standard Counter subsystem attributes in order +to maintain a consistent interface for userspace. For example, a counter +device driver may define several standard attributes like so:: + + struct counter_comp count_ext[] = { + COUNTER_COMP_DIRECTION(count_direction_read), + COUNTER_COMP_ENABLE(count_enable_read, count_enable_write), + COUNTER_COMP_CEILING(count_ceiling_read, count_ceiling_write), + }; + +This makes it simple to see, add, and modify the attributes that are +supported by this driver ("direction", "enable", and "ceiling") and to +maintain this code without getting lost in a web of struct braces. + +Callbacks must match the function type expected for the respective +component or extension. These function types are defined in the struct +counter_comp structure as the "``*_read``" and "``*_write``" union +members. + +The corresponding callback prototypes for the extensions mentioned in +the previous example above would be:: + + int count_direction_read(struct counter_device *counter, + struct counter_count *count, + enum counter_count_direction *direction); + int count_enable_read(struct counter_device *counter, + struct counter_count *count, u8 *enable); + int count_enable_write(struct counter_device *counter, + struct counter_count *count, u8 enable); + int count_ceiling_read(struct counter_device *counter, + struct counter_count *count, u64 *ceiling); + int count_ceiling_write(struct counter_device *counter, + struct counter_count *count, u64 ceiling); Determining the type of extension to create is a matter of scope. @@ -313,52 +349,127 @@ Determining the type of extension to create is a matter of scope. chip overheated via a device extension called "error_overtemp": /sys/bus/counter/devices/counterX/error_overtemp -Architecture -============ - -When the Generic Counter interface counter module is loaded, the -counter_init function is called which registers a bus_type named -"counter" to the system. Subsequently, when the module is unloaded, the -counter_exit function is called which unregisters the bus_type named -"counter" from the system. - -Counter devices are registered to the system via the counter_register -function, and later removed via the counter_unregister function. The -counter_register function establishes a unique ID for the Counter -device and creates a respective sysfs directory, where X is the -mentioned unique ID: - - /sys/bus/counter/devices/counterX - -Sysfs attributes are created within the counterX directory to expose -functionality, configurations, and data relating to the Counts, Signals, -and Synapses of the Counter device, as well as options and information -for the Counter device itself. - -Each Signal has a directory created to house its relevant sysfs -attributes, where Y is the unique ID of the respective Signal: - - /sys/bus/counter/devices/counterX/signalY - -Similarly, each Count has a directory created to house its relevant -sysfs attributes, where Y is the unique ID of the respective Count: - - /sys/bus/counter/devices/counterX/countY - -For a more detailed breakdown of the available Generic Counter interface -sysfs attributes, please refer to the -Documentation/ABI/testing/sysfs-bus-counter file. - -The Signals and Counts associated with the Counter device are registered -to the system as well by the counter_register function. The -signal_read/signal_write driver callbacks are associated with their -respective Signal attributes, while the count_read/count_write and -function_get/function_set driver callbacks are associated with their -respective Count attributes; similarly, the same is true for the -action_get/action_set driver callbacks and their respective Synapse -attributes. If a driver callback is left undefined, then the respective -read/write permission is left disabled for the relevant attributes. - -Similarly, extension sysfs attributes are created for the defined -counter_device_ext, counter_count_ext, and counter_signal_ext -structures that are passed in. +Subsystem Architecture +====================== + +Counter drivers pass and take data natively (i.e. ``u8``, ``u64``, etc.) +and the shared counter module handles the translation between the sysfs +interface. This guarantees a standard userspace interface for all +counter drivers, and enables a Generic Counter chrdev interface via a +generalized device driver ABI. + +A high-level view of how a count value is passed down from a counter +driver is exemplified by the following. The driver callbacks are first +registered to the Counter core component for use by the Counter +userspace interface components:: + + Driver callbacks registration: + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +----------------------------+ + | Counter device driver | + +----------------------------+ + | Processes data from device | + +----------------------------+ + | + ------------------- + / driver callbacks / + ------------------- + | + V + +----------------------+ + | Counter core | + +----------------------+ + | Routes device driver | + | callbacks to the | + | userspace interfaces | + +----------------------+ + | + ------------------- + / driver callbacks / + ------------------- + | + +---------------+ + | + V + +--------------------+ + | Counter sysfs | + +--------------------+ + | Translates to the | + | standard Counter | + | sysfs output | + +--------------------+ + +Thereafter, data can be transferred directly between the Counter device +driver and Counter userspace interface:: + + Count data request: + ~~~~~~~~~~~~~~~~~~~ + ---------------------- + / Counter device \ + +----------------------+ + | Count register: 0x28 | + +----------------------+ + | + ----------------- + / raw count data / + ----------------- + | + V + +----------------------------+ + | Counter device driver | + +----------------------------+ + | Processes data from device | + |----------------------------| + | Type: u64 | + | Value: 42 | + +----------------------------+ + | + ---------- + / u64 / + ---------- + | + +---------------+ + | + V + +--------------------+ + | Counter sysfs | + +--------------------+ + | Translates to the | + | standard Counter | + | sysfs output | + |--------------------| + | Type: const char * | + | Value: "42" | + +--------------------+ + | + --------------- + / const char * / + --------------- + | + V + +--------------------------------------------------+ + | `/sys/bus/counter/devices/counterX/countY/count` | + +--------------------------------------------------+ + \ Count: "42" / + -------------------------------------------------- + +There are three primary components involved: + +Counter device driver +--------------------- +Communicates with the hardware device to read/write data; e.g. counter +drivers for quadrature encoders, timers, etc. + +Counter core +------------ +Registers the counter device driver to the system so that the respective +callbacks are called during userspace interaction. + +Counter sysfs +------------- +Translates counter data to the standard Counter sysfs interface format +and vice versa. + +Please refer to the ``Documentation/ABI/testing/sysfs-bus-counter`` file +for a detailed breakdown of the available Generic Counter interface +sysfs attributes. -- cgit v1.2.3-70-g09d2 From e65c26f413718ed2e6d788491adcd8cebc0f44b6 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Wed, 29 Sep 2021 12:15:58 +0900 Subject: counter: Move counter enums to uapi header This is in preparation for a subsequent patch implementing a character device interface for the Counter subsystem. Reviewed-by: David Lechner Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/962a5f2027fafcf4f77c10e1baf520463960d1ee.1632884256.git.vilhelm.gray@gmail.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 1 + include/linux/counter.h | 42 +-------------------------------- include/uapi/linux/counter.h | 56 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 41 deletions(-) create mode 100644 include/uapi/linux/counter.h diff --git a/MAINTAINERS b/MAINTAINERS index b98a34fcec4b..8310a42abdab 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4804,6 +4804,7 @@ F: Documentation/ABI/testing/sysfs-bus-counter F: Documentation/driver-api/generic-counter.rst F: drivers/counter/ F: include/linux/counter.h +F: include/uapi/linux/counter.h CP2615 I2C DRIVER M: Bence Csókás diff --git a/include/linux/counter.h b/include/linux/counter.h index 445f22d8bfe2..7c9f7e23953a 100644 --- a/include/linux/counter.h +++ b/include/linux/counter.h @@ -9,6 +9,7 @@ #include #include #include +#include struct counter_device; struct counter_count; @@ -27,47 +28,6 @@ enum counter_comp_type { COUNTER_COMP_COUNT_MODE, }; -enum counter_scope { - COUNTER_SCOPE_DEVICE, - COUNTER_SCOPE_SIGNAL, - COUNTER_SCOPE_COUNT, -}; - -enum counter_count_direction { - COUNTER_COUNT_DIRECTION_FORWARD, - COUNTER_COUNT_DIRECTION_BACKWARD, -}; - -enum counter_count_mode { - COUNTER_COUNT_MODE_NORMAL, - COUNTER_COUNT_MODE_RANGE_LIMIT, - COUNTER_COUNT_MODE_NON_RECYCLE, - COUNTER_COUNT_MODE_MODULO_N, -}; - -enum counter_function { - COUNTER_FUNCTION_INCREASE, - COUNTER_FUNCTION_DECREASE, - COUNTER_FUNCTION_PULSE_DIRECTION, - COUNTER_FUNCTION_QUADRATURE_X1_A, - COUNTER_FUNCTION_QUADRATURE_X1_B, - COUNTER_FUNCTION_QUADRATURE_X2_A, - COUNTER_FUNCTION_QUADRATURE_X2_B, - COUNTER_FUNCTION_QUADRATURE_X4, -}; - -enum counter_signal_level { - COUNTER_SIGNAL_LEVEL_LOW, - COUNTER_SIGNAL_LEVEL_HIGH, -}; - -enum counter_synapse_action { - COUNTER_SYNAPSE_ACTION_NONE, - COUNTER_SYNAPSE_ACTION_RISING_EDGE, - COUNTER_SYNAPSE_ACTION_FALLING_EDGE, - COUNTER_SYNAPSE_ACTION_BOTH_EDGES, -}; - /** * struct counter_comp - Counter component node * @type: Counter component data type diff --git a/include/uapi/linux/counter.h b/include/uapi/linux/counter.h new file mode 100644 index 000000000000..6113938a6044 --- /dev/null +++ b/include/uapi/linux/counter.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Userspace ABI for Counter character devices + * Copyright (C) 2020 William Breathitt Gray + */ +#ifndef _UAPI_COUNTER_H_ +#define _UAPI_COUNTER_H_ + +/* Component scope definitions */ +enum counter_scope { + COUNTER_SCOPE_DEVICE, + COUNTER_SCOPE_SIGNAL, + COUNTER_SCOPE_COUNT, +}; + +/* Count direction values */ +enum counter_count_direction { + COUNTER_COUNT_DIRECTION_FORWARD, + COUNTER_COUNT_DIRECTION_BACKWARD, +}; + +/* Count mode values */ +enum counter_count_mode { + COUNTER_COUNT_MODE_NORMAL, + COUNTER_COUNT_MODE_RANGE_LIMIT, + COUNTER_COUNT_MODE_NON_RECYCLE, + COUNTER_COUNT_MODE_MODULO_N, +}; + +/* Count function values */ +enum counter_function { + COUNTER_FUNCTION_INCREASE, + COUNTER_FUNCTION_DECREASE, + COUNTER_FUNCTION_PULSE_DIRECTION, + COUNTER_FUNCTION_QUADRATURE_X1_A, + COUNTER_FUNCTION_QUADRATURE_X1_B, + COUNTER_FUNCTION_QUADRATURE_X2_A, + COUNTER_FUNCTION_QUADRATURE_X2_B, + COUNTER_FUNCTION_QUADRATURE_X4, +}; + +/* Signal values */ +enum counter_signal_level { + COUNTER_SIGNAL_LEVEL_LOW, + COUNTER_SIGNAL_LEVEL_HIGH, +}; + +/* Action mode values */ +enum counter_synapse_action { + COUNTER_SYNAPSE_ACTION_NONE, + COUNTER_SYNAPSE_ACTION_RISING_EDGE, + COUNTER_SYNAPSE_ACTION_FALLING_EDGE, + COUNTER_SYNAPSE_ACTION_BOTH_EDGES, +}; + +#endif /* _UAPI_COUNTER_H_ */ -- cgit v1.2.3-70-g09d2 From b6c50affda5957a3629b149a91c7f6688ffce7f7 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Wed, 29 Sep 2021 12:15:59 +0900 Subject: counter: Add character device interface This patch introduces a character device interface for the Counter subsystem. Device data is exposed through standard character device read operations. Device data is gathered when a Counter event is pushed by the respective Counter device driver. Configuration is handled via ioctl operations on the respective Counter character device node. Cc: David Lechner Cc: Gwendal Grignou Cc: Dan Carpenter Cc: Oleksij Rempel Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/b8b8c64b4065aedff43699ad1f0e2f8d1419c15b.1632884256.git.vilhelm.gray@gmail.com Signed-off-by: Jonathan Cameron --- drivers/counter/Makefile | 2 +- drivers/counter/counter-chrdev.c | 578 +++++++++++++++++++++++++++++++++++++++ drivers/counter/counter-chrdev.h | 14 + drivers/counter/counter-core.c | 56 +++- include/linux/counter.h | 56 ++++ include/uapi/linux/counter.h | 98 +++++++ 6 files changed, 798 insertions(+), 6 deletions(-) create mode 100644 drivers/counter/counter-chrdev.c create mode 100644 drivers/counter/counter-chrdev.h diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile index 1ab7e087fdc2..8fde6c100ebc 100644 --- a/drivers/counter/Makefile +++ b/drivers/counter/Makefile @@ -4,7 +4,7 @@ # obj-$(CONFIG_COUNTER) += counter.o -counter-y := counter-core.o counter-sysfs.o +counter-y := counter-core.o counter-sysfs.o counter-chrdev.o obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o obj-$(CONFIG_INTERRUPT_CNT) += interrupt-cnt.o diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c new file mode 100644 index 000000000000..967c94ae95bb --- /dev/null +++ b/drivers/counter/counter-chrdev.c @@ -0,0 +1,578 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Generic Counter character device interface + * Copyright (C) 2020 William Breathitt Gray + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "counter-chrdev.h" + +struct counter_comp_node { + struct list_head l; + struct counter_component component; + struct counter_comp comp; + void *parent; +}; + +#define counter_comp_read_is_equal(a, b) \ + (a.action_read == b.action_read || \ + a.device_u8_read == b.device_u8_read || \ + a.count_u8_read == b.count_u8_read || \ + a.signal_u8_read == b.signal_u8_read || \ + a.device_u32_read == b.device_u32_read || \ + a.count_u32_read == b.count_u32_read || \ + a.signal_u32_read == b.signal_u32_read || \ + a.device_u64_read == b.device_u64_read || \ + a.count_u64_read == b.count_u64_read || \ + a.signal_u64_read == b.signal_u64_read) + +#define counter_comp_read_is_set(comp) \ + (comp.action_read || \ + comp.device_u8_read || \ + comp.count_u8_read || \ + comp.signal_u8_read || \ + comp.device_u32_read || \ + comp.count_u32_read || \ + comp.signal_u32_read || \ + comp.device_u64_read || \ + comp.count_u64_read || \ + comp.signal_u64_read) + +static ssize_t counter_chrdev_read(struct file *filp, char __user *buf, + size_t len, loff_t *f_ps) +{ + struct counter_device *const counter = filp->private_data; + int err; + unsigned int copied; + + if (!counter->ops) + return -ENODEV; + + if (len < sizeof(struct counter_event)) + return -EINVAL; + + do { + if (kfifo_is_empty(&counter->events)) { + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + err = wait_event_interruptible(counter->events_wait, + !kfifo_is_empty(&counter->events) || + !counter->ops); + if (err < 0) + return err; + if (!counter->ops) + return -ENODEV; + } + + if (mutex_lock_interruptible(&counter->events_lock)) + return -ERESTARTSYS; + err = kfifo_to_user(&counter->events, buf, len, &copied); + mutex_unlock(&counter->events_lock); + if (err < 0) + return err; + } while (!copied); + + return copied; +} + +static __poll_t counter_chrdev_poll(struct file *filp, + struct poll_table_struct *pollt) +{ + struct counter_device *const counter = filp->private_data; + __poll_t events = 0; + + if (!counter->ops) + return events; + + poll_wait(filp, &counter->events_wait, pollt); + + if (!kfifo_is_empty(&counter->events)) + events = EPOLLIN | EPOLLRDNORM; + + return events; +} + +static void counter_events_list_free(struct list_head *const events_list) +{ + struct counter_event_node *p, *n; + struct counter_comp_node *q, *o; + + list_for_each_entry_safe(p, n, events_list, l) { + /* Free associated component nodes */ + list_for_each_entry_safe(q, o, &p->comp_list, l) { + list_del(&q->l); + kfree(q); + } + + /* Free event node */ + list_del(&p->l); + kfree(p); + } +} + +static int counter_set_event_node(struct counter_device *const counter, + struct counter_watch *const watch, + const struct counter_comp_node *const cfg) +{ + struct counter_event_node *event_node; + int err = 0; + struct counter_comp_node *comp_node; + + /* Search for event in the list */ + list_for_each_entry(event_node, &counter->next_events_list, l) + if (event_node->event == watch->event && + event_node->channel == watch->channel) + break; + + /* If event is not already in the list */ + if (&event_node->l == &counter->next_events_list) { + /* Allocate new event node */ + event_node = kmalloc(sizeof(*event_node), GFP_KERNEL); + if (!event_node) + return -ENOMEM; + + /* Configure event node and add to the list */ + event_node->event = watch->event; + event_node->channel = watch->channel; + INIT_LIST_HEAD(&event_node->comp_list); + list_add(&event_node->l, &counter->next_events_list); + } + + /* Check if component watch has already been set before */ + list_for_each_entry(comp_node, &event_node->comp_list, l) + if (comp_node->parent == cfg->parent && + counter_comp_read_is_equal(comp_node->comp, cfg->comp)) { + err = -EINVAL; + goto exit_free_event_node; + } + + /* Allocate component node */ + comp_node = kmalloc(sizeof(*comp_node), GFP_KERNEL); + if (!comp_node) { + err = -ENOMEM; + goto exit_free_event_node; + } + *comp_node = *cfg; + + /* Add component node to event node */ + list_add_tail(&comp_node->l, &event_node->comp_list); + +exit_free_event_node: + /* Free event node if no one else is watching */ + if (list_empty(&event_node->comp_list)) { + list_del(&event_node->l); + kfree(event_node); + } + + return err; +} + +static int counter_enable_events(struct counter_device *const counter) +{ + unsigned long flags; + int err = 0; + + mutex_lock(&counter->n_events_list_lock); + spin_lock_irqsave(&counter->events_list_lock, flags); + + counter_events_list_free(&counter->events_list); + list_replace_init(&counter->next_events_list, + &counter->events_list); + + if (counter->ops->events_configure) + err = counter->ops->events_configure(counter); + + spin_unlock_irqrestore(&counter->events_list_lock, flags); + mutex_unlock(&counter->n_events_list_lock); + + return err; +} + +static int counter_disable_events(struct counter_device *const counter) +{ + unsigned long flags; + int err = 0; + + spin_lock_irqsave(&counter->events_list_lock, flags); + + counter_events_list_free(&counter->events_list); + + if (counter->ops->events_configure) + err = counter->ops->events_configure(counter); + + spin_unlock_irqrestore(&counter->events_list_lock, flags); + + mutex_lock(&counter->n_events_list_lock); + + counter_events_list_free(&counter->next_events_list); + + mutex_unlock(&counter->n_events_list_lock); + + return err; +} + +static int counter_add_watch(struct counter_device *const counter, + const unsigned long arg) +{ + void __user *const uwatch = (void __user *)arg; + struct counter_watch watch; + struct counter_comp_node comp_node = {}; + size_t parent, id; + struct counter_comp *ext; + size_t num_ext; + int err = 0; + + if (copy_from_user(&watch, uwatch, sizeof(watch))) + return -EFAULT; + + if (watch.component.type == COUNTER_COMPONENT_NONE) + goto no_component; + + parent = watch.component.parent; + + /* Configure parent component info for comp node */ + switch (watch.component.scope) { + case COUNTER_SCOPE_DEVICE: + ext = counter->ext; + num_ext = counter->num_ext; + break; + case COUNTER_SCOPE_SIGNAL: + if (parent >= counter->num_signals) + return -EINVAL; + parent = array_index_nospec(parent, counter->num_signals); + + comp_node.parent = counter->signals + parent; + + ext = counter->signals[parent].ext; + num_ext = counter->signals[parent].num_ext; + break; + case COUNTER_SCOPE_COUNT: + if (parent >= counter->num_counts) + return -EINVAL; + parent = array_index_nospec(parent, counter->num_counts); + + comp_node.parent = counter->counts + parent; + + ext = counter->counts[parent].ext; + num_ext = counter->counts[parent].num_ext; + break; + default: + return -EINVAL; + } + + id = watch.component.id; + + /* Configure component info for comp node */ + switch (watch.component.type) { + case COUNTER_COMPONENT_SIGNAL: + if (watch.component.scope != COUNTER_SCOPE_SIGNAL) + return -EINVAL; + + comp_node.comp.type = COUNTER_COMP_SIGNAL_LEVEL; + comp_node.comp.signal_u32_read = counter->ops->signal_read; + break; + case COUNTER_COMPONENT_COUNT: + if (watch.component.scope != COUNTER_SCOPE_COUNT) + return -EINVAL; + + comp_node.comp.type = COUNTER_COMP_U64; + comp_node.comp.count_u64_read = counter->ops->count_read; + break; + case COUNTER_COMPONENT_FUNCTION: + if (watch.component.scope != COUNTER_SCOPE_COUNT) + return -EINVAL; + + comp_node.comp.type = COUNTER_COMP_FUNCTION; + comp_node.comp.count_u32_read = counter->ops->function_read; + break; + case COUNTER_COMPONENT_SYNAPSE_ACTION: + if (watch.component.scope != COUNTER_SCOPE_COUNT) + return -EINVAL; + if (id >= counter->counts[parent].num_synapses) + return -EINVAL; + id = array_index_nospec(id, counter->counts[parent].num_synapses); + + comp_node.comp.type = COUNTER_COMP_SYNAPSE_ACTION; + comp_node.comp.action_read = counter->ops->action_read; + comp_node.comp.priv = counter->counts[parent].synapses + id; + break; + case COUNTER_COMPONENT_EXTENSION: + if (id >= num_ext) + return -EINVAL; + id = array_index_nospec(id, num_ext); + + comp_node.comp = ext[id]; + break; + default: + return -EINVAL; + } + if (!counter_comp_read_is_set(comp_node.comp)) + return -EOPNOTSUPP; + +no_component: + mutex_lock(&counter->n_events_list_lock); + + if (counter->ops->watch_validate) { + err = counter->ops->watch_validate(counter, &watch); + if (err < 0) + goto err_exit; + } + + comp_node.component = watch.component; + + err = counter_set_event_node(counter, &watch, &comp_node); + +err_exit: + mutex_unlock(&counter->n_events_list_lock); + + return err; +} + +static long counter_chrdev_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct counter_device *const counter = filp->private_data; + int ret = -ENODEV; + + mutex_lock(&counter->ops_exist_lock); + + if (!counter->ops) + goto out_unlock; + + switch (cmd) { + case COUNTER_ADD_WATCH_IOCTL: + ret = counter_add_watch(counter, arg); + break; + case COUNTER_ENABLE_EVENTS_IOCTL: + ret = counter_enable_events(counter); + break; + case COUNTER_DISABLE_EVENTS_IOCTL: + ret = counter_disable_events(counter); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + +out_unlock: + mutex_unlock(&counter->ops_exist_lock); + + return ret; +} + +static int counter_chrdev_open(struct inode *inode, struct file *filp) +{ + struct counter_device *const counter = container_of(inode->i_cdev, + typeof(*counter), + chrdev); + + /* Ensure chrdev is not opened more than 1 at a time */ + if (!atomic_add_unless(&counter->chrdev_lock, 1, 1)) + return -EBUSY; + + get_device(&counter->dev); + filp->private_data = counter; + + return nonseekable_open(inode, filp); +} + +static int counter_chrdev_release(struct inode *inode, struct file *filp) +{ + struct counter_device *const counter = filp->private_data; + int ret = 0; + + mutex_lock(&counter->ops_exist_lock); + + if (!counter->ops) { + /* Free any lingering held memory */ + counter_events_list_free(&counter->events_list); + counter_events_list_free(&counter->next_events_list); + ret = -ENODEV; + goto out_unlock; + } + + ret = counter_disable_events(counter); + if (ret < 0) { + mutex_unlock(&counter->ops_exist_lock); + return ret; + } + +out_unlock: + mutex_unlock(&counter->ops_exist_lock); + + put_device(&counter->dev); + atomic_dec(&counter->chrdev_lock); + + return ret; +} + +static const struct file_operations counter_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = counter_chrdev_read, + .poll = counter_chrdev_poll, + .unlocked_ioctl = counter_chrdev_ioctl, + .open = counter_chrdev_open, + .release = counter_chrdev_release, +}; + +int counter_chrdev_add(struct counter_device *const counter) +{ + /* Initialize Counter events lists */ + INIT_LIST_HEAD(&counter->events_list); + INIT_LIST_HEAD(&counter->next_events_list); + spin_lock_init(&counter->events_list_lock); + mutex_init(&counter->n_events_list_lock); + init_waitqueue_head(&counter->events_wait); + mutex_init(&counter->events_lock); + + /* Initialize character device */ + atomic_set(&counter->chrdev_lock, 0); + cdev_init(&counter->chrdev, &counter_fops); + + /* Allocate Counter events queue */ + return kfifo_alloc(&counter->events, 64, GFP_KERNEL); +} + +void counter_chrdev_remove(struct counter_device *const counter) +{ + kfifo_free(&counter->events); +} + +static int counter_get_data(struct counter_device *const counter, + const struct counter_comp_node *const comp_node, + u64 *const value) +{ + const struct counter_comp *const comp = &comp_node->comp; + void *const parent = comp_node->parent; + u8 value_u8 = 0; + u32 value_u32 = 0; + int ret; + + if (comp_node->component.type == COUNTER_COMPONENT_NONE) + return 0; + + switch (comp->type) { + case COUNTER_COMP_U8: + case COUNTER_COMP_BOOL: + switch (comp_node->component.scope) { + case COUNTER_SCOPE_DEVICE: + ret = comp->device_u8_read(counter, &value_u8); + break; + case COUNTER_SCOPE_SIGNAL: + ret = comp->signal_u8_read(counter, parent, &value_u8); + break; + case COUNTER_SCOPE_COUNT: + ret = comp->count_u8_read(counter, parent, &value_u8); + break; + } + *value = value_u8; + return ret; + case COUNTER_COMP_SIGNAL_LEVEL: + case COUNTER_COMP_FUNCTION: + case COUNTER_COMP_ENUM: + case COUNTER_COMP_COUNT_DIRECTION: + case COUNTER_COMP_COUNT_MODE: + switch (comp_node->component.scope) { + case COUNTER_SCOPE_DEVICE: + ret = comp->device_u32_read(counter, &value_u32); + break; + case COUNTER_SCOPE_SIGNAL: + ret = comp->signal_u32_read(counter, parent, + &value_u32); + break; + case COUNTER_SCOPE_COUNT: + ret = comp->count_u32_read(counter, parent, &value_u32); + break; + } + *value = value_u32; + return ret; + case COUNTER_COMP_U64: + switch (comp_node->component.scope) { + case COUNTER_SCOPE_DEVICE: + return comp->device_u64_read(counter, value); + case COUNTER_SCOPE_SIGNAL: + return comp->signal_u64_read(counter, parent, value); + case COUNTER_SCOPE_COUNT: + return comp->count_u64_read(counter, parent, value); + default: + return -EINVAL; + } + case COUNTER_COMP_SYNAPSE_ACTION: + ret = comp->action_read(counter, parent, comp->priv, + &value_u32); + *value = value_u32; + return ret; + default: + return -EINVAL; + } +} + +/** + * counter_push_event - queue event for userspace reading + * @counter: pointer to Counter structure + * @event: triggered event + * @channel: event channel + * + * Note: If no one is watching for the respective event, it is silently + * discarded. + */ +void counter_push_event(struct counter_device *const counter, const u8 event, + const u8 channel) +{ + struct counter_event ev; + unsigned int copied = 0; + unsigned long flags; + struct counter_event_node *event_node; + struct counter_comp_node *comp_node; + + ev.timestamp = ktime_get_ns(); + ev.watch.event = event; + ev.watch.channel = channel; + + /* Could be in an interrupt context, so use a spin lock */ + spin_lock_irqsave(&counter->events_list_lock, flags); + + /* Search for event in the list */ + list_for_each_entry(event_node, &counter->events_list, l) + if (event_node->event == event && + event_node->channel == channel) + break; + + /* If event is not in the list */ + if (&event_node->l == &counter->events_list) + goto exit_early; + + /* Read and queue relevant comp for userspace */ + list_for_each_entry(comp_node, &event_node->comp_list, l) { + ev.watch.component = comp_node->component; + ev.status = -counter_get_data(counter, comp_node, &ev.value); + + copied += kfifo_in(&counter->events, &ev, 1); + } + +exit_early: + spin_unlock_irqrestore(&counter->events_list_lock, flags); + + if (copied) + wake_up_poll(&counter->events_wait, EPOLLIN); +} +EXPORT_SYMBOL_GPL(counter_push_event); diff --git a/drivers/counter/counter-chrdev.h b/drivers/counter/counter-chrdev.h new file mode 100644 index 000000000000..5529d16703c4 --- /dev/null +++ b/drivers/counter/counter-chrdev.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Counter character device interface + * Copyright (C) 2020 William Breathitt Gray + */ +#ifndef _COUNTER_CHRDEV_H_ +#define _COUNTER_CHRDEV_H_ + +#include + +int counter_chrdev_add(struct counter_device *const counter); +void counter_chrdev_remove(struct counter_device *const counter); + +#endif /* _COUNTER_CHRDEV_H_ */ diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c index 3cda2c47bacb..5acc54539623 100644 --- a/drivers/counter/counter-core.c +++ b/drivers/counter/counter-core.c @@ -3,14 +3,22 @@ * Generic Counter interface * Copyright (C) 2020 William Breathitt Gray */ +#include #include #include +#include #include +#include #include #include #include +#include #include +#include +#include +#include +#include "counter-chrdev.h" #include "counter-sysfs.h" /* Provides a unique ID for each counter device */ @@ -18,6 +26,9 @@ static DEFINE_IDA(counter_ida); static void counter_device_release(struct device *dev) { + struct counter_device *const counter = dev_get_drvdata(dev); + + counter_chrdev_remove(counter); ida_free(&counter_ida, dev->id); } @@ -31,6 +42,8 @@ static struct bus_type counter_bus_type = { .dev_name = "counter", }; +static dev_t counter_devt; + /** * counter_register - register Counter to the system * @counter: pointer to Counter to register @@ -53,10 +66,13 @@ int counter_register(struct counter_device *const counter) if (id < 0) return id; + mutex_init(&counter->ops_exist_lock); + /* Configure device structure for Counter */ dev->id = id; dev->type = &counter_device_type; dev->bus = &counter_bus_type; + dev->devt = MKDEV(MAJOR(counter_devt), id); if (counter->parent) { dev->parent = counter->parent; dev->of_node = counter->parent->of_node; @@ -64,18 +80,22 @@ int counter_register(struct counter_device *const counter) device_initialize(dev); dev_set_drvdata(dev, counter); - /* Add Counter sysfs attributes */ err = counter_sysfs_add(counter); if (err < 0) goto err_free_id; - /* Add device to system */ - err = device_add(dev); + err = counter_chrdev_add(counter); if (err < 0) goto err_free_id; + err = cdev_device_add(&counter->chrdev, dev); + if (err < 0) + goto err_remove_chrdev; + return 0; +err_remove_chrdev: + counter_chrdev_remove(counter); err_free_id: put_device(dev); return err; @@ -93,7 +113,16 @@ void counter_unregister(struct counter_device *const counter) if (!counter) return; - device_unregister(&counter->dev); + cdev_device_del(&counter->chrdev, &counter->dev); + + mutex_lock(&counter->ops_exist_lock); + + counter->ops = NULL; + wake_up(&counter->events_wait); + + mutex_unlock(&counter->ops_exist_lock); + + put_device(&counter->dev); } EXPORT_SYMBOL_GPL(counter_unregister); @@ -127,13 +156,30 @@ int devm_counter_register(struct device *dev, } EXPORT_SYMBOL_GPL(devm_counter_register); +#define COUNTER_DEV_MAX 256 + static int __init counter_init(void) { - return bus_register(&counter_bus_type); + int err; + + err = bus_register(&counter_bus_type); + if (err < 0) + return err; + + err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX, "counter"); + if (err < 0) + goto err_unregister_bus; + + return 0; + +err_unregister_bus: + bus_unregister(&counter_bus_type); + return err; } static void __exit counter_exit(void) { + unregister_chrdev_region(counter_devt, COUNTER_DEV_MAX); bus_unregister(&counter_bus_type); } diff --git a/include/linux/counter.h b/include/linux/counter.h index 7c9f7e23953a..22b14a552b1d 100644 --- a/include/linux/counter.h +++ b/include/linux/counter.h @@ -6,9 +6,14 @@ #ifndef _COUNTER_H_ #define _COUNTER_H_ +#include #include #include +#include +#include +#include #include +#include #include struct counter_device; @@ -199,6 +204,20 @@ struct counter_count { size_t num_ext; }; +/** + * struct counter_event_node - Counter Event node + * @l: list of current watching Counter events + * @event: event that triggers + * @channel: event channel + * @comp_list: list of components to watch when event triggers + */ +struct counter_event_node { + struct list_head l; + u8 event; + u8 channel; + struct list_head comp_list; +}; + /** * struct counter_ops - Callbacks from driver * @signal_read: optional read callback for Signals. The read level of @@ -222,6 +241,13 @@ struct counter_count { * @action_write: optional write callback for Synapse action modes. The * action mode to write for the respective Synapse is * passed in via the action parameter. + * @events_configure: optional write callback to configure events. The list of + * struct counter_event_node may be accessed via the + * events_list member of the counter parameter. + * @watch_validate: optional callback to validate a watch. The Counter + * component watch configuration is passed in via the watch + * parameter. A return value of 0 indicates a valid Counter + * component watch configuration. */ struct counter_ops { int (*signal_read)(struct counter_device *counter, @@ -245,6 +271,9 @@ struct counter_ops { struct counter_count *count, struct counter_synapse *synapse, enum counter_synapse_action action); + int (*events_configure)(struct counter_device *counter); + int (*watch_validate)(struct counter_device *counter, + const struct counter_watch *watch); }; /** @@ -260,6 +289,16 @@ struct counter_ops { * @num_ext: number of Counter device extensions specified in @ext * @priv: optional private data supplied by driver * @dev: internal device structure + * @chrdev: internal character device structure + * @events_list: list of current watching Counter events + * @events_list_lock: lock to protect Counter events list operations + * @next_events_list: list of next watching Counter events + * @n_events_list_lock: lock to protect Counter next events list operations + * @events: queue of detected Counter events + * @events_wait: wait queue to allow blocking reads of Counter events + * @events_lock: lock to protect Counter events queue read operations + * @chrdev_lock: lock to limit chrdev to a single open at a time + * @ops_exist_lock: lock to prevent use during removal */ struct counter_device { const char *name; @@ -278,12 +317,29 @@ struct counter_device { void *priv; struct device dev; + struct cdev chrdev; + struct list_head events_list; + spinlock_t events_list_lock; + struct list_head next_events_list; + struct mutex n_events_list_lock; + DECLARE_KFIFO_PTR(events, struct counter_event); + wait_queue_head_t events_wait; + struct mutex events_lock; + /* + * chrdev_lock is locked by counter_chrdev_open() and unlocked by + * counter_chrdev_release(), so a mutex is not possible here because + * chrdev_lock will invariably be held when returning to user space + */ + atomic_t chrdev_lock; + struct mutex ops_exist_lock; }; int counter_register(struct counter_device *const counter); void counter_unregister(struct counter_device *const counter); int devm_counter_register(struct device *dev, struct counter_device *const counter); +void counter_push_event(struct counter_device *const counter, const u8 event, + const u8 channel); #define COUNTER_COMP_DEVICE_U8(_name, _read, _write) \ { \ diff --git a/include/uapi/linux/counter.h b/include/uapi/linux/counter.h index 6113938a6044..d0aa95aeff7b 100644 --- a/include/uapi/linux/counter.h +++ b/include/uapi/linux/counter.h @@ -6,6 +6,19 @@ #ifndef _UAPI_COUNTER_H_ #define _UAPI_COUNTER_H_ +#include +#include + +/* Component type definitions */ +enum counter_component_type { + COUNTER_COMPONENT_NONE, + COUNTER_COMPONENT_SIGNAL, + COUNTER_COMPONENT_COUNT, + COUNTER_COMPONENT_FUNCTION, + COUNTER_COMPONENT_SYNAPSE_ACTION, + COUNTER_COMPONENT_EXTENSION, +}; + /* Component scope definitions */ enum counter_scope { COUNTER_SCOPE_DEVICE, @@ -13,6 +26,91 @@ enum counter_scope { COUNTER_SCOPE_COUNT, }; +/** + * struct counter_component - Counter component identification + * @type: component type (one of enum counter_component_type) + * @scope: component scope (one of enum counter_scope) + * @parent: parent ID (matching the ID suffix of the respective parent sysfs + * path as described by the ABI documentation file + * Documentation/ABI/testing/sysfs-bus-counter) + * @id: component ID (matching the ID provided by the respective *_component_id + * sysfs attribute of the desired component) + * + * For example, if the Count 2 ceiling extension of Counter device 4 is desired, + * set type equal to COUNTER_COMPONENT_EXTENSION, scope equal to + * COUNTER_COUNT_SCOPE, parent equal to 2, and id equal to the value provided by + * the respective /sys/bus/counter/devices/counter4/count2/ceiling_component_id + * sysfs attribute. + */ +struct counter_component { + __u8 type; + __u8 scope; + __u8 parent; + __u8 id; +}; + +/* Event type definitions */ +enum counter_event_type { + /* Count value increased past ceiling */ + COUNTER_EVENT_OVERFLOW, + /* Count value decreased past floor */ + COUNTER_EVENT_UNDERFLOW, + /* Count value increased past ceiling, or decreased past floor */ + COUNTER_EVENT_OVERFLOW_UNDERFLOW, + /* Count value reached threshold */ + COUNTER_EVENT_THRESHOLD, + /* Index signal detected */ + COUNTER_EVENT_INDEX, +}; + +/** + * struct counter_watch - Counter component watch configuration + * @component: component to watch when event triggers + * @event: event that triggers (one of enum counter_event_type) + * @channel: event channel (typically 0 unless the device supports concurrent + * events of the same type) + */ +struct counter_watch { + struct counter_component component; + __u8 event; + __u8 channel; +}; + +/* + * Queues a Counter watch for the specified event. + * + * The queued watches will not be applied until COUNTER_ENABLE_EVENTS_IOCTL is + * called. + */ +#define COUNTER_ADD_WATCH_IOCTL _IOW(0x3E, 0x00, struct counter_watch) +/* + * Enables monitoring the events specified by the Counter watches that were + * queued by COUNTER_ADD_WATCH_IOCTL. + * + * If events are already enabled, the new set of watches replaces the old one. + * Calling this ioctl also has the effect of clearing the queue of watches added + * by COUNTER_ADD_WATCH_IOCTL. + */ +#define COUNTER_ENABLE_EVENTS_IOCTL _IO(0x3E, 0x01) +/* + * Stops monitoring the previously enabled events. + */ +#define COUNTER_DISABLE_EVENTS_IOCTL _IO(0x3E, 0x02) + +/** + * struct counter_event - Counter event data + * @timestamp: best estimate of time of event occurrence, in nanoseconds + * @value: component value + * @watch: component watch configuration + * @status: return status (system error number) + */ +struct counter_event { + __aligned_u64 timestamp; + __aligned_u64 value; + struct counter_watch watch; + __u8 status; +}; + /* Count direction values */ enum counter_count_direction { COUNTER_COUNT_DIRECTION_FORWARD, -- cgit v1.2.3-70-g09d2 From a8a28737c2c568e55b9fdbe55ab02b5c7c4247b7 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Wed, 29 Sep 2021 12:16:00 +0900 Subject: docs: counter: Document character device interface This patch adds high-level documentation about the Counter subsystem character device interface. Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/7a71dff2868195901d074ae2ec1b5611838d4b24.1632884256.git.vilhelm.gray@gmail.com Signed-off-by: Jonathan Cameron --- Documentation/driver-api/generic-counter.rst | 177 ++++++++++++++++----- Documentation/userspace-api/ioctl/ioctl-number.rst | 1 + 2 files changed, 137 insertions(+), 41 deletions(-) diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst index f6397218aa4c..25932dcb6e67 100644 --- a/Documentation/driver-api/generic-counter.rst +++ b/Documentation/driver-api/generic-counter.rst @@ -223,19 +223,6 @@ whether an input line is differential or single-ended) and instead focus on the core idea of what the data and process represent (e.g. position as interpreted from quadrature encoding data). -Userspace Interface -=================== - -Several sysfs attributes are generated by the Generic Counter interface, -and reside under the /sys/bus/counter/devices/counterX directory, where -counterX refers to the respective counter device. Please see -Documentation/ABI/testing/sysfs-bus-counter for detailed -information on each Generic Counter interface sysfs attribute. - -Through these sysfs attributes, programs and scripts may interact with -the Generic Counter paradigm Counts, Signals, and Synapses of respective -counter devices. - Driver API ========== @@ -388,16 +375,16 @@ userspace interface components:: / driver callbacks / ------------------- | - +---------------+ - | - V - +--------------------+ - | Counter sysfs | - +--------------------+ - | Translates to the | - | standard Counter | - | sysfs output | - +--------------------+ + +---------------+---------------+ + | | + V V + +--------------------+ +---------------------+ + | Counter sysfs | | Counter chrdev | + +--------------------+ +---------------------+ + | Translates to the | | Translates to the | + | standard Counter | | standard Counter | + | sysfs output | | character device | + +--------------------+ +---------------------+ Thereafter, data can be transferred directly between the Counter device driver and Counter userspace interface:: @@ -428,23 +415,30 @@ driver and Counter userspace interface:: / u64 / ---------- | - +---------------+ - | - V - +--------------------+ - | Counter sysfs | - +--------------------+ - | Translates to the | - | standard Counter | - | sysfs output | - |--------------------| - | Type: const char * | - | Value: "42" | - +--------------------+ - | - --------------- - / const char * / - --------------- + +---------------+---------------+ + | | + V V + +--------------------+ +---------------------+ + | Counter sysfs | | Counter chrdev | + +--------------------+ +---------------------+ + | Translates to the | | Translates to the | + | standard Counter | | standard Counter | + | sysfs output | | character device | + |--------------------| |---------------------| + | Type: const char * | | Type: u64 | + | Value: "42" | | Value: 42 | + +--------------------+ +---------------------+ + | | + --------------- ----------------------- + / const char * / / struct counter_event / + --------------- ----------------------- + | | + | V + | +-----------+ + | | read | + | +-----------+ + | \ Count: 42 / + | ----------- | V +--------------------------------------------------+ @@ -453,7 +447,7 @@ driver and Counter userspace interface:: \ Count: "42" / -------------------------------------------------- -There are three primary components involved: +There are four primary components involved: Counter device driver --------------------- @@ -473,3 +467,104 @@ and vice versa. Please refer to the ``Documentation/ABI/testing/sysfs-bus-counter`` file for a detailed breakdown of the available Generic Counter interface sysfs attributes. + +Counter chrdev +-------------- +Translates Counter events to the standard Counter character device; data +is transferred via standard character device read calls, while Counter +events are configured via ioctl calls. + +Sysfs Interface +=============== + +Several sysfs attributes are generated by the Generic Counter interface, +and reside under the ``/sys/bus/counter/devices/counterX`` directory, +where ``X`` is to the respective counter device id. Please see +``Documentation/ABI/testing/sysfs-bus-counter`` for detailed information +on each Generic Counter interface sysfs attribute. + +Through these sysfs attributes, programs and scripts may interact with +the Generic Counter paradigm Counts, Signals, and Synapses of respective +counter devices. + +Counter Character Device +======================== + +Counter character device nodes are created under the ``/dev`` directory +as ``counterX``, where ``X`` is the respective counter device id. +Defines for the standard Counter data types are exposed via the +userspace ``include/uapi/linux/counter.h`` file. + +Counter events +-------------- +Counter device drivers can support Counter events by utilizing the +``counter_push_event`` function:: + + void counter_push_event(struct counter_device *const counter, const u8 event, + const u8 channel); + +The event id is specified by the ``event`` parameter; the event channel +id is specified by the ``channel`` parameter. When this function is +called, the Counter data associated with the respective event is +gathered, and a ``struct counter_event`` is generated for each datum and +pushed to userspace. + +Counter events can be configured by users to report various Counter +data of interest. This can be conceptualized as a list of Counter +component read calls to perform. For example: + + +------------------------+------------------------+ + | COUNTER_EVENT_OVERFLOW | COUNTER_EVENT_INDEX | + +========================+========================+ + | Channel 0 | Channel 0 | + +------------------------+------------------------+ + | * Count 0 | * Signal 0 | + | * Count 1 | * Signal 0 Extension 0 | + | * Signal 3 | * Extension 4 | + | * Count 4 Extension 2 +------------------------+ + | * Signal 5 Extension 0 | Channel 1 | + | +------------------------+ + | | * Signal 4 | + | | * Signal 4 Extension 0 | + | | * Count 7 | + +------------------------+------------------------+ + +When ``counter_push_event(counter, COUNTER_EVENT_INDEX, 1)`` is called +for example, it will go down the list for the ``COUNTER_EVENT_INDEX`` +event channel 1 and execute the read callbacks for Signal 4, Signal 4 +Extension 0, and Count 7 -- the data returned for each is pushed to a +kfifo as a ``struct counter_event``, which userspace can retrieve via a +standard read operation on the respective character device node. + +Userspace +--------- +Userspace applications can configure Counter events via ioctl operations +on the Counter character device node. There following ioctl codes are +supported and provided by the ``linux/counter.h`` userspace header file: + +* :c:macro:`COUNTER_ADD_WATCH_IOCTL` + +* :c:macro:`COUNTER_ENABLE_EVENTS_IOCTL` + +* :c:macro:`COUNTER_DISABLE_EVENTS_IOCTL` + +To configure events to gather Counter data, users first populate a +``struct counter_watch`` with the relevant event id, event channel id, +and the information for the desired Counter component from which to +read, and then pass it via the ``COUNTER_ADD_WATCH_IOCTL`` ioctl +command. + +Note that an event can be watched without gathering Counter data by +setting the ``component.type`` member equal to +``COUNTER_COMPONENT_NONE``. With this configuration the Counter +character device will simply populate the event timestamps for those +respective ``struct counter_event`` elements and ignore the component +value. + +The ``COUNTER_ADD_WATCH_IOCTL`` command will buffer these Counter +watches. When ready, the ``COUNTER_ENABLE_EVENTS_IOCTL`` ioctl command +may be used to activate these Counter watches. + +Userspace applications can then execute a ``read`` operation (optionally +calling ``poll`` first) on the Counter character device node to retrieve +``struct counter_event`` elements with the desired data. diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index 2e8134059c87..0ba463be6c58 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -88,6 +88,7 @@ Code Seq# Include File Comments 0x20 all drivers/cdrom/cm206.h 0x22 all scsi/sg.h +0x3E 00-0F linux/counter.h '!' 00-1F uapi/linux/seccomp.h '#' 00-3F IEEE 1394 Subsystem Block for the entire subsystem -- cgit v1.2.3-70-g09d2 From 086099893fcebeae50f9020588080de43c82e4c0 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Wed, 29 Sep 2021 12:16:01 +0900 Subject: tools/counter: Create Counter tools This creates an example Counter program under tools/counter/* to exemplify the Counter character device interface. Cc: Pavel Machek Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/7c0f975ba098952122302d258ec9ffdef04befaf.1632884256.git.vilhelm.gray@gmail.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 1 + tools/Makefile | 13 +++--- tools/counter/Build | 1 + tools/counter/Makefile | 53 ++++++++++++++++++++++++ tools/counter/counter_example.c | 92 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 154 insertions(+), 6 deletions(-) create mode 100644 tools/counter/Build create mode 100644 tools/counter/Makefile create mode 100644 tools/counter/counter_example.c diff --git a/MAINTAINERS b/MAINTAINERS index 8310a42abdab..ec1590eba56f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4805,6 +4805,7 @@ F: Documentation/driver-api/generic-counter.rst F: drivers/counter/ F: include/linux/counter.h F: include/uapi/linux/counter.h +F: tools/counter/ CP2615 I2C DRIVER M: Bence Csókás diff --git a/tools/Makefile b/tools/Makefile index 7e9d34ddd74c..5da1fde03a9a 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -12,6 +12,7 @@ help: @echo ' acpi - ACPI tools' @echo ' bpf - misc BPF tools' @echo ' cgroup - cgroup tools' + @echo ' counter - counter tools' @echo ' cpupower - a tool for all things x86 CPU power' @echo ' debugging - tools for debugging' @echo ' firewire - the userspace part of nosy, an IEEE-1394 traffic sniffer' @@ -65,7 +66,7 @@ acpi: FORCE cpupower: FORCE $(call descend,power/$@) -cgroup firewire hv guest bootconfig spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging tracing: FORCE +cgroup counter firewire hv guest bootconfig spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging tracing: FORCE $(call descend,$@) bpf/%: FORCE @@ -100,7 +101,7 @@ freefall: FORCE kvm_stat: FORCE $(call descend,kvm/$@) -all: acpi cgroup cpupower gpio hv firewire liblockdep \ +all: acpi cgroup counter cpupower gpio hv firewire liblockdep \ perf selftests bootconfig spi turbostat usb \ virtio vm bpf x86_energy_perf_policy \ tmon freefall iio objtool kvm_stat wmi \ @@ -112,7 +113,7 @@ acpi_install: cpupower_install: $(call descend,power/$(@:_install=),install) -cgroup_install firewire_install gpio_install hv_install iio_install perf_install bootconfig_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install tracing_install: +cgroup_install counter_install firewire_install gpio_install hv_install iio_install perf_install bootconfig_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install tracing_install: $(call descend,$(@:_install=),install) liblockdep_install: @@ -133,7 +134,7 @@ freefall_install: kvm_stat_install: $(call descend,kvm/$(@:_install=),install) -install: acpi_install cgroup_install cpupower_install gpio_install \ +install: acpi_install cgroup_install counter_install cpupower_install gpio_install \ hv_install firewire_install iio_install liblockdep_install \ perf_install selftests_install turbostat_install usb_install \ virtio_install vm_install bpf_install x86_energy_perf_policy_install \ @@ -147,7 +148,7 @@ acpi_clean: cpupower_clean: $(call descend,power/cpupower,clean) -cgroup_clean hv_clean firewire_clean bootconfig_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean pci_clean firmware_clean debugging_clean tracing_clean: +cgroup_clean counter_clean hv_clean firewire_clean bootconfig_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean pci_clean firmware_clean debugging_clean tracing_clean: $(call descend,$(@:_clean=),clean) liblockdep_clean: @@ -181,7 +182,7 @@ freefall_clean: build_clean: $(call descend,build,clean) -clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \ +clean: acpi_clean cgroup_clean counter_clean cpupower_clean hv_clean firewire_clean \ perf_clean selftests_clean turbostat_clean bootconfig_clean spi_clean usb_clean virtio_clean \ vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \ freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \ diff --git a/tools/counter/Build b/tools/counter/Build new file mode 100644 index 000000000000..33f4a51d715e --- /dev/null +++ b/tools/counter/Build @@ -0,0 +1 @@ +counter_example-y += counter_example.o diff --git a/tools/counter/Makefile b/tools/counter/Makefile new file mode 100644 index 000000000000..5ebc195fd9c0 --- /dev/null +++ b/tools/counter/Makefile @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: GPL-2.0 +include ../scripts/Makefile.include + +bindir ?= /usr/bin + +ifeq ($(srctree),) +srctree := $(patsubst %/,%,$(dir $(CURDIR))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +endif + +# Do not use make's built-in rules +# (this improves performance and avoids hard-to-debug behaviour); +MAKEFLAGS += -r + +override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include + +ALL_TARGETS := counter_example +ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS)) + +all: $(ALL_PROGRAMS) + +export srctree OUTPUT CC LD CFLAGS +include $(srctree)/tools/build/Makefile.include + +# +# We need the following to be outside of kernel tree +# +$(OUTPUT)include/linux/counter.h: ../../include/uapi/linux/counter.h + mkdir -p $(OUTPUT)include/linux 2>&1 || true + ln -sf $(CURDIR)/../../include/uapi/linux/counter.h $@ + +prepare: $(OUTPUT)include/linux/counter.h + +COUNTER_EXAMPLE := $(OUTPUT)counter_example.o +$(COUNTER_EXAMPLE): prepare FORCE + $(Q)$(MAKE) $(build)=counter_example +$(OUTPUT)counter_example: $(COUNTER_EXAMPLE) + $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ + +clean: + rm -f $(ALL_PROGRAMS) + rm -rf $(OUTPUT)include/linux/counter.h + find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete + +install: $(ALL_PROGRAMS) + install -d -m 755 $(DESTDIR)$(bindir); \ + for program in $(ALL_PROGRAMS); do \ + install $$program $(DESTDIR)$(bindir); \ + done + +FORCE: + +.PHONY: all install clean FORCE prepare diff --git a/tools/counter/counter_example.c b/tools/counter/counter_example.c new file mode 100644 index 000000000000..be55287b950f --- /dev/null +++ b/tools/counter/counter_example.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Counter - example userspace application + * + * The userspace application opens /dev/counter0, configures the + * COUNTER_EVENT_INDEX event channel 0 to gather Count 0 count and Count + * 1 count, and prints out the data as it becomes available on the + * character device node. + * + * Copyright (C) 2021 William Breathitt Gray + */ +#include +#include +#include +#include +#include +#include +#include + +static struct counter_watch watches[2] = { + { + /* Component data: Count 0 count */ + .component.type = COUNTER_COMPONENT_COUNT, + .component.scope = COUNTER_SCOPE_COUNT, + .component.parent = 0, + /* Event type: Index */ + .event = COUNTER_EVENT_INDEX, + /* Device event channel 0 */ + .channel = 0, + }, + { + /* Component data: Count 1 count */ + .component.type = COUNTER_COMPONENT_COUNT, + .component.scope = COUNTER_SCOPE_COUNT, + .component.parent = 1, + /* Event type: Index */ + .event = COUNTER_EVENT_INDEX, + /* Device event channel 0 */ + .channel = 0, + }, +}; + +int main(void) +{ + int fd; + int ret; + int i; + struct counter_event event_data[2]; + + fd = open("/dev/counter0", O_RDWR); + if (fd == -1) { + perror("Unable to open /dev/counter0"); + return 1; + } + + for (i = 0; i < 2; i++) { + ret = ioctl(fd, COUNTER_ADD_WATCH_IOCTL, watches + i); + if (ret == -1) { + fprintf(stderr, "Error adding watches[%d]: %s\n", i, + strerror(errno)); + return 1; + } + } + ret = ioctl(fd, COUNTER_ENABLE_EVENTS_IOCTL); + if (ret == -1) { + perror("Error enabling events"); + return 1; + } + + for (;;) { + ret = read(fd, event_data, sizeof(event_data)); + if (ret == -1) { + perror("Failed to read event data"); + return 1; + } + + if (ret != sizeof(event_data)) { + fprintf(stderr, "Failed to read event data\n"); + return -EIO; + } + + printf("Timestamp 0: %llu\tCount 0: %llu\n" + "Error Message 0: %s\n" + "Timestamp 1: %llu\tCount 1: %llu\n" + "Error Message 1: %s\n", + event_data[0].timestamp, event_data[0].value, + strerror(event_data[0].status), + event_data[1].timestamp, event_data[1].value, + strerror(event_data[1].status)); + } + + return 0; +} -- cgit v1.2.3-70-g09d2 From bb6264a61de84320e77a22b4b8f4babf240608c4 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Wed, 29 Sep 2021 12:16:02 +0900 Subject: counter: Implement signalZ_action_component_id sysfs attribute The Generic Counter chrdev interface expects users to supply component IDs in order to select Synapses for requests. In order for users to know what component ID belongs to which Synapse this information must be exposed. The signalZ_action_component_id attribute provides a way for users to discover what component ID belongs to the respective Synapse. Cc: Gwendal Grignou Cc: Dan Carpenter Reviewed-by: David Lechner Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/a6c81b2f1f5d0b8d59b1ebe4f0fed04914b07547.1632884256.git.vilhelm.gray@gmail.com Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-counter | 7 +++++ drivers/counter/counter-sysfs.c | 45 +++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-counter b/Documentation/ABI/testing/sysfs-bus-counter index dee79b606847..aa2cd3c10d74 100644 --- a/Documentation/ABI/testing/sysfs-bus-counter +++ b/Documentation/ABI/testing/sysfs-bus-counter @@ -203,6 +203,13 @@ Description: both edges: Any state transition. +What: /sys/bus/counter/devices/counterX/countY/signalZ_action_component_id +KernelVersion: 5.16 +Contact: linux-iio@vger.kernel.org +Description: + Read-only attribute that indicates the component ID of the + respective Synapse of Count Y for Signal Z. + What: /sys/bus/counter/devices/counterX/countY/spike_filter_ns KernelVersion: 5.14 Contact: linux-iio@vger.kernel.org diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c index 108cbd838eb9..ee7ff1cde454 100644 --- a/drivers/counter/counter-sysfs.c +++ b/drivers/counter/counter-sysfs.c @@ -532,6 +532,46 @@ static int counter_name_attr_create(struct device *const dev, return 0; } +static ssize_t counter_comp_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const size_t id = (size_t)to_counter_attribute(attr)->comp.priv; + + return sprintf(buf, "%zu\n", id); +} + +static int counter_comp_id_attr_create(struct device *const dev, + struct counter_attribute_group *const group, + const char *name, const size_t id) +{ + struct counter_attribute *counter_attr; + + /* Allocate Counter attribute */ + counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL); + if (!counter_attr) + return -ENOMEM; + + /* Generate component ID name */ + name = devm_kasprintf(dev, GFP_KERNEL, "%s_component_id", name); + if (!name) + return -ENOMEM; + + /* Configure Counter attribute */ + counter_attr->comp.priv = (void *)id; + + /* Configure device attribute */ + sysfs_attr_init(&counter_attr->dev_attr.attr); + counter_attr->dev_attr.attr.name = name; + counter_attr->dev_attr.attr.mode = 0444; + counter_attr->dev_attr.show = counter_comp_id_show; + + /* Store list node */ + list_add(&counter_attr->l, &group->attr_list); + group->num_attr++; + + return 0; +} + static struct counter_comp counter_signal_comp = { .type = COUNTER_COMP_SIGNAL_LEVEL, .name = "signal", @@ -626,6 +666,11 @@ static int counter_sysfs_synapses_add(struct counter_device *const counter, COUNTER_SCOPE_COUNT, count); if (err < 0) return err; + + /* Create Synapse component ID attribute */ + err = counter_comp_id_attr_create(dev, group, comp.name, i); + if (err < 0) + return err; } return 0; -- cgit v1.2.3-70-g09d2 From 4bdec61d927b5db25f75fa377504d4e127c3682b Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Wed, 29 Sep 2021 12:16:03 +0900 Subject: counter: Implement *_component_id sysfs attributes The Generic Counter chrdev interface expects users to supply component IDs in order to select extensions for requests. In order for users to know what component ID belongs to which extension this information must be exposed. The *_component_id attribute provides a way for users to discover what component ID belongs to which respective extension. Cc: David Lechner Cc: Gwendal Grignou Cc: Dan Carpenter Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/8961a11edbb882fc689e468194f5be75f572443e.1632884256.git.vilhelm.gray@gmail.com Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-counter | 16 +++++++++++++- drivers/counter/counter-sysfs.c | 33 +++++++++++++++++++++++------ 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-counter b/Documentation/ABI/testing/sysfs-bus-counter index aa2cd3c10d74..0ce16669157e 100644 --- a/Documentation/ABI/testing/sysfs-bus-counter +++ b/Documentation/ABI/testing/sysfs-bus-counter @@ -203,12 +203,26 @@ Description: both edges: Any state transition. +What: /sys/bus/counter/devices/counterX/countY/ceiling_component_id +What: /sys/bus/counter/devices/counterX/countY/floor_component_id +What: /sys/bus/counter/devices/counterX/countY/count_mode_component_id +What: /sys/bus/counter/devices/counterX/countY/direction_component_id +What: /sys/bus/counter/devices/counterX/countY/enable_component_id +What: /sys/bus/counter/devices/counterX/countY/error_noise_component_id +What: /sys/bus/counter/devices/counterX/countY/prescaler_component_id +What: /sys/bus/counter/devices/counterX/countY/preset_component_id +What: /sys/bus/counter/devices/counterX/countY/preset_enable_component_id What: /sys/bus/counter/devices/counterX/countY/signalZ_action_component_id +What: /sys/bus/counter/devices/counterX/signalY/cable_fault_component_id +What: /sys/bus/counter/devices/counterX/signalY/cable_fault_enable_component_id +What: /sys/bus/counter/devices/counterX/signalY/filter_clock_prescaler_component_id +What: /sys/bus/counter/devices/counterX/signalY/index_polarity_component_id +What: /sys/bus/counter/devices/counterX/signalY/synchronous_mode_component_id KernelVersion: 5.16 Contact: linux-iio@vger.kernel.org Description: Read-only attribute that indicates the component ID of the - respective Synapse of Count Y for Signal Z. + respective extension or Synapse. What: /sys/bus/counter/devices/counterX/countY/spike_filter_ns KernelVersion: 5.14 diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c index ee7ff1cde454..97d8d7c2a2b6 100644 --- a/drivers/counter/counter-sysfs.c +++ b/drivers/counter/counter-sysfs.c @@ -586,6 +586,7 @@ static int counter_signal_attrs_create(struct counter_device *const counter, int err; struct counter_comp comp; size_t i; + struct counter_comp *ext; /* Create main Signal attribute */ comp = counter_signal_comp; @@ -601,8 +602,14 @@ static int counter_signal_attrs_create(struct counter_device *const counter, /* Create an attribute for each extension */ for (i = 0; i < signal->num_ext; i++) { - err = counter_attr_create(dev, cattr_group, signal->ext + i, - scope, signal); + ext = &signal->ext[i]; + + err = counter_attr_create(dev, cattr_group, ext, scope, signal); + if (err < 0) + return err; + + err = counter_comp_id_attr_create(dev, cattr_group, ext->name, + i); if (err < 0) return err; } @@ -693,6 +700,7 @@ static int counter_count_attrs_create(struct counter_device *const counter, int err; struct counter_comp comp; size_t i; + struct counter_comp *ext; /* Create main Count attribute */ comp = counter_count_comp; @@ -717,8 +725,14 @@ static int counter_count_attrs_create(struct counter_device *const counter, /* Create an attribute for each extension */ for (i = 0; i < count->num_ext; i++) { - err = counter_attr_create(dev, cattr_group, count->ext + i, - scope, count); + ext = &count->ext[i]; + + err = counter_attr_create(dev, cattr_group, ext, scope, count); + if (err < 0) + return err; + + err = counter_comp_id_attr_create(dev, cattr_group, ext->name, + i); if (err < 0) return err; } @@ -782,6 +796,7 @@ static int counter_sysfs_attr_add(struct counter_device *const counter, struct device *const dev = &counter->dev; int err; size_t i; + struct counter_comp *ext; /* Add Signals sysfs attributes */ err = counter_sysfs_signals_add(counter, cattr_group); @@ -814,8 +829,14 @@ static int counter_sysfs_attr_add(struct counter_device *const counter, /* Create an attribute for each extension */ for (i = 0; i < counter->num_ext; i++) { - err = counter_attr_create(dev, cattr_group, counter->ext + i, - scope, NULL); + ext = &counter->ext[i]; + + err = counter_attr_create(dev, cattr_group, ext, scope, NULL); + if (err < 0) + return err; + + err = counter_comp_id_attr_create(dev, cattr_group, ext->name, + i); if (err < 0) return err; } -- cgit v1.2.3-70-g09d2 From feff17a550c7120009d8ba9431426135661a731b Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Wed, 29 Sep 2021 12:16:04 +0900 Subject: counter: Implement events_queue_size sysfs attribute The events_queue_size sysfs attribute provides a way for users to dynamically configure the Counter events queue size for the Counter character device interface. The size is in number of struct counter_event data structures. The number of elements will be rounded-up to a power of 2 due to a requirement of the kfifo_alloc function called during reallocation of the queue. Cc: Oleksij Rempel Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/c914b2db2ea0a2637633bcc3e86ded3c94783f2e.1632884256.git.vilhelm.gray@gmail.com Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-counter | 8 +++++ drivers/counter/counter-sysfs.c | 45 +++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-counter b/Documentation/ABI/testing/sysfs-bus-counter index 0ce16669157e..06c2b3e27e0b 100644 --- a/Documentation/ABI/testing/sysfs-bus-counter +++ b/Documentation/ABI/testing/sysfs-bus-counter @@ -233,6 +233,14 @@ Description: shorter or equal to configured value are ignored. Value 0 means filter is disabled. +What: /sys/bus/counter/devices/counterX/events_queue_size +KernelVersion: 5.16 +Contact: linux-iio@vger.kernel.org +Description: + Size of the Counter events queue in number of struct + counter_event data structures. The number of elements will be + rounded-up to a power of 2. + What: /sys/bus/counter/devices/counterX/name KernelVersion: 5.2 Contact: linux-iio@vger.kernel.org diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c index 97d8d7c2a2b6..1ccd771da25f 100644 --- a/drivers/counter/counter-sysfs.c +++ b/drivers/counter/counter-sysfs.c @@ -3,11 +3,13 @@ * Generic Counter sysfs interface * Copyright (C) 2020 William Breathitt Gray */ +#include #include #include #include #include #include +#include #include #include #include @@ -783,12 +785,49 @@ static int counter_num_counts_read(struct counter_device *counter, u8 *val) return 0; } +static int counter_events_queue_size_read(struct counter_device *counter, + u64 *val) +{ + *val = kfifo_size(&counter->events); + return 0; +} + +static int counter_events_queue_size_write(struct counter_device *counter, + u64 val) +{ + DECLARE_KFIFO_PTR(events, struct counter_event); + int err = 0; + + /* Ensure chrdev is not opened more than 1 at a time */ + if (!atomic_add_unless(&counter->chrdev_lock, 1, 1)) + return -EBUSY; + + /* Allocate new events queue */ + err = kfifo_alloc(&events, val, GFP_KERNEL); + if (err) + goto exit_early; + + /* Swap in new events queue */ + kfifo_free(&counter->events); + counter->events.kfifo = events.kfifo; + +exit_early: + atomic_dec(&counter->chrdev_lock); + + return err; +} + static struct counter_comp counter_num_signals_comp = COUNTER_COMP_DEVICE_U8("num_signals", counter_num_signals_read, NULL); static struct counter_comp counter_num_counts_comp = COUNTER_COMP_DEVICE_U8("num_counts", counter_num_counts_read, NULL); +static struct counter_comp counter_events_queue_size_comp = + COUNTER_COMP_DEVICE_U64("events_queue_size", + counter_events_queue_size_read, + counter_events_queue_size_write); + static int counter_sysfs_attr_add(struct counter_device *const counter, struct counter_attribute_group *cattr_group) { @@ -827,6 +866,12 @@ static int counter_sysfs_attr_add(struct counter_device *const counter, if (err < 0) return err; + /* Create events_queue_size attribute */ + err = counter_attr_create(dev, cattr_group, + &counter_events_queue_size_comp, scope, NULL); + if (err < 0) + return err; + /* Create an attribute for each extension */ for (i = 0; i < counter->num_ext; i++) { ext = &counter->ext[i]; -- cgit v1.2.3-70-g09d2 From 09db4678bfbb429df259e1ce43854a0e2720c355 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Wed, 29 Sep 2021 12:16:05 +0900 Subject: counter: 104-quad-8: Replace mutex with spinlock This patch replaces the mutex I/O lock with a spinlock. This is in preparation for a subsequent patch adding IRQ support for 104-QUAD-8 devices; we can't sleep in an interrupt context, so we'll need to use a spinlock instead. Acked-by: Syed Nayyar Waris Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/3f74491dec66de10d062978bcb7b9c2b3bdea86c.1632884256.git.vilhelm.gray@gmail.com Signed-off-by: Jonathan Cameron --- drivers/counter/104-quad-8.c | 89 ++++++++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 37 deletions(-) diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c index c587f295d720..a56751bf1e9b 100644 --- a/drivers/counter/104-quad-8.c +++ b/drivers/counter/104-quad-8.c @@ -16,6 +16,7 @@ #include #include #include +#include #define QUAD8_EXTENT 32 @@ -43,7 +44,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses"); * @base: base port address of the device */ struct quad8 { - struct mutex lock; + spinlock_t lock; struct counter_device counter; unsigned int fck_prescaler[QUAD8_NUM_COUNTERS]; unsigned int preset[QUAD8_NUM_COUNTERS]; @@ -124,6 +125,7 @@ static int quad8_count_read(struct counter_device *counter, unsigned int flags; unsigned int borrow; unsigned int carry; + unsigned long irqflags; int i; flags = inb(base_offset + 1); @@ -133,7 +135,7 @@ static int quad8_count_read(struct counter_device *counter, /* Borrow XOR Carry effectively doubles count range */ *val = (unsigned long)(borrow ^ carry) << 24; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); /* Reset Byte Pointer; transfer Counter to Output Latch */ outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT, @@ -142,7 +144,7 @@ static int quad8_count_read(struct counter_device *counter, for (i = 0; i < 3; i++) *val |= (unsigned long)inb(base_offset) << (8 * i); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -152,13 +154,14 @@ static int quad8_count_write(struct counter_device *counter, { struct quad8 *const priv = counter->priv; const int base_offset = priv->base + 2 * count->id; + unsigned long irqflags; int i; /* Only 24-bit values are supported */ if (val > 0xFFFFFF) return -ERANGE; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); /* Reset Byte Pointer */ outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); @@ -183,7 +186,7 @@ static int quad8_count_write(struct counter_device *counter, /* Reset Error flag */ outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -201,8 +204,9 @@ static int quad8_function_read(struct counter_device *counter, { struct quad8 *const priv = counter->priv; const int id = count->id; + unsigned long irqflags; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); if (priv->quadrature_mode[id]) switch (priv->quadrature_scale[id]) { @@ -219,7 +223,7 @@ static int quad8_function_read(struct counter_device *counter, else *function = COUNTER_FUNCTION_PULSE_DIRECTION; - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -234,10 +238,11 @@ static int quad8_function_write(struct counter_device *counter, unsigned int *const scale = priv->quadrature_scale + id; unsigned int *const synchronous_mode = priv->synchronous_mode + id; const int base_offset = priv->base + 2 * id + 1; + unsigned long irqflags; unsigned int mode_cfg; unsigned int idr_cfg; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); mode_cfg = priv->count_mode[id] << 1; idr_cfg = priv->index_polarity[id] << 1; @@ -272,7 +277,7 @@ static int quad8_function_write(struct counter_device *counter, break; default: /* should never reach this path */ - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return -EINVAL; } } @@ -280,7 +285,7 @@ static int quad8_function_write(struct counter_device *counter, /* Load mode configuration to Counter Mode Register */ outb(QUAD8_CTR_CMR | mode_cfg, base_offset); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -406,9 +411,10 @@ static int quad8_index_polarity_set(struct counter_device *counter, struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id - 16; const int base_offset = priv->base + 2 * channel_id + 1; + unsigned long irqflags; unsigned int idr_cfg = index_polarity << 1; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); idr_cfg |= priv->synchronous_mode[channel_id]; @@ -417,7 +423,7 @@ static int quad8_index_polarity_set(struct counter_device *counter, /* Load Index Control configuration to Index Control Register */ outb(QUAD8_CTR_IDR | idr_cfg, base_offset); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -446,15 +452,16 @@ static int quad8_synchronous_mode_set(struct counter_device *counter, struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id - 16; const int base_offset = priv->base + 2 * channel_id + 1; + unsigned long irqflags; unsigned int idr_cfg = synchronous_mode; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); idr_cfg |= priv->index_polarity[channel_id] << 1; /* Index function must be non-synchronous in non-quadrature mode */ if (synchronous_mode && !priv->quadrature_mode[channel_id]) { - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return -EINVAL; } @@ -463,7 +470,7 @@ static int quad8_synchronous_mode_set(struct counter_device *counter, /* Load Index Control configuration to Index Control Register */ outb(QUAD8_CTR_IDR | idr_cfg, base_offset); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -510,6 +517,7 @@ static int quad8_count_mode_write(struct counter_device *counter, unsigned int count_mode; unsigned int mode_cfg; const int base_offset = priv->base + 2 * count->id + 1; + unsigned long irqflags; /* Map Generic Counter count mode to 104-QUAD-8 count mode */ switch (cnt_mode) { @@ -530,7 +538,7 @@ static int quad8_count_mode_write(struct counter_device *counter, return -EINVAL; } - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); priv->count_mode[count->id] = count_mode; @@ -544,7 +552,7 @@ static int quad8_count_mode_write(struct counter_device *counter, /* Load mode configuration to Counter Mode Register */ outb(QUAD8_CTR_CMR | mode_cfg, base_offset); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -564,9 +572,10 @@ static int quad8_count_enable_write(struct counter_device *counter, { struct quad8 *const priv = counter->priv; const int base_offset = priv->base + 2 * count->id; + unsigned long irqflags; unsigned int ior_cfg; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); priv->ab_enable[count->id] = enable; @@ -575,7 +584,7 @@ static int quad8_count_enable_write(struct counter_device *counter, /* Load I/O control configuration */ outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -626,16 +635,17 @@ static int quad8_count_preset_write(struct counter_device *counter, struct counter_count *count, u64 preset) { struct quad8 *const priv = counter->priv; + unsigned long irqflags; /* Only 24-bit values are supported */ if (preset > 0xFFFFFF) return -ERANGE; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); quad8_preset_register_set(priv, count->id, preset); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -644,8 +654,9 @@ static int quad8_count_ceiling_read(struct counter_device *counter, struct counter_count *count, u64 *ceiling) { struct quad8 *const priv = counter->priv; + unsigned long irqflags; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); /* Range Limit and Modulo-N count modes use preset value as ceiling */ switch (priv->count_mode[count->id]) { @@ -659,7 +670,7 @@ static int quad8_count_ceiling_read(struct counter_device *counter, break; } - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -668,23 +679,24 @@ static int quad8_count_ceiling_write(struct counter_device *counter, struct counter_count *count, u64 ceiling) { struct quad8 *const priv = counter->priv; + unsigned long irqflags; /* Only 24-bit values are supported */ if (ceiling > 0xFFFFFF) return -ERANGE; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); /* Range Limit and Modulo-N count modes use preset value as ceiling */ switch (priv->count_mode[count->id]) { case 1: case 3: quad8_preset_register_set(priv, count->id, ceiling); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return -EINVAL; } @@ -706,12 +718,13 @@ static int quad8_count_preset_enable_write(struct counter_device *counter, { struct quad8 *const priv = counter->priv; const int base_offset = priv->base + 2 * count->id + 1; + unsigned long irqflags; unsigned int ior_cfg; /* Preset enable is active low in Input/Output Control register */ preset_enable = !preset_enable; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); priv->preset_enable[count->id] = preset_enable; @@ -720,7 +733,7 @@ static int quad8_count_preset_enable_write(struct counter_device *counter, /* Load I/O control configuration to Input / Output Control Register */ outb(QUAD8_CTR_IOR | ior_cfg, base_offset); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -731,22 +744,23 @@ static int quad8_signal_cable_fault_read(struct counter_device *counter, { struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id / 2; + unsigned long irqflags; bool disabled; unsigned int status; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); disabled = !(priv->cable_fault_enable & BIT(channel_id)); if (disabled) { - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return -EINVAL; } /* Logic 0 = cable fault */ status = inb(priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); /* Mask respective channel and invert logic */ *cable_fault = !(status & BIT(channel_id)); @@ -772,9 +786,10 @@ static int quad8_signal_cable_fault_enable_write(struct counter_device *counter, { struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id / 2; + unsigned long irqflags; unsigned int cable_fault_enable; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); if (enable) priv->cable_fault_enable |= BIT(channel_id); @@ -786,7 +801,7 @@ static int quad8_signal_cable_fault_enable_write(struct counter_device *counter, outb(cable_fault_enable, priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -809,8 +824,9 @@ static int quad8_signal_fck_prescaler_write(struct counter_device *counter, struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id / 2; const int base_offset = priv->base + 2 * channel_id; + unsigned long irqflags; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); priv->fck_prescaler[channel_id] = prescaler; @@ -822,7 +838,7 @@ static int quad8_signal_fck_prescaler_write(struct counter_device *counter, outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC, base_offset + 1); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -991,8 +1007,7 @@ static int quad8_probe(struct device *dev, unsigned int id) priv->counter.priv = priv; priv->base = base[id]; - /* Initialize mutex */ - mutex_init(&priv->lock); + spin_lock_init(&priv->lock); /* Reset all counters and disable interrupt function */ outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP); -- cgit v1.2.3-70-g09d2 From 7aa2ba0df651e3193c24b5da4887759ed788578c Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Wed, 29 Sep 2021 12:16:06 +0900 Subject: counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8 The LSI/CSI LS7266R1 chip provides programmable output via the FLG pins. When interrupts are enabled on the ACCES 104-QUAD-8, they occur whenever FLG1 is active. Four functions are available for the FLG1 signal: Carry, Compare, Carry-Borrow, and Index. Carry: Interrupt generated on active low Carry signal. Carry signal toggles every time the respective channel's counter overflows. Compare: Interrupt generated on active low Compare signal. Compare signal toggles every time respective channel's preset register is equal to the respective channel's counter. Carry-Borrow: Interrupt generated on active low Carry signal and active low Borrow signal. Carry signal toggles every time the respective channel's counter overflows. Borrow signal toggles every time the respective channel's counter underflows. Index: Interrupt generated on active high Index signal. These four functions correspond respectivefly to the following four Counter event types: COUNTER_EVENT_OVERFLOW, COUNTER_EVENT_THRESHOLD, COUNTER_EVENT_OVERFLOW_UNDERFLOW, and COUNTER_EVENT_INDEX. Interrupts push Counter events to event channel X, where 'X' is the respective channel whose FLG1 activated. This patch adds IRQ support for the ACCES 104-QUAD-8. The interrupt line numbers for the devices may be configured via the irq array module parameter. Acked-by: Syed Nayyar Waris Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/e3a28e100840e3a336fa93fce77445f0e9d9a674.1632884256.git.vilhelm.gray@gmail.com Signed-off-by: Jonathan Cameron --- drivers/counter/104-quad-8.c | 167 +++++++++++++++++++++++++++++++++++++++++-- drivers/counter/Kconfig | 6 +- 2 files changed, 164 insertions(+), 9 deletions(-) diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c index a56751bf1e9b..1cbd60aaed69 100644 --- a/drivers/counter/104-quad-8.c +++ b/drivers/counter/104-quad-8.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,10 @@ static unsigned int num_quad8; module_param_hw_array(base, uint, ioport, &num_quad8, 0); MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses"); +static unsigned int irq[max_num_isa_dev(QUAD8_EXTENT)]; +module_param_hw_array(irq, uint, irq, NULL, 0); +MODULE_PARM_DESC(irq, "ACCES 104-QUAD-8 interrupt line numbers"); + #define QUAD8_NUM_COUNTERS 8 /** @@ -38,6 +43,8 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses"); * @quadrature_scale: array of quadrature mode scale configurations * @ab_enable: array of A and B inputs enable configurations * @preset_enable: array of set_to_preset_on_index attribute configurations + * @irq_trigger: array of current IRQ trigger function configurations + * @next_irq_trigger: array of next IRQ trigger function configurations * @synchronous_mode: array of index function synchronous mode configurations * @index_polarity: array of index function polarity configurations * @cable_fault_enable: differential encoder cable status enable configurations @@ -53,13 +60,17 @@ struct quad8 { unsigned int quadrature_scale[QUAD8_NUM_COUNTERS]; unsigned int ab_enable[QUAD8_NUM_COUNTERS]; unsigned int preset_enable[QUAD8_NUM_COUNTERS]; + unsigned int irq_trigger[QUAD8_NUM_COUNTERS]; + unsigned int next_irq_trigger[QUAD8_NUM_COUNTERS]; unsigned int synchronous_mode[QUAD8_NUM_COUNTERS]; unsigned int index_polarity[QUAD8_NUM_COUNTERS]; unsigned int cable_fault_enable; unsigned int base; }; +#define QUAD8_REG_INTERRUPT_STATUS 0x10 #define QUAD8_REG_CHAN_OP 0x11 +#define QUAD8_REG_INDEX_INTERRUPT 0x12 #define QUAD8_REG_INDEX_INPUT_LEVELS 0x16 #define QUAD8_DIFF_ENCODER_CABLE_STATUS 0x17 /* Borrow Toggle flip-flop */ @@ -92,8 +103,8 @@ struct quad8 { #define QUAD8_RLD_CNTR_OUT 0x10 /* Transfer Preset Register LSB to FCK Prescaler */ #define QUAD8_RLD_PRESET_PSC 0x18 -#define QUAD8_CHAN_OP_ENABLE_COUNTERS 0x00 #define QUAD8_CHAN_OP_RESET_COUNTERS 0x01 +#define QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC 0x04 #define QUAD8_CMR_QUADRATURE_X1 0x08 #define QUAD8_CMR_QUADRATURE_X2 0x10 #define QUAD8_CMR_QUADRATURE_X4 0x18 @@ -378,13 +389,103 @@ static int quad8_action_read(struct counter_device *counter, } } +enum { + QUAD8_EVENT_NONE = -1, + QUAD8_EVENT_CARRY = 0, + QUAD8_EVENT_COMPARE = 1, + QUAD8_EVENT_CARRY_BORROW = 2, + QUAD8_EVENT_INDEX = 3, +}; + +static int quad8_events_configure(struct counter_device *counter) +{ + struct quad8 *const priv = counter->priv; + unsigned long irq_enabled = 0; + unsigned long irqflags; + size_t channel; + unsigned long ior_cfg; + unsigned long base_offset; + + spin_lock_irqsave(&priv->lock, irqflags); + + /* Enable interrupts for the requested channels, disable for the rest */ + for (channel = 0; channel < QUAD8_NUM_COUNTERS; channel++) { + if (priv->next_irq_trigger[channel] == QUAD8_EVENT_NONE) + continue; + + if (priv->irq_trigger[channel] != priv->next_irq_trigger[channel]) { + /* Save new IRQ function configuration */ + priv->irq_trigger[channel] = priv->next_irq_trigger[channel]; + + /* Load configuration to I/O Control Register */ + ior_cfg = priv->ab_enable[channel] | + priv->preset_enable[channel] << 1 | + priv->irq_trigger[channel] << 3; + base_offset = priv->base + 2 * channel + 1; + outb(QUAD8_CTR_IOR | ior_cfg, base_offset); + } + + /* Reset next IRQ trigger function configuration */ + priv->next_irq_trigger[channel] = QUAD8_EVENT_NONE; + + /* Enable IRQ line */ + irq_enabled |= BIT(channel); + } + + outb(irq_enabled, priv->base + QUAD8_REG_INDEX_INTERRUPT); + + spin_unlock_irqrestore(&priv->lock, irqflags); + + return 0; +} + +static int quad8_watch_validate(struct counter_device *counter, + const struct counter_watch *watch) +{ + struct quad8 *const priv = counter->priv; + + if (watch->channel > QUAD8_NUM_COUNTERS - 1) + return -EINVAL; + + switch (watch->event) { + case COUNTER_EVENT_OVERFLOW: + if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE) + priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_CARRY; + else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_CARRY) + return -EINVAL; + return 0; + case COUNTER_EVENT_THRESHOLD: + if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE) + priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_COMPARE; + else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_COMPARE) + return -EINVAL; + return 0; + case COUNTER_EVENT_OVERFLOW_UNDERFLOW: + if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE) + priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_CARRY_BORROW; + else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_CARRY_BORROW) + return -EINVAL; + return 0; + case COUNTER_EVENT_INDEX: + if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE) + priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_INDEX; + else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_INDEX) + return -EINVAL; + return 0; + default: + return -EINVAL; + } +} + static const struct counter_ops quad8_ops = { .signal_read = quad8_signal_read, .count_read = quad8_count_read, .count_write = quad8_count_write, .function_read = quad8_function_read, .function_write = quad8_function_write, - .action_read = quad8_action_read + .action_read = quad8_action_read, + .events_configure = quad8_events_configure, + .watch_validate = quad8_watch_validate, }; static const char *const quad8_index_polarity_modes[] = { @@ -579,7 +680,8 @@ static int quad8_count_enable_write(struct counter_device *counter, priv->ab_enable[count->id] = enable; - ior_cfg = enable | priv->preset_enable[count->id] << 1; + ior_cfg = enable | priv->preset_enable[count->id] << 1 | + priv->irq_trigger[count->id] << 3; /* Load I/O control configuration */ outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1); @@ -728,7 +830,8 @@ static int quad8_count_preset_enable_write(struct counter_device *counter, priv->preset_enable[count->id] = preset_enable; - ior_cfg = priv->ab_enable[count->id] | preset_enable << 1; + ior_cfg = priv->ab_enable[count->id] | preset_enable << 1 | + priv->irq_trigger[count->id] << 3; /* Load I/O control configuration to Input / Output Control Register */ outb(QUAD8_CTR_IOR | ior_cfg, base_offset); @@ -980,11 +1083,54 @@ static struct counter_count quad8_counts[] = { QUAD8_COUNT(7, "Channel 8 Count") }; +static irqreturn_t quad8_irq_handler(int irq, void *private) +{ + struct quad8 *const priv = private; + const unsigned long base = priv->base; + unsigned long irq_status; + unsigned long channel; + u8 event; + + irq_status = inb(base + QUAD8_REG_INTERRUPT_STATUS); + if (!irq_status) + return IRQ_NONE; + + for_each_set_bit(channel, &irq_status, QUAD8_NUM_COUNTERS) { + switch (priv->irq_trigger[channel]) { + case QUAD8_EVENT_CARRY: + event = COUNTER_EVENT_OVERFLOW; + break; + case QUAD8_EVENT_COMPARE: + event = COUNTER_EVENT_THRESHOLD; + break; + case QUAD8_EVENT_CARRY_BORROW: + event = COUNTER_EVENT_OVERFLOW_UNDERFLOW; + break; + case QUAD8_EVENT_INDEX: + event = COUNTER_EVENT_INDEX; + break; + default: + /* should never reach this path */ + WARN_ONCE(true, "invalid interrupt trigger function %u configured for channel %lu\n", + priv->irq_trigger[channel], channel); + continue; + } + + counter_push_event(&priv->counter, event, channel); + } + + /* Clear pending interrupts on device */ + outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base + QUAD8_REG_CHAN_OP); + + return IRQ_HANDLED; +} + static int quad8_probe(struct device *dev, unsigned int id) { struct quad8 *priv; int i, j; unsigned int base_offset; + int err; if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) { dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", @@ -1009,6 +1155,8 @@ static int quad8_probe(struct device *dev, unsigned int id) spin_lock_init(&priv->lock); + /* Reset Index/Interrupt Register */ + outb(0x00, base[id] + QUAD8_REG_INDEX_INTERRUPT); /* Reset all counters and disable interrupt function */ outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP); /* Set initial configuration for all counters */ @@ -1035,11 +1183,18 @@ static int quad8_probe(struct device *dev, unsigned int id) outb(QUAD8_CTR_IOR, base_offset + 1); /* Disable index function; negative index polarity */ outb(QUAD8_CTR_IDR, base_offset + 1); + /* Initialize next IRQ trigger function configuration */ + priv->next_irq_trigger[i] = QUAD8_EVENT_NONE; } /* Disable Differential Encoder Cable Status for all channels */ outb(0xFF, base[id] + QUAD8_DIFF_ENCODER_CABLE_STATUS); - /* Enable all counters */ - outb(QUAD8_CHAN_OP_ENABLE_COUNTERS, base[id] + QUAD8_REG_CHAN_OP); + /* Enable all counters and enable interrupt function */ + outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base[id] + QUAD8_REG_CHAN_OP); + + err = devm_request_irq(dev, irq[id], quad8_irq_handler, IRQF_SHARED, + priv->counter.name, priv); + if (err) + return err; return devm_counter_register(dev, &priv->counter); } diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index d5d2540b30c2..3dcdb681c4e4 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -23,11 +23,11 @@ config 104_QUAD_8 A counter's respective error flag may be cleared by performing a write operation on the respective count value attribute. Although the 104-QUAD-8 counters have a 25-bit range, only the lower 24 bits may be - set, either directly or via the counter's preset attribute. Interrupts - are not supported by this driver. + set, either directly or via the counter's preset attribute. The base port addresses for the devices may be configured via the base - array module parameter. + array module parameter. The interrupt line numbers for the devices may + be configured via the irq array module parameter. config INTERRUPT_CNT tristate "Interrupt counter driver" -- cgit v1.2.3-70-g09d2 From fe90fcabc852378a808bea713064da6ed2058ad2 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Thu, 30 Sep 2021 18:03:47 +0100 Subject: counter: microchip-tcb-capture: Tidy up a false kernel-doc /** marking. Deals with W=1 warning This comment starts with '/**', but isn't a kernel-doc comment. Signed-off-by: Jonathan Cameron Cc: Kamel Bouhara Cc: William Breathitt Gray Acked-by: William Breathitt Gray Link: https://lore.kernel.org/r/20210930170347.101153-1-jic23@kernel.org --- drivers/counter/microchip-tcb-capture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/counter/microchip-tcb-capture.c b/drivers/counter/microchip-tcb-capture.c index 79e0c84a3b81..0ab1b2716784 100644 --- a/drivers/counter/microchip-tcb-capture.c +++ b/drivers/counter/microchip-tcb-capture.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * Copyright (C) 2020 Microchip * * Author: Kamel Bouhara -- cgit v1.2.3-70-g09d2 From f27d1e769746b09d618dfbe341224a2e181b067d Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Wed, 8 Sep 2021 18:56:45 +0800 Subject: iio: ep93xx: Make use of the helper function devm_platform_ioremap_resource() Use the devm_platform_ioremap_resource() helper instead of calling platform_get_resource() and devm_ioremap_resource() separately Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20210908105646.1576-1-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ep93xx_adc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/iio/adc/ep93xx_adc.c b/drivers/iio/adc/ep93xx_adc.c index 8edd6407b7c3..fd5a9404c8dc 100644 --- a/drivers/iio/adc/ep93xx_adc.c +++ b/drivers/iio/adc/ep93xx_adc.c @@ -156,15 +156,13 @@ static int ep93xx_adc_probe(struct platform_device *pdev) struct iio_dev *iiodev; struct ep93xx_adc_priv *priv; struct clk *pclk; - struct resource *res; iiodev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv)); if (!iiodev) return -ENOMEM; priv = iio_priv(iiodev); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(&pdev->dev, res); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); -- cgit v1.2.3-70-g09d2 From 3b3870646642c2a22d2c4d5602559b3842e0fb99 Mon Sep 17 00:00:00 2001 From: Daniel Palmer Date: Mon, 13 Sep 2021 20:29:13 +0900 Subject: iio: imu: inv_mpu6050: Mark acpi match table as maybe unused When building kernels without ACPI support the table is declared but is not used because ACPI_PTR() turns it into a NULL. Add the __maybe_unused attribute to stop the compiler whining. Signed-off-by: Daniel Palmer Acked-by: Jean-Baptiste Maneyrol Link: https://lore.kernel.org/r/20210913112913.2148026-1-daniel@0x0f.com Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index 95f16951c8f4..3ef17e3f50e2 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -249,7 +249,7 @@ static const struct of_device_id inv_of_match[] = { }; MODULE_DEVICE_TABLE(of, inv_of_match); -static const struct acpi_device_id inv_acpi_match[] = { +static const struct acpi_device_id __maybe_unused inv_acpi_match[] = { {"INVN6500", INV_MPU6500}, { }, }; -- cgit v1.2.3-70-g09d2 From 8a16c76e23bb71d69aac121577d1114e1711eaa6 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 13 Sep 2021 14:51:21 +0300 Subject: iio: dac: ad7303: convert probe to full device-managed For this conversion, the regulators need to have some cleanup hooks registered with devm_add_action_or_reset() and then the devm_io_device_register() call can be used. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210913115121.300082-1-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad7303.c | 47 +++++++++++++++++------------------------------ 1 file changed, 17 insertions(+), 30 deletions(-) diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c index e1b6a92df12f..91eaaf793b3e 100644 --- a/drivers/iio/dac/ad7303.c +++ b/drivers/iio/dac/ad7303.c @@ -198,6 +198,11 @@ static const struct iio_chan_spec ad7303_channels[] = { AD7303_CHANNEL(1), }; +static void ad7303_reg_disable(void *reg) +{ + regulator_disable(reg); +} + static int ad7303_probe(struct spi_device *spi) { const struct spi_device_id *id = spi_get_device_id(spi); @@ -210,7 +215,6 @@ static int ad7303_probe(struct spi_device *spi) return -ENOMEM; st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); st->spi = spi; @@ -224,18 +228,27 @@ static int ad7303_probe(struct spi_device *spi) if (ret) return ret; + ret = devm_add_action_or_reset(&spi->dev, ad7303_reg_disable, st->vdd_reg); + if (ret) + return ret; + st->vref_reg = devm_regulator_get_optional(&spi->dev, "REF"); if (IS_ERR(st->vref_reg)) { ret = PTR_ERR(st->vref_reg); if (ret != -ENODEV) - goto err_disable_vdd_reg; + return ret; st->vref_reg = NULL; } if (st->vref_reg) { ret = regulator_enable(st->vref_reg); if (ret) - goto err_disable_vdd_reg; + return ret; + + ret = devm_add_action_or_reset(&spi->dev, ad7303_reg_disable, + st->vref_reg); + if (ret) + return ret; st->config |= AD7303_CFG_EXTERNAL_VREF; } @@ -246,32 +259,7 @@ static int ad7303_probe(struct spi_device *spi) indio_dev->channels = ad7303_channels; indio_dev->num_channels = ARRAY_SIZE(ad7303_channels); - ret = iio_device_register(indio_dev); - if (ret) - goto err_disable_vref_reg; - - return 0; - -err_disable_vref_reg: - if (st->vref_reg) - regulator_disable(st->vref_reg); -err_disable_vdd_reg: - regulator_disable(st->vdd_reg); - return ret; -} - -static int ad7303_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad7303_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - if (st->vref_reg) - regulator_disable(st->vref_reg); - regulator_disable(st->vdd_reg); - - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct of_device_id ad7303_spi_of_match[] = { @@ -292,7 +280,6 @@ static struct spi_driver ad7303_driver = { .of_match_table = ad7303_spi_of_match, }, .probe = ad7303_probe, - .remove = ad7303_remove, .id_table = ad7303_spi_ids, }; module_spi_driver(ad7303_driver); -- cgit v1.2.3-70-g09d2 From 96788444302658c6bf8224a811741b81611972fa Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 13 Sep 2021 14:52:09 +0300 Subject: staging: iio: ad9832: convert probe to device-managed This change does a conversion of the driver to use device-managed init functions. The 2 regulators and the clock inits are converted to use devm_add_action_or_reset() callbacks for de-initializing them when the driver unloads. And finally the devm_iio_device_register() function can be use to register the device. The remove hook can finally be removed and the spi_set_drvdata() call can also be removed as the private data is no longer used. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210913115209.300665-1-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/frequency/ad9832.c | 82 +++++++++++++++------------------- 1 file changed, 36 insertions(+), 46 deletions(-) diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c index 3f1981e287f5..f43464db618a 100644 --- a/drivers/staging/iio/frequency/ad9832.c +++ b/drivers/staging/iio/frequency/ad9832.c @@ -294,6 +294,16 @@ static const struct iio_info ad9832_info = { .attrs = &ad9832_attribute_group, }; +static void ad9832_reg_disable(void *reg) +{ + regulator_disable(reg); +} + +static void ad9832_clk_disable(void *clk) +{ + clk_disable_unprepare(clk); +} + static int ad9832_probe(struct spi_device *spi) { struct ad9832_platform_data *pdata = dev_get_platdata(&spi->dev); @@ -310,7 +320,6 @@ static int ad9832_probe(struct spi_device *spi) if (!indio_dev) return -ENOMEM; - spi_set_drvdata(spi, indio_dev); st = iio_priv(indio_dev); st->avdd = devm_regulator_get(&spi->dev, "avdd"); @@ -323,27 +332,35 @@ static int ad9832_probe(struct spi_device *spi) return ret; } + ret = devm_add_action_or_reset(&spi->dev, ad9832_reg_disable, st->avdd); + if (ret) + return ret; + st->dvdd = devm_regulator_get(&spi->dev, "dvdd"); - if (IS_ERR(st->dvdd)) { - ret = PTR_ERR(st->dvdd); - goto error_disable_avdd; - } + if (IS_ERR(st->dvdd)) + return PTR_ERR(st->dvdd); ret = regulator_enable(st->dvdd); if (ret) { dev_err(&spi->dev, "Failed to enable specified DVDD supply\n"); - goto error_disable_avdd; + return ret; } + ret = devm_add_action_or_reset(&spi->dev, ad9832_reg_disable, st->dvdd); + if (ret) + return ret; + st->mclk = devm_clk_get(&spi->dev, "mclk"); - if (IS_ERR(st->mclk)) { - ret = PTR_ERR(st->mclk); - goto error_disable_dvdd; - } + if (IS_ERR(st->mclk)) + return PTR_ERR(st->mclk); ret = clk_prepare_enable(st->mclk); if (ret < 0) - goto error_disable_dvdd; + return ret; + + ret = devm_add_action_or_reset(&spi->dev, ad9832_clk_disable, st->mclk); + if (ret) + return ret; st->spi = spi; mutex_init(&st->lock); @@ -394,60 +411,34 @@ static int ad9832_probe(struct spi_device *spi) ret = spi_sync(st->spi, &st->msg); if (ret) { dev_err(&spi->dev, "device init failed\n"); - goto error_unprepare_mclk; + return ret; } ret = ad9832_write_frequency(st, AD9832_FREQ0HM, pdata->freq0); if (ret) - goto error_unprepare_mclk; + return ret; ret = ad9832_write_frequency(st, AD9832_FREQ1HM, pdata->freq1); if (ret) - goto error_unprepare_mclk; + return ret; ret = ad9832_write_phase(st, AD9832_PHASE0H, pdata->phase0); if (ret) - goto error_unprepare_mclk; + return ret; ret = ad9832_write_phase(st, AD9832_PHASE1H, pdata->phase1); if (ret) - goto error_unprepare_mclk; + return ret; ret = ad9832_write_phase(st, AD9832_PHASE2H, pdata->phase2); if (ret) - goto error_unprepare_mclk; + return ret; ret = ad9832_write_phase(st, AD9832_PHASE3H, pdata->phase3); if (ret) - goto error_unprepare_mclk; - - ret = iio_device_register(indio_dev); - if (ret) - goto error_unprepare_mclk; - - return 0; - -error_unprepare_mclk: - clk_disable_unprepare(st->mclk); -error_disable_dvdd: - regulator_disable(st->dvdd); -error_disable_avdd: - regulator_disable(st->avdd); - - return ret; -} - -static int ad9832_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad9832_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - clk_disable_unprepare(st->mclk); - regulator_disable(st->dvdd); - regulator_disable(st->avdd); + return ret; - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct spi_device_id ad9832_id[] = { @@ -462,7 +453,6 @@ static struct spi_driver ad9832_driver = { .name = "ad9832", }, .probe = ad9832_probe, - .remove = ad9832_remove, .id_table = ad9832_id, }; module_spi_driver(ad9832_driver); -- cgit v1.2.3-70-g09d2 From 14a6ee6ec568463d1d1a800928194e769c6c802a Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 13 Sep 2021 14:52:37 +0300 Subject: iio: dac: ad5064: convert probe to full device-managed This change converts the probe of the AD5064 driver to use only device-managed functions. The regulator_bulk_disable() is passed on a devm_add_action_or_reset() hook and the devm_iio_device_register() can be used to register the IIO device. The driver has both I2C and SPI hooks inside, so all these can be removed. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210913115237.301310-1-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5064.c | 49 ++++++++++++------------------------------------ 1 file changed, 12 insertions(+), 37 deletions(-) diff --git a/drivers/iio/dac/ad5064.c b/drivers/iio/dac/ad5064.c index dff623b65e4f..fd9cac4f6321 100644 --- a/drivers/iio/dac/ad5064.c +++ b/drivers/iio/dac/ad5064.c @@ -843,6 +843,13 @@ static int ad5064_request_vref(struct ad5064_state *st, struct device *dev) return ret; } +static void ad5064_bulk_reg_disable(void *data) +{ + struct ad5064_state *st = data; + + regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg); +} + static int ad5064_probe(struct device *dev, enum ad5064_type type, const char *name, ad5064_write_func write) { @@ -858,7 +865,6 @@ static int ad5064_probe(struct device *dev, enum ad5064_type type, st = iio_priv(indio_dev); mutex_init(&st->lock); - dev_set_drvdata(dev, indio_dev); st->chip_info = &ad5064_chip_info_tbl[type]; st->dev = dev; @@ -872,6 +878,10 @@ static int ad5064_probe(struct device *dev, enum ad5064_type type, ret = regulator_bulk_enable(ad5064_num_vref(st), st->vref_reg); if (ret) return ret; + + ret = devm_add_action_or_reset(dev, ad5064_bulk_reg_disable, st); + if (ret) + return ret; } indio_dev->name = name; @@ -887,30 +897,7 @@ static int ad5064_probe(struct device *dev, enum ad5064_type type, st->dac_cache[i] = midscale; } - ret = iio_device_register(indio_dev); - if (ret) - goto error_disable_reg; - - return 0; - -error_disable_reg: - if (!st->use_internal_vref) - regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg); - - return ret; -} - -static int ad5064_remove(struct device *dev) -{ - struct iio_dev *indio_dev = dev_get_drvdata(dev); - struct ad5064_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - if (!st->use_internal_vref) - regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg); - - return 0; + return devm_iio_device_register(dev, indio_dev); } #if IS_ENABLED(CONFIG_SPI_MASTER) @@ -932,11 +919,6 @@ static int ad5064_spi_probe(struct spi_device *spi) ad5064_spi_write); } -static int ad5064_spi_remove(struct spi_device *spi) -{ - return ad5064_remove(&spi->dev); -} - static const struct spi_device_id ad5064_spi_ids[] = { {"ad5024", ID_AD5024}, {"ad5025", ID_AD5025}, @@ -963,7 +945,6 @@ static struct spi_driver ad5064_spi_driver = { .name = "ad5064", }, .probe = ad5064_spi_probe, - .remove = ad5064_spi_remove, .id_table = ad5064_spi_ids, }; @@ -1019,11 +1000,6 @@ static int ad5064_i2c_probe(struct i2c_client *i2c, ad5064_i2c_write); } -static int ad5064_i2c_remove(struct i2c_client *i2c) -{ - return ad5064_remove(&i2c->dev); -} - static const struct i2c_device_id ad5064_i2c_ids[] = { {"ad5625", ID_AD5625 }, {"ad5625r-1v25", ID_AD5625R_1V25 }, @@ -1081,7 +1057,6 @@ static struct i2c_driver ad5064_i2c_driver = { .name = "ad5064", }, .probe = ad5064_i2c_probe, - .remove = ad5064_i2c_remove, .id_table = ad5064_i2c_ids, }; -- cgit v1.2.3-70-g09d2 From da6fd2590940803aae1426a30a64c95da8157cf8 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 13 Sep 2021 14:53:08 +0300 Subject: iio: gyro: adis16080: use devm_iio_device_register() in probe There is nothing else that needs to be done for this driver. The remove hook calls only the iio_device_unregister() hook. So this driver can use devm_iio_device_register() directly. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210913115308.301877-1-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/adis16080.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/iio/gyro/adis16080.c b/drivers/iio/gyro/adis16080.c index e2f4d943e220..acef59d822b1 100644 --- a/drivers/iio/gyro/adis16080.c +++ b/drivers/iio/gyro/adis16080.c @@ -195,8 +195,6 @@ static int adis16080_probe(struct spi_device *spi) if (!indio_dev) return -ENOMEM; st = iio_priv(indio_dev); - /* this is only used for removal purposes */ - spi_set_drvdata(spi, indio_dev); mutex_init(&st->lock); @@ -210,13 +208,7 @@ static int adis16080_probe(struct spi_device *spi) indio_dev->info = &adis16080_info; indio_dev->modes = INDIO_DIRECT_MODE; - return iio_device_register(indio_dev); -} - -static int adis16080_remove(struct spi_device *spi) -{ - iio_device_unregister(spi_get_drvdata(spi)); - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct spi_device_id adis16080_ids[] = { @@ -231,7 +223,6 @@ static struct spi_driver adis16080_driver = { .name = "adis16080", }, .probe = adis16080_probe, - .remove = adis16080_remove, .id_table = adis16080_ids, }; module_spi_driver(adis16080_driver); -- cgit v1.2.3-70-g09d2 From 2b025c92cdae1df5e254040de4410b4837f662df Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 13 Sep 2021 15:00:02 +0300 Subject: iio: light: max44000: use device-managed functions in probe This is a simple conversion. Both iio_device_register() and iio_triggered_buffer_setup() functions have device-managed variants. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210913120002.306280-1-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/max44000.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/drivers/iio/light/max44000.c b/drivers/iio/light/max44000.c index b8e721bced5b..85689dffbcbf 100644 --- a/drivers/iio/light/max44000.c +++ b/drivers/iio/light/max44000.c @@ -540,7 +540,6 @@ static int max44000_probe(struct i2c_client *client, return PTR_ERR(data->regmap); } - i2c_set_clientdata(client, indio_dev); mutex_init(&data->lock); indio_dev->info = &max44000_info; indio_dev->name = MAX44000_DRV_NAME; @@ -589,23 +588,14 @@ static int max44000_probe(struct i2c_client *client, return ret; } - ret = iio_triggered_buffer_setup(indio_dev, NULL, max44000_trigger_handler, NULL); + ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL, + max44000_trigger_handler, NULL); if (ret < 0) { dev_err(&client->dev, "iio triggered buffer setup failed\n"); return ret; } - return iio_device_register(indio_dev); -} - -static int max44000_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - - iio_device_unregister(indio_dev); - iio_triggered_buffer_cleanup(indio_dev); - - return 0; + return devm_iio_device_register(&client->dev, indio_dev); } static const struct i2c_device_id max44000_id[] = { @@ -628,7 +618,6 @@ static struct i2c_driver max44000_driver = { .acpi_match_table = ACPI_PTR(max44000_acpi_match), }, .probe = max44000_probe, - .remove = max44000_remove, .id_table = max44000_id, }; -- cgit v1.2.3-70-g09d2 From 0fe1402069812f0887877b26471d7a04bc985ced Mon Sep 17 00:00:00 2001 From: Daniel Palmer Date: Sun, 19 Sep 2021 16:26:16 +0900 Subject: iio: accel: mma7660: Mark acpi match table as maybe unused When building kernels without ACPI support the table is declared but is not used because ACPI_PTR() turns it into a NULL. Add the __maybe_unused attribute to stop the compiler whining. Signed-off-by: Daniel Palmer Link: https://lore.kernel.org/r/20210919072616.3849723-1-daniel@0x0f.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma7660.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/accel/mma7660.c b/drivers/iio/accel/mma7660.c index 47f5cd66e996..cd6cdf2c51b0 100644 --- a/drivers/iio/accel/mma7660.c +++ b/drivers/iio/accel/mma7660.c @@ -254,7 +254,7 @@ static const struct of_device_id mma7660_of_match[] = { }; MODULE_DEVICE_TABLE(of, mma7660_of_match); -static const struct acpi_device_id mma7660_acpi_id[] = { +static const struct acpi_device_id __maybe_unused mma7660_acpi_id[] = { {"MMA7660", 0}, {} }; -- cgit v1.2.3-70-g09d2 From 7685f507986548901ef1178baaf692e902095eb2 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Wed, 8 Sep 2021 18:56:38 +0800 Subject: iio: dac: stm32-dac: Make use of the helper function devm_platform_ioremap_resource() Use the devm_platform_ioremap_resource() helper instead of calling platform_get_resource() and devm_ioremap_resource() separately Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20210908105638.1525-1-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/stm32-dac-core.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/iio/dac/stm32-dac-core.c b/drivers/iio/dac/stm32-dac-core.c index 906436780347..9a6a68b11b2a 100644 --- a/drivers/iio/dac/stm32-dac-core.c +++ b/drivers/iio/dac/stm32-dac-core.c @@ -90,7 +90,6 @@ static int stm32_dac_probe(struct platform_device *pdev) const struct stm32_dac_cfg *cfg; struct stm32_dac_priv *priv; struct regmap *regmap; - struct resource *res; void __iomem *mmio; struct reset_control *rst; int ret; @@ -106,8 +105,7 @@ static int stm32_dac_probe(struct platform_device *pdev) cfg = (const struct stm32_dac_cfg *) of_match_device(dev->driver->of_match_table, dev)->data; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mmio = devm_ioremap_resource(dev, res); + mmio = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(mmio)) return PTR_ERR(mmio); -- cgit v1.2.3-70-g09d2 From 948b3b3daf2ba8c21b04d5d797d772b0bebdc91d Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Wed, 8 Sep 2021 18:56:30 +0800 Subject: iio: adc: rockchip_saradc: Make use of the helper function devm_platform_ioremap_resource() Use the devm_platform_ioremap_resource() helper instead of calling platform_get_resource() and devm_ioremap_resource() separately Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20210908105631.1474-1-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/rockchip_saradc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c index a237fe469a30..a56a0d7337ca 100644 --- a/drivers/iio/adc/rockchip_saradc.c +++ b/drivers/iio/adc/rockchip_saradc.c @@ -319,7 +319,6 @@ static int rockchip_saradc_probe(struct platform_device *pdev) struct rockchip_saradc *info = NULL; struct device_node *np = pdev->dev.of_node; struct iio_dev *indio_dev = NULL; - struct resource *mem; const struct of_device_id *match; int ret; int irq; @@ -348,8 +347,7 @@ static int rockchip_saradc_probe(struct platform_device *pdev) return -EINVAL; } - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - info->regs = devm_ioremap_resource(&pdev->dev, mem); + info->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(info->regs)) return PTR_ERR(info->regs); -- cgit v1.2.3-70-g09d2 From de37b16462a7f0e153fae7e30a57b318cf75a3df Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 24 Sep 2021 15:35:15 +0200 Subject: iio: adc: exynos: describe drivers in KConfig Describe better which driver applies to which SoC, to make configuring kernel for Samsung SoC easier. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20210924133515.112357-1-krzysztof.kozlowski@canonical.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index af168e1c9fdb..6cc1268da184 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -430,9 +430,9 @@ config EXYNOS_ADC depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || ARCH_S5PV210 || (OF && COMPILE_TEST) depends on HAS_IOMEM help - Core support for the ADC block found in the Samsung EXYNOS series - of SoCs for drivers such as the touchscreen and hwmon to use to share - this resource. + Driver for the ADC block found in the Samsung S3C (S3C2410, S3C2416, + S3C2440, S3C2443, S3C6410), S5Pv210 and Exynos SoCs. + Choose Y here only if you build for such Samsung SoC. To compile this driver as a module, choose M here: the module will be called exynos_adc. -- cgit v1.2.3-70-g09d2 From 2eacfc13c6e1c4b545719860a695e490aab3e64d Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Sun, 19 Sep 2021 22:36:56 +0200 Subject: dt-bindings: iio: kionix,kxcjk1013: driver support interrupts Device has interrupts support, which description was missing in the bindings. Signed-off-by: David Heidelberg Acked-by: Rob Herring Link: https://lore.kernel.org/r/20210919203656.119742-1-david@ixit.cz Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/accel/kionix,kxcjk1013.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/accel/kionix,kxcjk1013.yaml b/Documentation/devicetree/bindings/iio/accel/kionix,kxcjk1013.yaml index 52fa0f7c2d0e..714e48e613de 100644 --- a/Documentation/devicetree/bindings/iio/accel/kionix,kxcjk1013.yaml +++ b/Documentation/devicetree/bindings/iio/accel/kionix,kxcjk1013.yaml @@ -21,6 +21,9 @@ properties: reg: maxItems: 1 + interrupts: + maxItems: 1 + vdd-supply: true vddio-supply: true -- cgit v1.2.3-70-g09d2 From 558df982d4ead9cac628153d0d7b60feae05ddc8 Mon Sep 17 00:00:00 2001 From: Pekka Korpinen Date: Wed, 29 Sep 2021 21:57:55 +0300 Subject: iio: dac: ad5446: Fix ad5622_write() return value On success i2c_master_send() returns the number of bytes written. The call from iio_write_channel_info(), however, expects the return value to be zero on success. This bug causes incorrect consumption of the sysfs buffer in iio_write_channel_info(). When writing more than two characters to out_voltage0_raw, the ad5446 write handler is called multiple times causing unexpected behavior. Fixes: 3ec36a2cf0d5 ("iio:ad5446: Add support for I2C based DACs") Signed-off-by: Pekka Korpinen Link: https://lore.kernel.org/r/20210929185755.2384-1-pekka.korpinen@iki.fi Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5446.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c index 488ec69967d6..e50718422411 100644 --- a/drivers/iio/dac/ad5446.c +++ b/drivers/iio/dac/ad5446.c @@ -531,8 +531,15 @@ static int ad5622_write(struct ad5446_state *st, unsigned val) { struct i2c_client *client = to_i2c_client(st->dev); __be16 data = cpu_to_be16(val); + int ret; + + ret = i2c_master_send(client, (char *)&data, sizeof(data)); + if (ret < 0) + return ret; + if (ret != sizeof(data)) + return -EIO; - return i2c_master_send(client, (char *)&data, sizeof(data)); + return 0; } /* -- cgit v1.2.3-70-g09d2 From 2c0ad3f0cc04dec489552a21b80cd6d708bea96d Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Wed, 13 Oct 2021 12:04:38 +0800 Subject: iio: buffer: check return value of kstrdup_const() Check return value of kstrdup_const() in iio_buffer_wrap_attr(), or it will cause null-ptr-deref in kernfs_name_hash() when calling device_add() as follows: BUG: kernel NULL pointer dereference, address: 0000000000000000 RIP: 0010:strlen+0x0/0x20 Call Trace: kernfs_name_hash+0x22/0x110 kernfs_find_ns+0x11d/0x390 kernfs_remove_by_name_ns+0x3b/0xb0 remove_files.isra.1+0x7b/0x190 internal_create_group+0x7f1/0xbb0 internal_create_groups+0xa3/0x150 device_add+0x8f0/0x2020 cdev_device_add+0xc3/0x160 __iio_device_register+0x1427/0x1b40 [industrialio] __devm_iio_device_register+0x22/0x80 [industrialio] adjd_s311_probe+0x195/0x200 [adjd_s311] i2c_device_probe+0xa07/0xbb0 Reported-by: Hulk Robot Fixes: 15097c7a1adc ("iio: buffer: wrap all buffer attributes into iio_dev_attr") Signed-off-by: Yang Yingliang Link: https://lore.kernel.org/r/20211013040438.1689277-1-yangyingliang@huawei.com Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index a95cc2da56be..55802da1deee 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -1312,6 +1312,11 @@ static struct attribute *iio_buffer_wrap_attr(struct iio_buffer *buffer, iio_attr->buffer = buffer; memcpy(&iio_attr->dev_attr, dattr, sizeof(iio_attr->dev_attr)); iio_attr->dev_attr.attr.name = kstrdup_const(attr->name, GFP_KERNEL); + if (!iio_attr->dev_attr.attr.name) { + kfree(iio_attr); + return NULL; + } + sysfs_attr_init(&iio_attr->dev_attr.attr); list_add(&iio_attr->l, &buffer->buffer_attr_list); -- cgit v1.2.3-70-g09d2 From 9a2ff8009e53296e47de72d5af0bc31cd53274ff Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Wed, 13 Oct 2021 17:43:43 +0800 Subject: iio: buffer: Fix memory leak in __iio_buffer_alloc_sysfs_and_mask() When iio_buffer_wrap_attr() returns NULL or buffer->buffer_group.name alloc fails, the 'attr' which is allocated in __iio_buffer_alloc_sysfs_and_mask() is not freed, and cause memory leak. unreferenced object 0xffff888014882a00 (size 64): comm "i2c-adjd_s311-8", pid 424, jiffies 4294907737 (age 44.396s) hex dump (first 32 bytes): 00 0f 8a 15 80 88 ff ff 00 0e 8a 15 80 88 ff ff ................ 80 04 8a 15 80 88 ff ff 80 05 8a 15 80 88 ff ff ................ backtrace: [<0000000021752e67>] __kmalloc+0x1af/0x3c0 [<0000000043e8305c>] iio_buffers_alloc_sysfs_and_mask+0xe73/0x1570 [industrialio] [<00000000b7aa5a17>] __iio_device_register+0x483/0x1a30 [industrialio] [<000000003fa0fb2f>] __devm_iio_device_register+0x23/0x90 [industrialio] [<000000003ab040cf>] adjd_s311_probe+0x19c/0x200 [adjd_s311] [<0000000080458969>] i2c_device_probe+0xa31/0xbe0 [<00000000e20678ad>] really_probe+0x299/0xc30 [<000000006bea9b27>] __driver_probe_device+0x357/0x500 [<00000000e1df10d4>] driver_probe_device+0x4e/0x140 [<0000000003661beb>] __device_attach_driver+0x257/0x340 [<000000005bb4aa26>] bus_for_each_drv+0x166/0x1e0 [<00000000272c5236>] __device_attach+0x272/0x420 [<00000000d52a96ae>] bus_probe_device+0x1eb/0x2a0 [<00000000129f7737>] device_add+0xbf0/0x1f90 [<000000005eed4e52>] i2c_new_client_device+0x622/0xb20 [<00000000b85a9c43>] new_device_store+0x1fa/0x420 This patch fix to free it before the error return. Reported-by: Hulk Robot Fixes: 15097c7a1adc ("iio: buffer: wrap all buffer attributes into iio_dev_attr") Fixes: d9a625744ed0 ("iio: core: merge buffer/ & scan_elements/ attributes") Signed-off-by: Yang Yingliang Link: https://lore.kernel.org/r/20211013094343.315275-1-yangyingliang@huawei.com Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 55802da1deee..e2587237dbf9 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -1536,6 +1536,7 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, sizeof(struct attribute *) * buffer_attrcount); buffer_attrcount += ARRAY_SIZE(iio_buffer_attrs); + buffer->buffer_group.attrs = attr; for (i = 0; i < buffer_attrcount; i++) { struct attribute *wrapped; @@ -1543,7 +1544,7 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, wrapped = iio_buffer_wrap_attr(buffer, attr[i]); if (!wrapped) { ret = -ENOMEM; - goto error_free_scan_mask; + goto error_free_buffer_attrs; } attr[i] = wrapped; } @@ -1558,8 +1559,6 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, goto error_free_buffer_attrs; } - buffer->buffer_group.attrs = attr; - ret = iio_device_register_sysfs_group(indio_dev, &buffer->buffer_group); if (ret) goto error_free_buffer_attr_group_name; -- cgit v1.2.3-70-g09d2 From 09776d9374e635b1580b3736c19b95b788fbaa85 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Wed, 13 Oct 2021 12:49:22 +0300 Subject: iio: buffer: Fix double-free in iio_buffers_alloc_sysfs_and_mask() When __iio_buffer_alloc_sysfs_and_mask() failed, 'unwind_idx' should be set to 'i - 1' to prevent double-free when cleanup resources. BUG: KASAN: double-free or invalid-free in __iio_buffer_free_sysfs_and_mask+0x32/0xb0 [industrialio] Call Trace: kfree+0x117/0x4c0 __iio_buffer_free_sysfs_and_mask+0x32/0xb0 [industrialio] iio_buffers_alloc_sysfs_and_mask+0x60d/0x1570 [industrialio] __iio_device_register+0x483/0x1a30 [industrialio] ina2xx_probe+0x625/0x980 [ina2xx_adc] Reported-by: Hulk Robot Fixes: ee708e6baacd ("iio: buffer: introduce support for attaching more IIO buffers") Signed-off-by: Yang Yingliang Reviewed-by: Alexandru Ardelean Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211013094923.2473-2-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index e2587237dbf9..ae0912a14578 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -1620,7 +1620,7 @@ int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev) buffer = iio_dev_opaque->attached_buffers[i]; ret = __iio_buffer_alloc_sysfs_and_mask(buffer, indio_dev, i); if (ret) { - unwind_idx = i; + unwind_idx = i - 1; goto error_unwind_sysfs_and_mask; } } -- cgit v1.2.3-70-g09d2 From 604faf9a2ecd1addcc0c10a47e5aaef3c4d4fd6b Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Wed, 13 Oct 2021 22:42:42 +0800 Subject: iio: buffer: Fix memory leak in iio_buffer_register_legacy_sysfs_groups() If the second iio_device_register_sysfs_group() fails, 'legacy_buffer_group.attrs' need be freed too or it will cause memory leak: unreferenced object 0xffff888003618280 (size 64): comm "xrun", pid 357, jiffies 4294907259 (age 22.296s) hex dump (first 32 bytes): 80 f6 8c 03 80 88 ff ff 80 fb 8c 03 80 88 ff ff ................ 00 f9 8c 03 80 88 ff ff 80 fc 8c 03 80 88 ff ff ................ backtrace: [<00000000076bfd43>] __kmalloc+0x1a3/0x2f0 [<00000000c32e4886>] iio_buffers_alloc_sysfs_and_mask+0xc31/0x1290 [industrialio] Reported-by: Hulk Robot Fixes: d9a625744ed0 ("iio: core: merge buffer/ & scan_elements/ attributes") Signed-off-by: Yang Yingliang Link: https://lore.kernel.org/r/20211013144242.1685060-1-yangyingliang@huawei.com Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index ae0912a14578..1c3972150ab4 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -1367,10 +1367,10 @@ static int iio_buffer_register_legacy_sysfs_groups(struct iio_dev *indio_dev, return 0; -error_free_buffer_attrs: - kfree(iio_dev_opaque->legacy_buffer_group.attrs); error_free_scan_el_attrs: kfree(iio_dev_opaque->legacy_scan_el_group.attrs); +error_free_buffer_attrs: + kfree(iio_dev_opaque->legacy_buffer_group.attrs); return ret; } -- cgit v1.2.3-70-g09d2 From fe6f45f6ba22d625a8500cbad0237c60dd3117ee Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Tue, 12 Oct 2021 14:36:24 +0800 Subject: iio: core: check return value when calling dev_set_name() I got a null-ptr-deref report when doing fault injection test: BUG: kernel NULL pointer dereference, address: 0000000000000000 RIP: 0010:strlen+0x0/0x20 Call Trace: start_creating+0x199/0x2f0 debugfs_create_dir+0x25/0x430 __iio_device_register+0x4da/0x1b40 [industrialio] __devm_iio_device_register+0x22/0x80 [industrialio] max1027_probe+0x639/0x860 [max1027] spi_probe+0x183/0x210 really_probe+0x285/0xc30 If dev_set_name() fails, the dev_name() is null, check the return value of dev_set_name() to avoid the null-ptr-deref. Reported-by: Hulk Robot Fixes: e553f182d55b ("staging: iio: core: Introduce debugfs support...") Signed-off-by: Yang Yingliang Cc: Link: https://lore.kernel.org/r/20211012063624.3167460-1-yangyingliang@huawei.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 2dbb37e09b8c..48fda6a79076 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1664,7 +1664,13 @@ struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv) kfree(iio_dev_opaque); return NULL; } - dev_set_name(&indio_dev->dev, "iio:device%d", iio_dev_opaque->id); + + if (dev_set_name(&indio_dev->dev, "iio:device%d", iio_dev_opaque->id)) { + ida_simple_remove(&iio_ida, iio_dev_opaque->id); + kfree(iio_dev_opaque); + return NULL; + } + INIT_LIST_HEAD(&iio_dev_opaque->buffer_list); INIT_LIST_HEAD(&iio_dev_opaque->ioctl_handlers); -- cgit v1.2.3-70-g09d2 From 19833c40d0415d6fe4340b5b9c46239abbf718f6 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Wed, 13 Oct 2021 11:05:32 +0800 Subject: iio: core: fix double free in iio_device_unregister_sysfs() I got the double free report: BUG: KASAN: double-free or invalid-free in kfree+0xce/0x390 iio_device_unregister_sysfs+0x108/0x13b [industrialio] iio_dev_release+0x9e/0x10e [industrialio] device_release+0xa5/0x240 If __iio_device_register() fails, iio_dev_opaque->groups will be freed in error path in iio_device_unregister_sysfs(), then iio_dev_release() will call iio_device_unregister_sysfs() again, it causes double free. Set iio_dev_opaque->groups to NULL when it's freed to fix this double free. Not this is a local work around for a more general mess around life time management that will get cleaned up and should make this handling unnecesarry. Fixes: 32f171724e5c ("iio: core: rework iio device group creation") Reported-by: Hulk Robot Reviewed-by: Alexandru Ardelean Signed-off-by: Yang Yingliang Link: https://lore.kernel.org/r/20211013030532.956133-1-yangyingliang@huawei.com Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 48fda6a79076..3e1e86d987cc 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1600,6 +1600,7 @@ static void iio_device_unregister_sysfs(struct iio_dev *indio_dev) kfree(iio_dev_opaque->chan_attr_group.attrs); iio_dev_opaque->chan_attr_group.attrs = NULL; kfree(iio_dev_opaque->groups); + iio_dev_opaque->groups = NULL; } static void iio_dev_release(struct device *device) -- cgit v1.2.3-70-g09d2 From 69b31fd7a61784692db6433c05d46915b1b1a680 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Thu, 7 Oct 2021 11:30:06 +0200 Subject: iio: adc: tsc2046: fix scan interval warning Sync if statement with the actual warning. Fixes: 9504db5765e8 ("iio: adc: tsc2046: fix a warning message in tsc2046_adc_update_scan_mode()") Signed-off-by: Oleksij Rempel Link: https://lore.kernel.org/r/20211007093007.1466-2-o.rempel@pengutronix.de Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-tsc2046.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ti-tsc2046.c b/drivers/iio/adc/ti-tsc2046.c index 170950d5dd49..d84ae6b008c1 100644 --- a/drivers/iio/adc/ti-tsc2046.c +++ b/drivers/iio/adc/ti-tsc2046.c @@ -398,7 +398,7 @@ static int tsc2046_adc_update_scan_mode(struct iio_dev *indio_dev, priv->xfer.len = size; priv->time_per_scan_us = size * 8 * priv->time_per_bit_ns / NSEC_PER_USEC; - if (priv->scan_interval_us > priv->time_per_scan_us) + if (priv->scan_interval_us < priv->time_per_scan_us) dev_warn(&priv->spi->dev, "The scan interval (%d) is less then calculated scan time (%d)\n", priv->scan_interval_us, priv->time_per_scan_us); -- cgit v1.2.3-70-g09d2 From 511c1957de9d9f5a70e6760dfb6af4382ae0501d Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Wed, 1 Sep 2021 19:20:00 +0300 Subject: habanalabs: add kernel-doc style comments Modify some comments in the uapi file to be in kernel-doc style. Signed-off-by: Oded Gabbay --- include/uapi/misc/habanalabs.h | 43 ++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index d13bb8c1b450..ccfcb4d188fc 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -272,6 +272,16 @@ enum hl_gaudi_pll_index { HL_GAUDI_PLL_MAX }; +/** + * enum hl_device_status - Device status information. + * @HL_DEVICE_STATUS_OPERATIONAL: Device is operational. + * @HL_DEVICE_STATUS_IN_RESET: Device is currently during reset. + * @HL_DEVICE_STATUS_MALFUNCTION: Device is unusable. + * @HL_DEVICE_STATUS_NEEDS_RESET: Device needs reset because auto reset was disabled. + * @HL_DEVICE_STATUS_IN_DEVICE_CREATION: Device is operational but its creation is still in + * progress. + * @HL_DEVICE_STATUS_LAST: Last status. + */ enum hl_device_status { HL_DEVICE_STATUS_OPERATIONAL, HL_DEVICE_STATUS_IN_RESET, @@ -556,33 +566,30 @@ enum gaudi_dcores { HL_GAUDI_ES_DCORE }; +/** + * struct hl_info_args - Main structure to retrieve device related information. + * @return_pointer: User space address of the relevant structure related to HL_INFO_* operation + * mentioned in @op. + * @return_size: Size of the structure used in @return_pointer, just like "size" in "snprintf", it + * limits how many bytes the kernel can write. For hw_events array, the size should be + * hl_info_hw_ip_info.num_of_events * sizeof(__u32). + * @op: Defines which type of information to be retrieved. Refer HL_INFO_* for details. + * @dcore_id: DCORE id for which the information is relevant (for Gaudi refer to enum gaudi_dcores). + * @ctx_id: Context ID of the user. Currently not in use. + * @period_ms: Period value, in milliseconds, for utilization rate in range 100ms - 1000ms in 100 ms + * resolution. Currently not in use. + * @pll_index: Index as defined in hl__pll_index enumeration. + * @pad: Padding to 64 bit. + */ struct hl_info_args { - /* Location of relevant struct in userspace */ __u64 return_pointer; - /* - * The size of the return value. Just like "size" in "snprintf", - * it limits how many bytes the kernel can write - * - * For hw_events array, the size should be - * hl_info_hw_ip_info.num_of_events * sizeof(__u32) - */ __u32 return_size; - - /* HL_INFO_* */ __u32 op; union { - /* Dcore id for which the information is relevant. - * For Gaudi refer to 'enum gaudi_dcores' - */ __u32 dcore_id; - /* Context ID - Currently not in use */ __u32 ctx_id; - /* Period value for utilization rate (100ms - 1000ms, in 100ms - * resolution. - */ __u32 period_ms; - /* PLL frequency retrieval */ __u32 pll_index; }; -- cgit v1.2.3-70-g09d2 From 4be9fb53039ab1327f058e09039404ad33926adb Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Thu, 2 Sep 2021 10:18:01 +0300 Subject: habanalabs: add debugfs node for configuring CS timeout Command submission timeout is currently determined during driver loading time. As some environments requires this timeout to be modified in runtime, we introduce a new debugfs node that controls the timeout value without the need to reload the driver. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- .../ABI/testing/debugfs-driver-habanalabs | 6 +++ drivers/misc/habanalabs/common/debugfs.c | 51 ++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/Documentation/ABI/testing/debugfs-driver-habanalabs b/Documentation/ABI/testing/debugfs-driver-habanalabs index 284e2dfa61cd..63c46d9d538f 100644 --- a/Documentation/ABI/testing/debugfs-driver-habanalabs +++ b/Documentation/ABI/testing/debugfs-driver-habanalabs @@ -226,6 +226,12 @@ Description: Gets the state dump occurring on a CS timeout or failure. Writing an integer X discards X state dumps, so that the next read would return X+1-st newest state dump. +What: /sys/kernel/debug/habanalabs/hl/timeout_locked +Date: Sep 2021 +KernelVersion: 5.16 +Contact: obitton@habana.ai +Description: Sets the command submission timeout value in seconds. + What: /sys/kernel/debug/habanalabs/hl/stop_on_err Date: Mar 2020 KernelVersion: 5.6 diff --git a/drivers/misc/habanalabs/common/debugfs.c b/drivers/misc/habanalabs/common/debugfs.c index 985f1f3dbd20..1f2a3dc6c4e2 100644 --- a/drivers/misc/habanalabs/common/debugfs.c +++ b/drivers/misc/habanalabs/common/debugfs.c @@ -1167,6 +1167,45 @@ static ssize_t hl_state_dump_write(struct file *f, const char __user *buf, return count; } +static ssize_t hl_timeout_locked_read(struct file *f, char __user *buf, + size_t count, loff_t *ppos) +{ + struct hl_dbg_device_entry *entry = file_inode(f)->i_private; + struct hl_device *hdev = entry->hdev; + char tmp_buf[200]; + ssize_t rc; + + if (*ppos) + return 0; + + sprintf(tmp_buf, "%d\n", + jiffies_to_msecs(hdev->timeout_jiffies) / 1000); + rc = simple_read_from_buffer(buf, strlen(tmp_buf) + 1, ppos, tmp_buf, + strlen(tmp_buf) + 1); + + return rc; +} + +static ssize_t hl_timeout_locked_write(struct file *f, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct hl_dbg_device_entry *entry = file_inode(f)->i_private; + struct hl_device *hdev = entry->hdev; + u32 value; + ssize_t rc; + + rc = kstrtouint_from_user(buf, count, 10, &value); + if (rc) + return rc; + + if (value) + hdev->timeout_jiffies = msecs_to_jiffies(value * 1000); + else + hdev->timeout_jiffies = MAX_SCHEDULE_TIMEOUT; + + return count; +} + static const struct file_operations hl_data32b_fops = { .owner = THIS_MODULE, .read = hl_data_read32, @@ -1240,6 +1279,12 @@ static const struct file_operations hl_state_dump_fops = { .write = hl_state_dump_write }; +static const struct file_operations hl_timeout_locked_fops = { + .owner = THIS_MODULE, + .read = hl_timeout_locked_read, + .write = hl_timeout_locked_write +}; + static const struct hl_info_list hl_debugfs_list[] = { {"command_buffers", command_buffers_show, NULL}, {"command_submission", command_submission_show, NULL}, @@ -1421,6 +1466,12 @@ void hl_debugfs_add_device(struct hl_device *hdev) dev_entry, &hl_state_dump_fops); + debugfs_create_file("timeout_locked", + 0644, + dev_entry->root, + dev_entry, + &hl_timeout_locked_fops); + for (i = 0, entry = dev_entry->entry_arr ; i < count ; i++, entry++) { debugfs_create_file(hl_debugfs_list[i].name, 0444, -- cgit v1.2.3-70-g09d2 From 7457269136043fdbb7ff28bbc82d9655ad6012d7 Mon Sep 17 00:00:00 2001 From: Rajaravi Krishna Katta Date: Tue, 24 Aug 2021 16:39:16 +0300 Subject: habanalabs: create static map of f/w hwmon enums Instead of using the Linux kernel HWMON enums definition when communicating with the firmware, use proprietary HWMON based enums i.e. map hwmon.h header enum to cpucp_if.h based enum while. This is needed because the HWMON enums are not forcing backward compatibility and therefore changes can break compatibility between newer driver and older firmware. The driver will check for CPU_BOOT_DEV_STS0_MAP_HWMON_EN bit to validate if f/w supports cpucp->hwmon enum mapping to support older firmware where this mapping won't be available. Signed-off-by: Rajaravi Krishna Katta Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/hwmon.c | 100 +++++++++++++++++++-- .../misc/habanalabs/include/common/hl_boot_if.h | 6 ++ 2 files changed, 97 insertions(+), 9 deletions(-) diff --git a/drivers/misc/habanalabs/common/hwmon.c b/drivers/misc/habanalabs/common/hwmon.c index 6b421d76b311..8f12d99fda09 100644 --- a/drivers/misc/habanalabs/common/hwmon.c +++ b/drivers/misc/habanalabs/common/hwmon.c @@ -113,6 +113,9 @@ static int hl_read(struct device *dev, enum hwmon_sensor_types type, { struct hl_device *hdev = dev_get_drvdata(dev); int rc; + u32 cpucp_attr; + bool use_cpucp_enum = (hdev->asic_prop.fw_app_cpu_boot_dev_sts0 & + CPU_BOOT_DEV_STS0_MAP_HWMON_EN) ? true : false; if (!hl_device_operational(hdev, NULL)) return -ENODEV; @@ -121,65 +124,117 @@ static int hl_read(struct device *dev, enum hwmon_sensor_types type, case hwmon_temp: switch (attr) { case hwmon_temp_input: + cpucp_attr = cpucp_temp_input; + break; case hwmon_temp_max: + cpucp_attr = cpucp_temp_max; + break; case hwmon_temp_crit: + cpucp_attr = cpucp_temp_crit; + break; case hwmon_temp_max_hyst: + cpucp_attr = cpucp_temp_max_hyst; + break; case hwmon_temp_crit_hyst: + cpucp_attr = cpucp_temp_crit_hyst; + break; case hwmon_temp_offset: + cpucp_attr = cpucp_temp_offset; + break; case hwmon_temp_highest: + cpucp_attr = cpucp_temp_highest; break; default: return -EINVAL; } - rc = hl_get_temperature(hdev, channel, attr, val); + if (use_cpucp_enum) + rc = hl_get_temperature(hdev, channel, cpucp_attr, val); + else + rc = hl_get_temperature(hdev, channel, attr, val); break; case hwmon_in: switch (attr) { case hwmon_in_input: + cpucp_attr = cpucp_in_input; + break; case hwmon_in_min: + cpucp_attr = cpucp_in_min; + break; case hwmon_in_max: + cpucp_attr = cpucp_in_max; + break; case hwmon_in_highest: + cpucp_attr = cpucp_in_highest; break; default: return -EINVAL; } - rc = hl_get_voltage(hdev, channel, attr, val); + if (use_cpucp_enum) + rc = hl_get_voltage(hdev, channel, cpucp_attr, val); + else + rc = hl_get_voltage(hdev, channel, attr, val); break; case hwmon_curr: switch (attr) { case hwmon_curr_input: + cpucp_attr = cpucp_curr_input; + break; case hwmon_curr_min: + cpucp_attr = cpucp_curr_min; + break; case hwmon_curr_max: + cpucp_attr = cpucp_curr_max; + break; case hwmon_curr_highest: + cpucp_attr = cpucp_curr_highest; break; default: return -EINVAL; } - rc = hl_get_current(hdev, channel, attr, val); + if (use_cpucp_enum) + rc = hl_get_current(hdev, channel, cpucp_attr, val); + else + rc = hl_get_current(hdev, channel, attr, val); break; case hwmon_fan: switch (attr) { case hwmon_fan_input: + cpucp_attr = cpucp_fan_input; + break; case hwmon_fan_min: + cpucp_attr = cpucp_fan_min; + break; case hwmon_fan_max: + cpucp_attr = cpucp_fan_max; break; default: return -EINVAL; } - rc = hl_get_fan_speed(hdev, channel, attr, val); + + if (use_cpucp_enum) + rc = hl_get_fan_speed(hdev, channel, cpucp_attr, val); + else + rc = hl_get_fan_speed(hdev, channel, attr, val); break; case hwmon_pwm: switch (attr) { case hwmon_pwm_input: + cpucp_attr = cpucp_pwm_input; + break; case hwmon_pwm_enable: + cpucp_attr = cpucp_pwm_enable; break; default: return -EINVAL; } - rc = hl_get_pwm_info(hdev, channel, attr, val); + + if (use_cpucp_enum) + rc = hl_get_pwm_info(hdev, channel, cpucp_attr, val); + else + rc = hl_get_pwm_info(hdev, channel, attr, val); break; default: return -EINVAL; @@ -191,6 +246,9 @@ static int hl_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { struct hl_device *hdev = dev_get_drvdata(dev); + u32 cpucp_attr; + bool use_cpucp_enum = (hdev->asic_prop.fw_app_cpu_boot_dev_sts0 & + CPU_BOOT_DEV_STS0_MAP_HWMON_EN) ? true : false; if (!hl_device_operational(hdev, NULL)) return -ENODEV; @@ -199,40 +257,64 @@ static int hl_write(struct device *dev, enum hwmon_sensor_types type, case hwmon_temp: switch (attr) { case hwmon_temp_offset: + cpucp_attr = cpucp_temp_offset; + break; case hwmon_temp_reset_history: + cpucp_attr = cpucp_temp_reset_history; break; default: return -EINVAL; } - hl_set_temperature(hdev, channel, attr, val); + + if (use_cpucp_enum) + hl_set_temperature(hdev, channel, cpucp_attr, val); + else + hl_set_temperature(hdev, channel, attr, val); break; case hwmon_pwm: switch (attr) { case hwmon_pwm_input: + cpucp_attr = cpucp_pwm_input; + break; case hwmon_pwm_enable: + cpucp_attr = cpucp_pwm_enable; break; default: return -EINVAL; } - hl_set_pwm_info(hdev, channel, attr, val); + + if (use_cpucp_enum) + hl_set_pwm_info(hdev, channel, cpucp_attr, val); + else + hl_set_pwm_info(hdev, channel, attr, val); break; case hwmon_in: switch (attr) { case hwmon_in_reset_history: + cpucp_attr = cpucp_in_reset_history; break; default: return -EINVAL; } - hl_set_voltage(hdev, channel, attr, val); + + if (use_cpucp_enum) + hl_set_voltage(hdev, channel, cpucp_attr, val); + else + hl_set_voltage(hdev, channel, attr, val); break; case hwmon_curr: switch (attr) { case hwmon_curr_reset_history: + cpucp_attr = cpucp_curr_reset_history; break; default: return -EINVAL; } - hl_set_current(hdev, channel, attr, val); + + if (use_cpucp_enum) + hl_set_current(hdev, channel, cpucp_attr, val); + else + hl_set_current(hdev, channel, attr, val); break; default: return -EINVAL; diff --git a/drivers/misc/habanalabs/include/common/hl_boot_if.h b/drivers/misc/habanalabs/include/common/hl_boot_if.h index 3099653234e4..8837925b5d85 100644 --- a/drivers/misc/habanalabs/include/common/hl_boot_if.h +++ b/drivers/misc/habanalabs/include/common/hl_boot_if.h @@ -252,6 +252,11 @@ * where a bit is set if the engine is not idle. * Initialized in: linux * + * CPU_BOOT_DEV_STS0_MAP_HWMON_EN + * If set, means f/w supports proprietary + * HWMON enum mapping to cpucp enums. + * Initialized in: linux + * * CPU_BOOT_DEV_STS0_ENABLED Device status register enabled. * This is a main indication that the * running FW populates the device status @@ -287,6 +292,7 @@ #define CPU_BOOT_DEV_STS0_FW_NIC_STAT_XPCS91_EN (1 << 23) #define CPU_BOOT_DEV_STS0_FW_NIC_STAT_EXT_EN (1 << 24) #define CPU_BOOT_DEV_STS0_IS_IDLE_CHECK_EN (1 << 25) +#define CPU_BOOT_DEV_STS0_MAP_HWMON_EN (1 << 26) #define CPU_BOOT_DEV_STS0_ENABLED (1 << 31) #define CPU_BOOT_DEV_STS1_ENABLED (1 << 31) -- cgit v1.2.3-70-g09d2 From 2ee58fee3f8c63a1c689ad6f2fc72821cabb7748 Mon Sep 17 00:00:00 2001 From: Alon Mizrahi Date: Wed, 1 Sep 2021 18:05:00 +0300 Subject: habanalabs: generalize COMMS message sending procedure Instead of having dedicated function per message that we want to send to the firmware in COMMS protocol, have a generic function that we can call to from other parts of the driver Signed-off-by: Alon Mizrahi Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/firmware_if.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index 8d2568c63f19..4e68fb9d2a6b 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -2162,18 +2162,17 @@ static void hl_fw_linux_update_state(struct hl_device *hdev, } /** - * hl_fw_dynamic_report_reset_cause - send a COMMS message with the cause - * of the newly triggered hard reset + * hl_fw_dynamic_send_msg - send a COMMS message with attached data * * @hdev: pointer to the habanalabs device structure * @fw_loader: managing structure for loading device's FW - * @reset_cause: enumerated cause for the recent hard reset + * @msg_type: message type + * @data: data to be sent * * @return 0 on success, otherwise non-zero error code */ -static int hl_fw_dynamic_report_reset_cause(struct hl_device *hdev, - struct fw_load_mgr *fw_loader, - enum comms_reset_cause reset_cause) +static int hl_fw_dynamic_send_msg(struct hl_device *hdev, + struct fw_load_mgr *fw_loader, u8 msg_type, void *data) { struct lkd_msg_comms msg; int rc; @@ -2181,11 +2180,20 @@ static int hl_fw_dynamic_report_reset_cause(struct hl_device *hdev, memset(&msg, 0, sizeof(msg)); /* create message to be sent */ - msg.header.type = HL_COMMS_RESET_CAUSE_TYPE; + msg.header.type = msg_type; msg.header.size = cpu_to_le16(sizeof(struct comms_msg_header)); msg.header.magic = cpu_to_le32(HL_COMMS_MSG_MAGIC); - msg.reset_cause = reset_cause; + switch (msg_type) { + case HL_COMMS_RESET_CAUSE_TYPE: + msg.reset_cause = *(__u8 *) data; + break; + default: + dev_err(hdev->dev, + "Send COMMS message - invalid message type %u\n", + msg_type); + return -EINVAL; + } rc = hl_fw_dynamic_request_descriptor(hdev, fw_loader, sizeof(struct lkd_msg_comms)); @@ -2252,8 +2260,8 @@ static int hl_fw_dynamic_init_cpu(struct hl_device *hdev, goto protocol_err; if (hdev->curr_reset_cause) { - rc = hl_fw_dynamic_report_reset_cause(hdev, fw_loader, - hdev->curr_reset_cause); + rc = hl_fw_dynamic_send_msg(hdev, fw_loader, + HL_COMMS_RESET_CAUSE_TYPE, &hdev->curr_reset_cause); if (rc) goto protocol_err; -- cgit v1.2.3-70-g09d2 From 2b28485d0a3bf8aa220af4644eb186b5034ff830 Mon Sep 17 00:00:00 2001 From: Rajaravi Krishna Katta Date: Wed, 18 Aug 2021 08:12:41 +0300 Subject: habanalabs: enable power info via HWMON framework Add support to retrieve following power info via HWMON: - instantaneous power value - highest value since last reset - reset the highest place holder Signed-off-by: Rajaravi Krishna Katta Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/habanalabs.h | 4 + drivers/misc/habanalabs/common/hwmon.c | 94 +++++++++++++++++++++++ drivers/misc/habanalabs/include/common/cpucp_if.h | 10 +++ 3 files changed, 108 insertions(+) diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index bebebcb163ee..f8e23ca18a57 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -2956,6 +2956,10 @@ int hl_set_voltage(struct hl_device *hdev, int sensor_index, u32 attr, long value); int hl_set_current(struct hl_device *hdev, int sensor_index, u32 attr, long value); +int hl_set_power(struct hl_device *hdev, + int sensor_index, u32 attr, long value); +int hl_get_power(struct hl_device *hdev, + int sensor_index, u32 attr, long *value); void hw_sob_get(struct hl_hw_sob *hw_sob); void hw_sob_put(struct hl_hw_sob *hw_sob); void hl_encaps_handle_do_release(struct kref *ref); diff --git a/drivers/misc/habanalabs/common/hwmon.c b/drivers/misc/habanalabs/common/hwmon.c index 8f12d99fda09..e33f65be8a00 100644 --- a/drivers/misc/habanalabs/common/hwmon.c +++ b/drivers/misc/habanalabs/common/hwmon.c @@ -236,6 +236,23 @@ static int hl_read(struct device *dev, enum hwmon_sensor_types type, else rc = hl_get_pwm_info(hdev, channel, attr, val); break; + case hwmon_power: + switch (attr) { + case hwmon_power_input: + cpucp_attr = CPUCP_POWER_INPUT; + break; + case hwmon_power_input_highest: + cpucp_attr = CPUCP_POWER_INPUT_HIGHEST; + break; + default: + return -EINVAL; + } + + if (use_cpucp_enum) + rc = hl_get_power(hdev, channel, cpucp_attr, val); + else + rc = hl_get_power(hdev, channel, attr, val); + break; default: return -EINVAL; } @@ -316,6 +333,20 @@ static int hl_write(struct device *dev, enum hwmon_sensor_types type, else hl_set_current(hdev, channel, attr, val); break; + case hwmon_power: + switch (attr) { + case hwmon_power_reset_history: + cpucp_attr = CPUCP_POWER_RESET_INPUT_HISTORY; + break; + default: + return -EINVAL; + } + + if (use_cpucp_enum) + hl_set_power(hdev, channel, cpucp_attr, val); + else + hl_set_power(hdev, channel, attr, val); + break; default: return -EINVAL; } @@ -378,6 +409,15 @@ static umode_t hl_is_visible(const void *data, enum hwmon_sensor_types type, return 0644; } break; + case hwmon_power: + switch (attr) { + case hwmon_power_input: + case hwmon_power_input_highest: + return 0444; + case hwmon_power_reset_history: + return 0200; + } + break; default: break; } @@ -633,6 +673,60 @@ int hl_set_current(struct hl_device *hdev, return rc; } +int hl_set_power(struct hl_device *hdev, + int sensor_index, u32 attr, long value) +{ + struct cpucp_packet pkt; + int rc; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ctl = cpu_to_le32(CPUCP_PACKET_POWER_GET << + CPUCP_PKT_CTL_OPCODE_SHIFT); + pkt.sensor_index = __cpu_to_le16(sensor_index); + pkt.type = __cpu_to_le16(attr); + pkt.value = __cpu_to_le64(value); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + 0, NULL); + + if (rc) + dev_err(hdev->dev, + "Failed to set power of sensor %d, error %d\n", + sensor_index, rc); + + return rc; +} + +int hl_get_power(struct hl_device *hdev, + int sensor_index, u32 attr, long *value) +{ + struct cpucp_packet pkt; + u64 result; + int rc; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ctl = cpu_to_le32(CPUCP_PACKET_POWER_GET << + CPUCP_PKT_CTL_OPCODE_SHIFT); + pkt.sensor_index = __cpu_to_le16(sensor_index); + pkt.type = __cpu_to_le16(attr); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + 0, &result); + + *value = (long) result; + + if (rc) { + dev_err(hdev->dev, + "Failed to get power of sensor %d, error %d\n", + sensor_index, rc); + *value = 0; + } + + return rc; +} + int hl_hwmon_init(struct hl_device *hdev) { struct device *dev = hdev->pdev ? &hdev->pdev->dev : hdev->dev; diff --git a/drivers/misc/habanalabs/include/common/cpucp_if.h b/drivers/misc/habanalabs/include/common/cpucp_if.h index 9ff6a448f0d4..a6fa1cfa38a5 100644 --- a/drivers/misc/habanalabs/include/common/cpucp_if.h +++ b/drivers/misc/habanalabs/include/common/cpucp_if.h @@ -598,6 +598,16 @@ enum cpucp_pll_type_attributes { cpucp_pll_pci, }; +/* + * cpucp_power_type aligns with hwmon_power_attributes + * defined in Linux kernel hwmon.h file + */ +enum cpucp_power_type { + CPUCP_POWER_INPUT = 8, + CPUCP_POWER_INPUT_HIGHEST = 9, + CPUCP_POWER_RESET_INPUT_HISTORY = 11 +}; + /* * MSI type enumeration table for all ASICs and future SW versions. * For future ASIC-LKD compatibility, we can only add new enumerations. -- cgit v1.2.3-70-g09d2 From 027d53b03ca1fe55ea9356ffdb7296432d236e59 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Mon, 13 Sep 2021 10:55:02 +0300 Subject: habanalabs: remove redundant cs validity checks During TDR handling, we check multiple times if CS is valid. No need to perform this check as CS must be valid at all time during the TDR handling. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_submission.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index 6dafff375f1c..a344928363c8 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -431,11 +431,10 @@ static void cs_handle_tdr(struct hl_device *hdev, struct hl_cs *cs) /* Don't cancel TDR in case this CS was timedout because we might be * running from the TDR context */ - if (cs && (cs->timedout || - hdev->timeout_jiffies == MAX_SCHEDULE_TIMEOUT)) + if (cs->timedout || hdev->timeout_jiffies == MAX_SCHEDULE_TIMEOUT) return; - if (cs && cs->tdr_active) + if (cs->tdr_active) cancel_delayed_work_sync(&cs->work_tdr); spin_lock(&hdev->cs_mirror_lock); -- cgit v1.2.3-70-g09d2 From d62b9a6976cdac30a3af745de1f935ffe246fcdd Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Thu, 23 Sep 2021 12:02:23 +0300 Subject: habanalabs: add support for a long interrupt target value In order to avoid user target value wraparound, we modify the current interface so user will be able to wait for an 8-byte target value rather than a 4-byte value. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_submission.c | 8 ++++---- include/uapi/misc/habanalabs.h | 13 +++++++------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index a344928363c8..44bab01cd033 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -2739,13 +2739,13 @@ static int hl_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data) static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx, u32 timeout_us, u64 user_address, - u32 target_value, u16 interrupt_offset, + u64 target_value, u16 interrupt_offset, enum hl_cs_wait_status *status) { struct hl_user_pending_interrupt *pend; struct hl_user_interrupt *interrupt; unsigned long timeout, flags; - u32 completion_value; + u64 completion_value; long completion_rc; int rc = 0; @@ -2779,7 +2779,7 @@ static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx, /* We check for completion value as interrupt could have been received * before we added the node to the wait list */ - if (copy_from_user(&completion_value, u64_to_user_ptr(user_address), 4)) { + if (copy_from_user(&completion_value, u64_to_user_ptr(user_address), 8)) { dev_err(hdev->dev, "Failed to copy completion value from user\n"); rc = -EFAULT; goto remove_pending_user_interrupt; @@ -2811,7 +2811,7 @@ wait_again: reinit_completion(&pend->fence.completion); spin_unlock_irqrestore(&interrupt->wait_list_lock, flags); - if (copy_from_user(&completion_value, u64_to_user_ptr(user_address), 4)) { + if (copy_from_user(&completion_value, u64_to_user_ptr(user_address), 8)) { dev_err(hdev->dev, "Failed to copy completion value from user\n"); rc = -EFAULT; diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index ccfcb4d188fc..fe73630e1a05 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -897,11 +897,7 @@ struct hl_wait_cs_in { */ __u64 addr; /* Target value for completion comparison */ - __u32 target; - /* Absolute timeout to wait for interrupt - * in microseconds - */ - __u32 interrupt_timeout_us; + __u64 target; }; }; @@ -917,7 +913,12 @@ struct hl_wait_cs_in { /* Multi CS API info- valid entries in multi-CS array */ __u8 seq_arr_len; - __u8 pad[7]; + __u8 pad[3]; + + /* Absolute timeout to wait for an interrupt in microseconds. + * Relevant only when HL_WAIT_CS_FLAGS_INTERRUPT is set + */ + __u32 interrupt_timeout_us; }; #define HL_WAIT_CS_STATUS_COMPLETED 0 -- cgit v1.2.3-70-g09d2 From dd08335fb909e62bd290117f34490ef4e577b554 Mon Sep 17 00:00:00 2001 From: Yuri Nudelman Date: Thu, 30 Sep 2021 11:18:01 +0300 Subject: habanalabs: fix debugfs device memory MMU VA translation The translation in debugfs of device memory MMU virtual addresses was wrong as it did not take into consideration the fact that the page sizes there can be not power of 2. Signed-off-by: Yuri Nudelman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/mmu/mmu.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/misc/habanalabs/common/mmu/mmu.c b/drivers/misc/habanalabs/common/mmu/mmu.c index 792d25b79ea6..aa96917f62e5 100644 --- a/drivers/misc/habanalabs/common/mmu/mmu.c +++ b/drivers/misc/habanalabs/common/mmu/mmu.c @@ -501,23 +501,25 @@ static void hl_mmu_pa_page_with_offset(struct hl_ctx *ctx, u64 virt_addr, if ((hops->range_type == HL_VA_RANGE_TYPE_DRAM) && !is_power_of_2(prop->dram_page_size)) { - unsigned long dram_page_size = prop->dram_page_size; - u64 page_offset_mask; - u64 phys_addr_mask; - u32 bit; + u64 dram_page_size, dram_base, abs_phys_addr, abs_virt_addr, + page_id, page_start; + u32 page_off; /* - * find last set bit in page_size to cover all bits of page - * offset. note that 1 has to be added to bit index. - * note that the internal ulong variable is used to avoid - * alignment issue. + * Bit arithmetics cannot be used for non power of two page + * sizes. In addition, since bit arithmetics is not used, + * we cannot ignore dram base. All that shall be considerd. */ - bit = find_last_bit(&dram_page_size, - sizeof(dram_page_size) * BITS_PER_BYTE) + 1; - page_offset_mask = (BIT_ULL(bit) - 1); - phys_addr_mask = ~page_offset_mask; - *phys_addr = (tmp_phys_addr & phys_addr_mask) | - (virt_addr & page_offset_mask); + + dram_page_size = prop->dram_page_size; + dram_base = prop->dram_base_address; + abs_phys_addr = tmp_phys_addr - dram_base; + abs_virt_addr = virt_addr - dram_base; + page_id = DIV_ROUND_DOWN_ULL(abs_phys_addr, dram_page_size); + page_start = page_id * dram_page_size; + div_u64_rem(abs_virt_addr, dram_page_size, &page_off); + + *phys_addr = page_start + page_off + dram_base; } else { /* * find the correct hop shift field in hl_mmu_properties -- cgit v1.2.3-70-g09d2 From a00f1f571e50eb33c5b89db8ac7cd2d684da2943 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Thu, 30 Sep 2021 11:36:07 +0300 Subject: habanalabs: define soft-reset as inference op Soft-reset is the procedure where we reset only the compute/DMA engines of the device, without requiring the current user-space process to release the device. This type of reset can happen if TDR event occurred (a workload got stuck) or by a root request through sysfs. This is only relevant for inference ASICs, as there is no real-world use-case to do that in training, because training runs on multiple devices. In addition, we also do (in certain ASICs) a reset upon device release. That reset uses the same code as the soft-reset. Therefore, to better differentiate between the two resets, it is better to rename the soft-reset support as "inference soft-reset", to make the code more self-explanatory. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/device.c | 2 +- drivers/misc/habanalabs/common/habanalabs.h | 10 +++++++--- drivers/misc/habanalabs/common/sysfs.c | 6 +++--- drivers/misc/habanalabs/goya/goya.c | 2 +- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index 97c7c86580e6..8f96f2bb14ec 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -954,7 +954,7 @@ int hl_device_reset(struct hl_device *hdev, u32 flags) goto do_reset; } - if (!hard_reset && !hdev->allow_external_soft_reset) { + if (!hard_reset && !hdev->allow_inference_soft_reset) { hard_instead_soft = true; hard_reset = true; } diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index f8e23ca18a57..99de80915eed 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -2440,8 +2440,12 @@ struct multi_cs_data { * @collective_mon_idx: helper index for collective initialization * @supports_coresight: is CoreSight supported. * @supports_soft_reset: is soft reset supported. - * @allow_external_soft_reset: true if soft reset initiated by user or TDR is - * allowed. + * @allow_inference_soft_reset: true if the ASIC supports soft reset that is + * initiated by user or TDR. This is only true + * in inference ASICs, as there is no real-world + * use-case of doing soft-reset in training (due + * to the fact that training runs on multiple + * devices) * @supports_cb_mapping: is mapping a CB to the device's MMU supported. * @needs_reset: true if reset_on_lockup is false and device should be reset * due to lockup. @@ -2572,7 +2576,7 @@ struct hl_device { u8 collective_mon_idx; u8 supports_coresight; u8 supports_soft_reset; - u8 allow_external_soft_reset; + u8 allow_inference_soft_reset; u8 supports_cb_mapping; u8 needs_reset; u8 process_kill_trial_cnt; diff --git a/drivers/misc/habanalabs/common/sysfs.c b/drivers/misc/habanalabs/common/sysfs.c index 34f9f2779962..42c1769ad25d 100644 --- a/drivers/misc/habanalabs/common/sysfs.c +++ b/drivers/misc/habanalabs/common/sysfs.c @@ -206,12 +206,12 @@ static ssize_t soft_reset_store(struct device *dev, goto out; } - if (!hdev->allow_external_soft_reset) { - dev_err(hdev->dev, "Device does not support soft-reset\n"); + if (!hdev->allow_inference_soft_reset) { + dev_err(hdev->dev, "Device does not support inference soft-reset\n"); goto out; } - dev_warn(hdev->dev, "Soft-Reset requested through sysfs\n"); + dev_warn(hdev->dev, "Inference Soft-Reset requested through sysfs\n"); hl_device_reset(hdev, 0); diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 031c1849da14..ef67e2586ede 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -959,7 +959,7 @@ static int goya_sw_init(struct hl_device *hdev) spin_lock_init(&goya->hw_queues_lock); hdev->supports_coresight = true; hdev->supports_soft_reset = true; - hdev->allow_external_soft_reset = true; + hdev->allow_inference_soft_reset = true; hdev->supports_wait_for_multi_cs = false; hdev->asic_funcs->set_pci_memory_regions(hdev); -- cgit v1.2.3-70-g09d2 From 1282dbbd292e9525cc5659ae1409188b76869c4c Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Thu, 30 Sep 2021 11:53:37 +0300 Subject: habanalabs: refactor reset log message Reset to the device is not necessarily due to an error, so print it as info instead of error. In addition, print the type of reset we are doing: - reset of the entire device (aka hard reset) - reset of the device after user have released it (less than hard reset) - lighter reset of an inference device (aka soft reset) Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/device.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index 8f96f2bb14ec..be18ad0c1bfc 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -1018,7 +1018,14 @@ do_reset: take_release_locks(hdev); - dev_err(hdev->dev, "Going to RESET device!\n"); + if (hard_reset) + dev_info(hdev->dev, "Going to reset device\n"); + else if (flags & HL_RESET_DEVICE_RELEASE) + dev_info(hdev->dev, + "Going to reset device after it was released by user\n"); + else + dev_info(hdev->dev, + "Going to reset compute engines of inference device\n"); } again: -- cgit v1.2.3-70-g09d2 From c1904127ce8d1b37154c1a1d1fc5836a0ef6c830 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Thu, 30 Sep 2021 11:22:32 +0300 Subject: habanalabs: prevent race between fd close/open The driver allows only a single process to open a device's FD at any single time. This is done by checking "hdev->compute_ctx" under mutex. Therefore, to prevent a race between the moment a user closes it's FD and when another user tries to open the device, we need to make sure that clearing this variable is the very last thing that is done in the code of the FD's release. I'm moving the idle check before clearing this variable and the "reset on device release". btw, if the reset happens it will prevent any other user from opening the device until the reset is finished. An important thing to note is that we need to remove the user process that is closing the device from the process list BEFORE calling the reset function. That is to prevent a case where the reset code will try to kill that user process and it is unnecessary as the process doesn't hold any device/driver resources anymore. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/device.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index be18ad0c1bfc..e1949b087ae3 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -69,13 +69,6 @@ static void hpriv_release(struct kref *ref) mutex_destroy(&hpriv->restore_phase_mutex); - mutex_lock(&hdev->fpriv_list_lock); - list_del(&hpriv->dev_node); - hdev->compute_ctx = NULL; - mutex_unlock(&hdev->fpriv_list_lock); - - kfree(hpriv); - if ((!hdev->pldm) && (hdev->pdev) && (!hdev->asic_funcs->is_device_idle(hdev, idle_mask, @@ -87,9 +80,32 @@ static void hpriv_release(struct kref *ref) device_is_idle = false; } + /* We need to remove the user from the list to make sure the reset process won't + * try to kill the user process. Because, if we got here, it means there are no + * more driver/device resources that the user process is occupying so there is + * no need to kill it + * + * However, we can't set the compute_ctx to NULL at this stage. This is to prevent + * a race between the release and opening the device again. We don't want to let + * a user open the device while there a reset is about to happen. + */ + mutex_lock(&hdev->fpriv_list_lock); + list_del(&hpriv->dev_node); + mutex_unlock(&hdev->fpriv_list_lock); + if ((hdev->reset_if_device_not_idle && !device_is_idle) || hdev->reset_upon_device_release) hl_device_reset(hdev, HL_RESET_DEVICE_RELEASE); + + /* Now we can mark the compute_ctx as empty. Even if a reset is running in a different + * thread, we don't care because the in_reset is marked so if a user will try to open + * the device it will fail on that, even if compute_ctx is NULL. + */ + mutex_lock(&hdev->fpriv_list_lock); + hdev->compute_ctx = NULL; + mutex_unlock(&hdev->fpriv_list_lock); + + kfree(hpriv); } void hl_hpriv_get(struct hl_fpriv *hpriv) -- cgit v1.2.3-70-g09d2 From f05d17b226dbb5e2f21b724918b263cba57f2ad8 Mon Sep 17 00:00:00 2001 From: Yuri Nudelman Date: Thu, 23 Sep 2021 17:40:14 +0300 Subject: habanalabs: take timestamp on wait for interrupt Taking an accurate timestamp in a close proximity of the interrupt is required for user side statistics management. Signed-off-by: Yuri Nudelman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_submission.c | 17 +++++++++++++++-- drivers/misc/habanalabs/common/habanalabs.h | 2 ++ drivers/misc/habanalabs/common/irq.c | 5 ++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index 44bab01cd033..4bc24852a283 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -2740,7 +2740,8 @@ static int hl_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data) static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx, u32 timeout_us, u64 user_address, u64 target_value, u16 interrupt_offset, - enum hl_cs_wait_status *status) + enum hl_cs_wait_status *status, + bool take_timestamp, u64 *timestamp) { struct hl_user_pending_interrupt *pend; struct hl_user_interrupt *interrupt; @@ -2764,6 +2765,8 @@ static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx, hl_fence_init(&pend->fence, ULONG_MAX); + pend->fence.take_timestamp = take_timestamp; + if (interrupt_offset == HL_COMMON_USER_INTERRUPT_ID) interrupt = &hdev->common_user_interrupt; else @@ -2838,6 +2841,8 @@ remove_pending_user_interrupt: list_del(&pend->wait_list_node); spin_unlock_irqrestore(&interrupt->wait_list_lock, flags); + *timestamp = ktime_to_ns(pend->fence.timestamp); + kfree(pend); hl_ctx_put(ctx); @@ -2851,6 +2856,7 @@ static int hl_interrupt_wait_ioctl(struct hl_fpriv *hpriv, void *data) struct asic_fixed_properties *prop; union hl_wait_cs_args *args = data; enum hl_cs_wait_status status; + u64 timestamp; int rc; prop = &hdev->asic_prop; @@ -2880,7 +2886,9 @@ static int hl_interrupt_wait_ioctl(struct hl_fpriv *hpriv, void *data) rc = _hl_interrupt_wait_ioctl(hdev, hpriv->ctx, args->in.interrupt_timeout_us, args->in.addr, - args->in.target, interrupt_offset, &status); + args->in.target, interrupt_offset, &status, + args->in.flags & HL_CS_FLAGS_TIMESTAMP, + ×tamp); if (rc) { if (rc != -EINTR) @@ -2892,6 +2900,11 @@ static int hl_interrupt_wait_ioctl(struct hl_fpriv *hpriv, void *data) memset(args, 0, sizeof(*args)); + if (timestamp) { + args->out.timestamp_nsec = timestamp; + args->out.flags |= HL_WAIT_CS_STATUS_FLAG_TIMESTAMP_VLD; + } + switch (status) { case CS_WAIT_STATUS_COMPLETED: args->out.status = HL_WAIT_CS_STATUS_COMPLETED; diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 99de80915eed..2d9edd734d1c 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -601,6 +601,7 @@ struct asic_fixed_properties { * masters QIDs that multi cs is waiting on * @error: mark this fence with error * @timestamp: timestamp upon completion + * @take_timestamp: timestamp shall be taken upon completion */ struct hl_fence { struct completion completion; @@ -609,6 +610,7 @@ struct hl_fence { u32 stream_master_qid_map; int error; ktime_t timestamp; + u8 take_timestamp; }; /** diff --git a/drivers/misc/habanalabs/common/irq.c b/drivers/misc/habanalabs/common/irq.c index 39b14a933393..0dd00ffd1809 100644 --- a/drivers/misc/habanalabs/common/irq.c +++ b/drivers/misc/habanalabs/common/irq.c @@ -143,8 +143,11 @@ static void handle_user_cq(struct hl_device *hdev, struct hl_user_pending_interrupt *pend; spin_lock(&user_cq->wait_list_lock); - list_for_each_entry(pend, &user_cq->wait_list_head, wait_list_node) + list_for_each_entry(pend, &user_cq->wait_list_head, wait_list_node) { + if (pend->fence.take_timestamp) + pend->fence.timestamp = ktime_get(); complete_all(&pend->fence.completion); + } spin_unlock(&user_cq->wait_list_lock); } -- cgit v1.2.3-70-g09d2 From 10cab81d1cf92b1b62234540efba34ccaf7079e8 Mon Sep 17 00:00:00 2001 From: Bharat Jauhari Date: Mon, 21 Jun 2021 09:57:19 +0300 Subject: habanalabs: bypass reset for continuous h/w error event There may be a situation where drivers receives continuous fatal H/W error events from FW immediately post reset cycle. This may be due to some fault on the silicon itself. In such case its better to bypass reset cycle so we won't be stuck in endless loop of resets. This commit bypasses reset request in case driver received two back to back FW fatal error before first occurrence of heartbeat event. Signed-off-by: Bharat Jauhari Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/device.c | 118 +++++++++++++++++------- drivers/misc/habanalabs/common/habanalabs.h | 14 +++ drivers/misc/habanalabs/common/habanalabs_drv.c | 1 + drivers/misc/habanalabs/gaudi/gaudi.c | 8 +- drivers/misc/habanalabs/goya/goya.c | 6 ++ 5 files changed, 111 insertions(+), 36 deletions(-) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index e1949b087ae3..2022e5d7b3ad 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -546,6 +546,19 @@ static void hl_device_heartbeat(struct work_struct *work) return; reschedule: + /* + * prev_reset_trigger tracks consecutive fatal h/w errors until first + * heartbeat immediately post reset. + * If control reached here, then at least one heartbeat work has been + * scheduled since last reset/init cycle. + * So if the device is not already in reset cycle, reset the flag + * prev_reset_trigger as no reset occurred with HL_RESET_FW_FATAL_ERR + * status for at least one heartbeat. From this point driver restarts + * tracking future consecutive fatal errors. + */ + if (!(atomic_read(&hdev->in_reset))) + hdev->prev_reset_trigger = HL_RESET_TRIGGER_DEFAULT; + schedule_delayed_work(&hdev->work_heartbeat, usecs_to_jiffies(HL_HEARTBEAT_PER_USEC)); } @@ -925,6 +938,65 @@ static void device_disable_open_processes(struct hl_device *hdev) mutex_unlock(&hdev->fpriv_list_lock); } +static void handle_reset_trigger(struct hl_device *hdev, u32 flags) +{ + u32 cur_reset_trigger = HL_RESET_TRIGGER_DEFAULT; + + /* + * 'reset cause' is being updated here, because getting here + * means that it's the 1st time and the last time we're here + * ('in_reset' makes sure of it). This makes sure that + * 'reset_cause' will continue holding its 1st recorded reason! + */ + if (flags & HL_RESET_HEARTBEAT) { + hdev->curr_reset_cause = HL_RESET_CAUSE_HEARTBEAT; + cur_reset_trigger = HL_RESET_HEARTBEAT; + } else if (flags & HL_RESET_TDR) { + hdev->curr_reset_cause = HL_RESET_CAUSE_TDR; + cur_reset_trigger = HL_RESET_TDR; + } else if (flags & HL_RESET_FW_FATAL_ERR) { + hdev->curr_reset_cause = HL_RESET_CAUSE_UNKNOWN; + cur_reset_trigger = HL_RESET_FW_FATAL_ERR; + } else { + hdev->curr_reset_cause = HL_RESET_CAUSE_UNKNOWN; + } + + /* + * If reset cause is same twice, then reset_trigger_repeated + * is set and if this reset is due to a fatal FW error + * device is set to an unstable state. + */ + if (hdev->prev_reset_trigger != cur_reset_trigger) { + hdev->prev_reset_trigger = cur_reset_trigger; + hdev->reset_trigger_repeated = 0; + } else { + hdev->reset_trigger_repeated = 1; + } + + /* If reset is due to heartbeat, device CPU is no responsive in + * which case no point sending PCI disable message to it. + * + * If F/W is performing the reset, no need to send it a message to disable + * PCI access + */ + if ((flags & HL_RESET_HARD) && + !(flags & (HL_RESET_HEARTBEAT | HL_RESET_FW))) { + /* Disable PCI access from device F/W so he won't send + * us additional interrupts. We disable MSI/MSI-X at + * the halt_engines function and we can't have the F/W + * sending us interrupts after that. We need to disable + * the access here because if the device is marked + * disable, the message won't be send. Also, in case + * of heartbeat, the device CPU is marked as disable + * so this message won't be sent + */ + if (hl_fw_send_pci_access_msg(hdev, + CPUCP_PACKET_DISABLE_PCI_ACCESS)) + dev_warn(hdev->dev, + "Failed to disable PCI access by F/W\n"); + } +} + /* * hl_device_reset - reset the device * @@ -994,40 +1066,7 @@ do_reset: if (rc) return 0; - /* - * 'reset cause' is being updated here, because getting here - * means that it's the 1st time and the last time we're here - * ('in_reset' makes sure of it). This makes sure that - * 'reset_cause' will continue holding its 1st recorded reason! - */ - if (flags & HL_RESET_HEARTBEAT) - hdev->curr_reset_cause = HL_RESET_CAUSE_HEARTBEAT; - else if (flags & HL_RESET_TDR) - hdev->curr_reset_cause = HL_RESET_CAUSE_TDR; - else - hdev->curr_reset_cause = HL_RESET_CAUSE_UNKNOWN; - - /* If reset is due to heartbeat, device CPU is no responsive in - * which case no point sending PCI disable message to it. - * - * If F/W is performing the reset, no need to send it a message to disable - * PCI access - */ - if (hard_reset && !(flags & (HL_RESET_HEARTBEAT | HL_RESET_FW))) { - /* Disable PCI access from device F/W so he won't send - * us additional interrupts. We disable MSI/MSI-X at - * the halt_engines function and we can't have the F/W - * sending us interrupts after that. We need to disable - * the access here because if the device is marked - * disable, the message won't be send. Also, in case - * of heartbeat, the device CPU is marked as disable - * so this message won't be sent - */ - if (hl_fw_send_pci_access_msg(hdev, - CPUCP_PACKET_DISABLE_PCI_ACCESS)) - dev_warn(hdev->dev, - "Failed to disable PCI access by F/W\n"); - } + handle_reset_trigger(hdev, flags); /* This also blocks future CS/VM/JOB completion operations */ hdev->disabled = true; @@ -1131,6 +1170,17 @@ kill_processes: hdev->device_cpu_disabled = false; hdev->hard_reset_pending = false; + if (hdev->reset_trigger_repeated && + (hdev->prev_reset_trigger == HL_RESET_FW_FATAL_ERR)) { + /* if there 2 back to back resets from FW, + * ensure driver puts the driver in a unusable state + */ + dev_crit(hdev->dev, + "Consecutive FW fatal errors received, stopping hard reset\n"); + rc = -EIO; + goto out_err; + } + if (hdev->kernel_ctx) { dev_crit(hdev->dev, "kernel ctx was alive during hard reset, something is terribly wrong\n"); diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 2d9edd734d1c..a06135155b57 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -68,6 +68,9 @@ #define HL_STATE_DUMP_HIST_LEN 5 +/* Default value for device reset trigger , an invalid value */ +#define HL_RESET_TRIGGER_DEFAULT 0xFF + #define OBJ_NAMES_HASH_TABLE_BITS 7 /* 1 << 7 buckets */ #define SYNC_TO_ENGINE_HASH_TABLE_BITS 7 /* 1 << 7 buckets */ @@ -132,13 +135,18 @@ enum hl_mmu_page_table_location { * - HL_RESET_FW * F/W will perform the reset. No need to ask it to reset the device. This is relevant * only when running with secured f/w + * + * - HL_RESET_FW_FATAL_ERR + * Set if reset is due to a fatal error from FW */ + #define HL_RESET_HARD (1 << 0) #define HL_RESET_FROM_RESET_THREAD (1 << 1) #define HL_RESET_HEARTBEAT (1 << 2) #define HL_RESET_TDR (1 << 3) #define HL_RESET_DEVICE_RELEASE (1 << 4) #define HL_RESET_FW (1 << 5) +#define HL_RESET_FW_FATAL_ERR (1 << 6) #define HL_MAX_SOBS_PER_MONITOR 8 @@ -2458,6 +2466,10 @@ struct multi_cs_data { * @supports_staged_submission: true if staged submissions are supported * @curr_reset_cause: saves an enumerated reset cause when a hard reset is * triggered, and cleared after it is shared with preboot. + * @prev_reset_trigger: saves the previous trigger which caused a reset, overidden + * with a new value on next reset + * @reset_trigger_repeated: set if device reset is triggered more than once with + * same cause. * @skip_reset_on_timeout: Skip device reset if CS has timed out, wait for it to * complete instead. * @device_cpu_is_halted: Flag to indicate whether the device CPU was already @@ -2585,6 +2597,8 @@ struct hl_device { u8 device_fini_pending; u8 supports_staged_submission; u8 curr_reset_cause; + u8 prev_reset_trigger; + u8 reset_trigger_repeated; u8 skip_reset_on_timeout; u8 device_cpu_is_halted; u8 supports_wait_for_multi_cs; diff --git a/drivers/misc/habanalabs/common/habanalabs_drv.c b/drivers/misc/habanalabs/common/habanalabs_drv.c index a75e4fceb9d8..1da56069750a 100644 --- a/drivers/misc/habanalabs/common/habanalabs_drv.c +++ b/drivers/misc/habanalabs/common/habanalabs_drv.c @@ -339,6 +339,7 @@ int create_hdev(struct hl_device **dev, struct pci_dev *pdev, set_driver_behavior_per_device(hdev); hdev->curr_reset_cause = HL_RESET_CAUSE_UNKNOWN; + hdev->prev_reset_trigger = HL_RESET_TRIGGER_DEFAULT; if (timeout_locked) hdev->timeout_jiffies = msecs_to_jiffies(timeout_locked * 1000); diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 14da87b38e83..70a668951ec4 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -7932,6 +7932,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, { struct gaudi_device *gaudi = hdev->asic_specific; u32 ctl = le32_to_cpu(eq_entry->hdr.ctl); + u32 fw_fatal_err_flag = 0; u16 event_type = ((ctl & EQ_CTL_EVENT_TYPE_MASK) >> EQ_CTL_EVENT_TYPE_SHIFT); bool reset_required; @@ -7972,6 +7973,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, case GAUDI_EVENT_NIC0_CS_DBG_DERR ... GAUDI_EVENT_NIC4_CS_DBG_DERR: gaudi_print_irq_info(hdev, event_type, true); gaudi_handle_ecc_event(hdev, event_type, &eq_entry->ecc_data); + fw_fatal_err_flag = HL_RESET_FW_FATAL_ERR; goto reset_device; case GAUDI_EVENT_GIC500: @@ -7979,6 +7981,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, case GAUDI_EVENT_L2_RAM_ECC: case GAUDI_EVENT_PLL0 ... GAUDI_EVENT_PLL17: gaudi_print_irq_info(hdev, event_type, false); + fw_fatal_err_flag = HL_RESET_FW_FATAL_ERR; goto reset_device; case GAUDI_EVENT_HBM0_SPI_0: @@ -7989,6 +7992,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, gaudi_hbm_read_interrupts(hdev, gaudi_hbm_event_to_dev(event_type), &eq_entry->hbm_ecc_data); + fw_fatal_err_flag = HL_RESET_FW_FATAL_ERR; goto reset_device; case GAUDI_EVENT_HBM0_SPI_1: @@ -8171,9 +8175,9 @@ static void gaudi_handle_eqe(struct hl_device *hdev, reset_device: if (hdev->asic_prop.fw_security_enabled) - hl_device_reset(hdev, HL_RESET_HARD | HL_RESET_FW); + hl_device_reset(hdev, HL_RESET_HARD | HL_RESET_FW | fw_fatal_err_flag); else if (hdev->hard_reset_on_fw_events) - hl_device_reset(hdev, HL_RESET_HARD); + hl_device_reset(hdev, HL_RESET_HARD | fw_fatal_err_flag); else hl_fw_unmask_irq(hdev, event_type); } diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index ef67e2586ede..78cf35879680 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -4829,6 +4829,12 @@ void goya_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entry) case GOYA_ASYNC_EVENT_ID_PLL0 ... GOYA_ASYNC_EVENT_ID_PLL6: case GOYA_ASYNC_EVENT_ID_AXI_ECC: case GOYA_ASYNC_EVENT_ID_L2_RAM_ECC: + goya_print_irq_info(hdev, event_type, false); + if (hdev->hard_reset_on_fw_events) + hl_device_reset(hdev, (HL_RESET_HARD | + HL_RESET_FW_FATAL_ERR)); + break; + case GOYA_ASYNC_EVENT_ID_PSOC_GPIO_05_SW_RESET: goya_print_irq_info(hdev, event_type, false); if (hdev->hard_reset_on_fw_events) -- cgit v1.2.3-70-g09d2 From efc6b04b869bcbcbe64da5d58eb1208599fb8041 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Tue, 5 Oct 2021 17:46:42 +0300 Subject: habanalabs: update firmware files Update the firmware headers to the latest version Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/include/common/cpucp_if.h | 12 +- .../misc/habanalabs/include/common/hl_boot_if.h | 185 +++++++++++++-------- .../misc/habanalabs/include/gaudi/gaudi_fw_if.h | 10 +- .../misc/habanalabs/include/gaudi/gaudi_reg_map.h | 1 + 4 files changed, 130 insertions(+), 78 deletions(-) diff --git a/drivers/misc/habanalabs/include/common/cpucp_if.h b/drivers/misc/habanalabs/include/common/cpucp_if.h index a6fa1cfa38a5..ae13231fda94 100644 --- a/drivers/misc/habanalabs/include/common/cpucp_if.h +++ b/drivers/misc/habanalabs/include/common/cpucp_if.h @@ -542,11 +542,14 @@ enum cpucp_packet_rc { */ enum cpucp_temp_type { cpucp_temp_input, + cpucp_temp_min = 4, + cpucp_temp_min_hyst, cpucp_temp_max = 6, cpucp_temp_max_hyst, cpucp_temp_crit, cpucp_temp_crit_hyst, cpucp_temp_offset = 19, + cpucp_temp_lowest = 21, cpucp_temp_highest = 22, cpucp_temp_reset_history = 23 }; @@ -555,6 +558,7 @@ enum cpucp_in_attributes { cpucp_in_input, cpucp_in_min, cpucp_in_max, + cpucp_in_lowest = 6, cpucp_in_highest = 7, cpucp_in_reset_history }; @@ -563,6 +567,7 @@ enum cpucp_curr_attributes { cpucp_curr_input, cpucp_curr_min, cpucp_curr_max, + cpucp_curr_lowest = 6, cpucp_curr_highest = 7, cpucp_curr_reset_history }; @@ -741,6 +746,9 @@ struct cpucp_security_info { * @pll_map: Bit map of supported PLLs for current ASIC version. * @mme_binning_mask: MME binning mask, * (0 = functional, 1 = binned) + * @dram_binning_mask: DRAM binning mask, 1 bit per dram instance + * (0 = functional 1 = binned) + * @memory_repair_flag: eFuse flag indicating memory repair */ struct cpucp_info { struct cpucp_sensor sensors[CPUCP_MAX_SENSORS]; @@ -759,7 +767,9 @@ struct cpucp_info { __le64 reserved3; __le64 reserved4; __u8 reserved5; - __u8 pad[7]; + __u8 dram_binning_mask; + __u8 memory_repair_flag; + __u8 pad[5]; struct cpucp_security_info sec_info; __le32 reserved6; __u8 pll_map[PLL_MAP_LEN]; diff --git a/drivers/misc/habanalabs/include/common/hl_boot_if.h b/drivers/misc/habanalabs/include/common/hl_boot_if.h index 8837925b5d85..2626df6ef3ef 100644 --- a/drivers/misc/habanalabs/include/common/hl_boot_if.h +++ b/drivers/misc/habanalabs/include/common/hl_boot_if.h @@ -15,6 +15,28 @@ #define VERSION_MAX_LEN 128 +enum cpu_boot_err { + CPU_BOOT_ERR_DRAM_INIT_FAIL = 0, + CPU_BOOT_ERR_FIT_CORRUPTED = 1, + CPU_BOOT_ERR_TS_INIT_FAIL = 2, + CPU_BOOT_ERR_DRAM_SKIPPED = 3, + CPU_BOOT_ERR_BMC_WAIT_SKIPPED = 4, + CPU_BOOT_ERR_NIC_DATA_NOT_RDY = 5, + CPU_BOOT_ERR_NIC_FW_FAIL = 6, + CPU_BOOT_ERR_SECURITY_NOT_RDY = 7, + CPU_BOOT_ERR_SECURITY_FAIL = 8, + CPU_BOOT_ERR_EFUSE_FAIL = 9, + CPU_BOOT_ERR_PRI_IMG_VER_FAIL = 10, + CPU_BOOT_ERR_SEC_IMG_VER_FAIL = 11, + CPU_BOOT_ERR_PLL_FAIL = 12, + CPU_BOOT_ERR_DEVICE_UNUSABLE_FAIL = 13, + CPU_BOOT_ERR_BOOT_FW_CRIT_ERR = 18, + CPU_BOOT_ERR_BINNING_FAIL = 19, + CPU_BOOT_ERR_ENABLED = 31, + CPU_BOOT_ERR_SCND_EN = 63, + CPU_BOOT_ERR_LAST = 64 /* we have 2 registers of 32 bits */ +}; + /* * CPU error bits in BOOT_ERROR registers * @@ -78,25 +100,13 @@ * CPU_BOOT_ERR0_DEVICE_UNUSABLE_FAIL Device is unusable and customer support * should be contacted. * - * CPU_BOOT_ERR0_ARC0_HALT_ACK_NOT_RCVD HALT ACK from ARC0 is not received - * within specified retries after issuing - * HALT request. ARC0 appears to be in bad - * reset. - * - * CPU_BOOT_ERR0_ARC1_HALT_ACK_NOT_RCVD HALT ACK from ARC1 is not received - * within specified retries after issuing - * HALT request. ARC1 appears to be in bad - * reset. - * - * CPU_BOOT_ERR0_ARC0_RUN_ACK_NOT_RCVD RUN ACK from ARC0 is not received - * within specified timeout after issuing - * RUN request. ARC0 appears to be in bad - * reset. + * CPU_BOOT_ERR0_BOOT_FW_CRIT_ERR Critical error was detected during + * the execution of ppboot or preboot. + * for example: stack overflow. * - * CPU_BOOT_ERR0_ARC1_RUN_ACK_NOT_RCVD RUN ACK from ARC1 is not received - * within specified timeout after issuing - * RUN request. ARC1 appears to be in bad - * reset. + * CPU_BOOT_ERR0_BINNING_FAIL Binning settings failed, meaning + * malfunctioning components might still be + * in use. * * CPU_BOOT_ERR0_ENABLED Error registers enabled. * This is a main indication that the @@ -104,26 +114,57 @@ * registers. Meaning the error bits are * not garbage, but actual error statuses. */ -#define CPU_BOOT_ERR0_DRAM_INIT_FAIL (1 << 0) -#define CPU_BOOT_ERR0_FIT_CORRUPTED (1 << 1) -#define CPU_BOOT_ERR0_TS_INIT_FAIL (1 << 2) -#define CPU_BOOT_ERR0_DRAM_SKIPPED (1 << 3) -#define CPU_BOOT_ERR0_BMC_WAIT_SKIPPED (1 << 4) -#define CPU_BOOT_ERR0_NIC_DATA_NOT_RDY (1 << 5) -#define CPU_BOOT_ERR0_NIC_FW_FAIL (1 << 6) -#define CPU_BOOT_ERR0_SECURITY_NOT_RDY (1 << 7) -#define CPU_BOOT_ERR0_SECURITY_FAIL (1 << 8) -#define CPU_BOOT_ERR0_EFUSE_FAIL (1 << 9) -#define CPU_BOOT_ERR0_PRI_IMG_VER_FAIL (1 << 10) -#define CPU_BOOT_ERR0_SEC_IMG_VER_FAIL (1 << 11) -#define CPU_BOOT_ERR0_PLL_FAIL (1 << 12) -#define CPU_BOOT_ERR0_DEVICE_UNUSABLE_FAIL (1 << 13) -#define CPU_BOOT_ERR0_ARC0_HALT_ACK_NOT_RCVD (1 << 14) -#define CPU_BOOT_ERR0_ARC1_HALT_ACK_NOT_RCVD (1 << 15) -#define CPU_BOOT_ERR0_ARC0_RUN_ACK_NOT_RCVD (1 << 16) -#define CPU_BOOT_ERR0_ARC1_RUN_ACK_NOT_RCVD (1 << 17) -#define CPU_BOOT_ERR0_ENABLED (1 << 31) -#define CPU_BOOT_ERR1_ENABLED (1 << 31) +#define CPU_BOOT_ERR0_DRAM_INIT_FAIL (1 << CPU_BOOT_ERR_DRAM_INIT_FAIL) +#define CPU_BOOT_ERR0_FIT_CORRUPTED (1 << CPU_BOOT_ERR_FIT_CORRUPTED) +#define CPU_BOOT_ERR0_TS_INIT_FAIL (1 << CPU_BOOT_ERR_TS_INIT_FAIL) +#define CPU_BOOT_ERR0_DRAM_SKIPPED (1 << CPU_BOOT_ERR_DRAM_SKIPPED) +#define CPU_BOOT_ERR0_BMC_WAIT_SKIPPED (1 << CPU_BOOT_ERR_BMC_WAIT_SKIPPED) +#define CPU_BOOT_ERR0_NIC_DATA_NOT_RDY (1 << CPU_BOOT_ERR_NIC_DATA_NOT_RDY) +#define CPU_BOOT_ERR0_NIC_FW_FAIL (1 << CPU_BOOT_ERR_NIC_FW_FAIL) +#define CPU_BOOT_ERR0_SECURITY_NOT_RDY (1 << CPU_BOOT_ERR_SECURITY_NOT_RDY) +#define CPU_BOOT_ERR0_SECURITY_FAIL (1 << CPU_BOOT_ERR_SECURITY_FAIL) +#define CPU_BOOT_ERR0_EFUSE_FAIL (1 << CPU_BOOT_ERR_EFUSE_FAIL) +#define CPU_BOOT_ERR0_PRI_IMG_VER_FAIL (1 << CPU_BOOT_ERR_PRI_IMG_VER_FAIL) +#define CPU_BOOT_ERR0_SEC_IMG_VER_FAIL (1 << CPU_BOOT_ERR_SEC_IMG_VER_FAIL) +#define CPU_BOOT_ERR0_PLL_FAIL (1 << CPU_BOOT_ERR_PLL_FAIL) +#define CPU_BOOT_ERR0_DEVICE_UNUSABLE_FAIL (1 << CPU_BOOT_ERR_DEVICE_UNUSABLE_FAIL) +#define CPU_BOOT_ERR0_BOOT_FW_CRIT_ERR (1 << CPU_BOOT_ERR_BOOT_FW_CRIT_ERR) +#define CPU_BOOT_ERR0_BINNING_FAIL (1 << CPU_BOOT_ERR_BINNING_FAIL) +#define CPU_BOOT_ERR0_ENABLED (1 << CPU_BOOT_ERR_ENABLED) +#define CPU_BOOT_ERR1_ENABLED (1 << CPU_BOOT_ERR_ENABLED) + +enum cpu_boot_dev_sts { + CPU_BOOT_DEV_STS_SECURITY_EN = 0, + CPU_BOOT_DEV_STS_DEBUG_EN = 1, + CPU_BOOT_DEV_STS_WATCHDOG_EN = 2, + CPU_BOOT_DEV_STS_DRAM_INIT_EN = 3, + CPU_BOOT_DEV_STS_BMC_WAIT_EN = 4, + CPU_BOOT_DEV_STS_E2E_CRED_EN = 5, + CPU_BOOT_DEV_STS_HBM_CRED_EN = 6, + CPU_BOOT_DEV_STS_RL_EN = 7, + CPU_BOOT_DEV_STS_SRAM_SCR_EN = 8, + CPU_BOOT_DEV_STS_DRAM_SCR_EN = 9, + CPU_BOOT_DEV_STS_FW_HARD_RST_EN = 10, + CPU_BOOT_DEV_STS_PLL_INFO_EN = 11, + CPU_BOOT_DEV_STS_SP_SRAM_EN = 12, + CPU_BOOT_DEV_STS_CLK_GATE_EN = 13, + CPU_BOOT_DEV_STS_HBM_ECC_EN = 14, + CPU_BOOT_DEV_STS_PKT_PI_ACK_EN = 15, + CPU_BOOT_DEV_STS_FW_LD_COM_EN = 16, + CPU_BOOT_DEV_STS_FW_IATU_CONF_EN = 17, + CPU_BOOT_DEV_STS_FW_NIC_MAC_EN = 18, + CPU_BOOT_DEV_STS_DYN_PLL_EN = 19, + CPU_BOOT_DEV_STS_GIC_PRIVILEGED_EN = 20, + CPU_BOOT_DEV_STS_EQ_INDEX_EN = 21, + CPU_BOOT_DEV_STS_MULTI_IRQ_POLL_EN = 22, + CPU_BOOT_DEV_STS_FW_NIC_STAT_XPCS91_EN = 23, + CPU_BOOT_DEV_STS_FW_NIC_STAT_EXT_EN = 24, + CPU_BOOT_DEV_STS_IS_IDLE_CHECK_EN = 25, + CPU_BOOT_DEV_STS_MAP_HWMON_EN = 26, + CPU_BOOT_DEV_STS_ENABLED = 31, + CPU_BOOT_DEV_STS_SCND_EN = 63, + CPU_BOOT_DEV_STS_LAST = 64 /* we have 2 registers of 32 bits */ +}; /* * BOOT DEVICE STATUS bits in BOOT_DEVICE_STS registers @@ -233,7 +274,7 @@ * was not served before. * Initialized in: linux * - * CPU_BOOT_DEV_STS0_MULTI_IRQ_POLL_EN Use multiple scratchpad interfaces to + * CPU_BOOT_DEV_STS0_MULTI_IRQ_POLL_EN Use multiple scratchpad interfaces to * prevent IRQs overriding each other. * Initialized in: linux * @@ -266,35 +307,35 @@ * Initialized in: preboot * */ -#define CPU_BOOT_DEV_STS0_SECURITY_EN (1 << 0) -#define CPU_BOOT_DEV_STS0_DEBUG_EN (1 << 1) -#define CPU_BOOT_DEV_STS0_WATCHDOG_EN (1 << 2) -#define CPU_BOOT_DEV_STS0_DRAM_INIT_EN (1 << 3) -#define CPU_BOOT_DEV_STS0_BMC_WAIT_EN (1 << 4) -#define CPU_BOOT_DEV_STS0_E2E_CRED_EN (1 << 5) -#define CPU_BOOT_DEV_STS0_HBM_CRED_EN (1 << 6) -#define CPU_BOOT_DEV_STS0_RL_EN (1 << 7) -#define CPU_BOOT_DEV_STS0_SRAM_SCR_EN (1 << 8) -#define CPU_BOOT_DEV_STS0_DRAM_SCR_EN (1 << 9) -#define CPU_BOOT_DEV_STS0_FW_HARD_RST_EN (1 << 10) -#define CPU_BOOT_DEV_STS0_PLL_INFO_EN (1 << 11) -#define CPU_BOOT_DEV_STS0_SP_SRAM_EN (1 << 12) -#define CPU_BOOT_DEV_STS0_CLK_GATE_EN (1 << 13) -#define CPU_BOOT_DEV_STS0_HBM_ECC_EN (1 << 14) -#define CPU_BOOT_DEV_STS0_PKT_PI_ACK_EN (1 << 15) -#define CPU_BOOT_DEV_STS0_FW_LD_COM_EN (1 << 16) -#define CPU_BOOT_DEV_STS0_FW_IATU_CONF_EN (1 << 17) -#define CPU_BOOT_DEV_STS0_FW_NIC_MAC_EN (1 << 18) -#define CPU_BOOT_DEV_STS0_DYN_PLL_EN (1 << 19) -#define CPU_BOOT_DEV_STS0_GIC_PRIVILEGED_EN (1 << 20) -#define CPU_BOOT_DEV_STS0_EQ_INDEX_EN (1 << 21) -#define CPU_BOOT_DEV_STS0_MULTI_IRQ_POLL_EN (1 << 22) -#define CPU_BOOT_DEV_STS0_FW_NIC_STAT_XPCS91_EN (1 << 23) -#define CPU_BOOT_DEV_STS0_FW_NIC_STAT_EXT_EN (1 << 24) -#define CPU_BOOT_DEV_STS0_IS_IDLE_CHECK_EN (1 << 25) -#define CPU_BOOT_DEV_STS0_MAP_HWMON_EN (1 << 26) -#define CPU_BOOT_DEV_STS0_ENABLED (1 << 31) -#define CPU_BOOT_DEV_STS1_ENABLED (1 << 31) +#define CPU_BOOT_DEV_STS0_SECURITY_EN (1 << CPU_BOOT_DEV_STS_SECURITY_EN) +#define CPU_BOOT_DEV_STS0_DEBUG_EN (1 << CPU_BOOT_DEV_STS_DEBUG_EN) +#define CPU_BOOT_DEV_STS0_WATCHDOG_EN (1 << CPU_BOOT_DEV_STS_WATCHDOG_EN) +#define CPU_BOOT_DEV_STS0_DRAM_INIT_EN (1 << CPU_BOOT_DEV_STS_DRAM_INIT_EN) +#define CPU_BOOT_DEV_STS0_BMC_WAIT_EN (1 << CPU_BOOT_DEV_STS_BMC_WAIT_EN) +#define CPU_BOOT_DEV_STS0_E2E_CRED_EN (1 << CPU_BOOT_DEV_STS_E2E_CRED_EN) +#define CPU_BOOT_DEV_STS0_HBM_CRED_EN (1 << CPU_BOOT_DEV_STS_HBM_CRED_EN) +#define CPU_BOOT_DEV_STS0_RL_EN (1 << CPU_BOOT_DEV_STS_RL_EN) +#define CPU_BOOT_DEV_STS0_SRAM_SCR_EN (1 << CPU_BOOT_DEV_STS_SRAM_SCR_EN) +#define CPU_BOOT_DEV_STS0_DRAM_SCR_EN (1 << CPU_BOOT_DEV_STS_DRAM_SCR_EN) +#define CPU_BOOT_DEV_STS0_FW_HARD_RST_EN (1 << CPU_BOOT_DEV_STS_FW_HARD_RST_EN) +#define CPU_BOOT_DEV_STS0_PLL_INFO_EN (1 << CPU_BOOT_DEV_STS_PLL_INFO_EN) +#define CPU_BOOT_DEV_STS0_SP_SRAM_EN (1 << CPU_BOOT_DEV_STS_SP_SRAM_EN) +#define CPU_BOOT_DEV_STS0_CLK_GATE_EN (1 << CPU_BOOT_DEV_STS_CLK_GATE_EN) +#define CPU_BOOT_DEV_STS0_HBM_ECC_EN (1 << CPU_BOOT_DEV_STS_HBM_ECC_EN) +#define CPU_BOOT_DEV_STS0_PKT_PI_ACK_EN (1 << CPU_BOOT_DEV_STS_PKT_PI_ACK_EN) +#define CPU_BOOT_DEV_STS0_FW_LD_COM_EN (1 << CPU_BOOT_DEV_STS_FW_LD_COM_EN) +#define CPU_BOOT_DEV_STS0_FW_IATU_CONF_EN (1 << CPU_BOOT_DEV_STS_FW_IATU_CONF_EN) +#define CPU_BOOT_DEV_STS0_FW_NIC_MAC_EN (1 << CPU_BOOT_DEV_STS_FW_NIC_MAC_EN) +#define CPU_BOOT_DEV_STS0_DYN_PLL_EN (1 << CPU_BOOT_DEV_STS_DYN_PLL_EN) +#define CPU_BOOT_DEV_STS0_GIC_PRIVILEGED_EN (1 << CPU_BOOT_DEV_STS_GIC_PRIVILEGED_EN) +#define CPU_BOOT_DEV_STS0_EQ_INDEX_EN (1 << CPU_BOOT_DEV_STS_EQ_INDEX_EN) +#define CPU_BOOT_DEV_STS0_MULTI_IRQ_POLL_EN (1 << CPU_BOOT_DEV_STS_MULTI_IRQ_POLL_EN) +#define CPU_BOOT_DEV_STS0_FW_NIC_STAT_XPCS91_EN (1 << CPU_BOOT_DEV_STS_FW_NIC_STAT_XPCS91_EN) +#define CPU_BOOT_DEV_STS0_FW_NIC_STAT_EXT_EN (1 << CPU_BOOT_DEV_STS_FW_NIC_STAT_EXT_EN) +#define CPU_BOOT_DEV_STS0_IS_IDLE_CHECK_EN (1 << CPU_BOOT_DEV_STS_IS_IDLE_CHECK_EN) +#define CPU_BOOT_DEV_STS0_MAP_HWMON_EN (1 << CPU_BOOT_DEV_STS_MAP_HWMON_EN) +#define CPU_BOOT_DEV_STS0_ENABLED (1 << CPU_BOOT_DEV_STS_ENABLED) +#define CPU_BOOT_DEV_STS1_ENABLED (1 << CPU_BOOT_DEV_STS_ENABLED) enum cpu_boot_status { CPU_BOOT_STATUS_NA = 0, /* Default value after reset of chip */ @@ -411,6 +452,8 @@ struct cpu_dyn_regs { enum comms_msg_type { HL_COMMS_DESC_TYPE = 0, HL_COMMS_RESET_CAUSE_TYPE = 1, + HL_COMMS_FW_CFG_SKIP_TYPE = 2, + HL_COMMS_BINNING_CONF_TYPE = 3, }; /* TODO: remove this struct after the code is updated to use message */ @@ -470,6 +513,9 @@ struct lkd_fw_comms_msg { struct { __u8 reset_cause; }; + struct { + __u8 fw_cfg_skip; /* 1 - skip, 0 - don't skip */ + }; }; }; @@ -513,8 +559,6 @@ struct lkd_fw_comms_msg { * COMMS_SKIP_BMC Perform actions required for BMC-less servers. * Do not wait for BMC response. * - * COMMS_LOW_PLL_OPP Initialize PLLs for low OPP. - * * COMMS_PREP_DESC_ELBI Same as COMMS_PREP_DESC only that the memory * space is allocated in a ELBI access only * address range. @@ -530,7 +574,6 @@ enum comms_cmd { COMMS_RST_DEV = 6, COMMS_GOTO_WFE = 7, COMMS_SKIP_BMC = 8, - COMMS_LOW_PLL_OPP = 9, COMMS_PREP_DESC_ELBI = 10, COMMS_INVLD_LAST }; diff --git a/drivers/misc/habanalabs/include/gaudi/gaudi_fw_if.h b/drivers/misc/habanalabs/include/gaudi/gaudi_fw_if.h index 34ca4fe50d91..2dba02757d37 100644 --- a/drivers/misc/habanalabs/include/gaudi/gaudi_fw_if.h +++ b/drivers/misc/habanalabs/include/gaudi/gaudi_fw_if.h @@ -8,8 +8,6 @@ #ifndef GAUDI_FW_IF_H #define GAUDI_FW_IF_H -#include - #define GAUDI_EVENT_QUEUE_MSI_IDX 8 #define GAUDI_NIC_PORT1_MSI_IDX 10 #define GAUDI_NIC_PORT3_MSI_IDX 12 @@ -78,13 +76,13 @@ struct gaudi_nic_status { __u32 high_ber_cnt; }; -struct gaudi_flops_2_data { +struct gaudi_cold_rst_data { union { struct { - __u32 spsram_init_done : 1; - __u32 reserved : 31; + u32 spsram_init_done : 1; + u32 reserved : 31; }; - __u32 data; + __le32 data; }; }; diff --git a/drivers/misc/habanalabs/include/gaudi/gaudi_reg_map.h b/drivers/misc/habanalabs/include/gaudi/gaudi_reg_map.h index b9bd5a7f71eb..92f25c2ae083 100644 --- a/drivers/misc/habanalabs/include/gaudi/gaudi_reg_map.h +++ b/drivers/misc/habanalabs/include/gaudi/gaudi_reg_map.h @@ -33,6 +33,7 @@ #define mmRDWR_TEST mmPSOC_GLOBAL_CONF_SCRATCHPAD_30 #define mmBTL_ID mmPSOC_GLOBAL_CONF_SCRATCHPAD_31 #define mmPREBOOT_PCIE_EN mmPSOC_GLOBAL_CONF_COLD_RST_FLOPS_1 +#define mmCOLD_RST_DATA mmPSOC_GLOBAL_CONF_COLD_RST_FLOPS_2 #define mmUPD_PENDING_STS mmPSOC_GLOBAL_CONF_COLD_RST_FLOPS_3 #endif /* GAUDI_REG_MAP_H_ */ -- cgit v1.2.3-70-g09d2 From 1d16a46b1a83ea44a73964fe28492656c70da831 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Tue, 5 Oct 2021 17:29:47 +0300 Subject: habanalabs: use only u32 In the kernel it is common to use u32 and not uint32_t. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi/gaudi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 70a668951ec4..654f7959c5ad 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -2616,7 +2616,7 @@ static void gaudi_init_e2e(struct hl_device *hdev) static void gaudi_init_hbm_cred(struct hl_device *hdev) { - uint32_t hbm0_wr, hbm1_wr, hbm0_rd, hbm1_rd; + u32 hbm0_wr, hbm1_wr, hbm0_rd, hbm1_rd; if (hdev->asic_prop.fw_security_enabled) return; -- cgit v1.2.3-70-g09d2 From ea6eb91c09cd4f2f92ba28ba277d2ecf63cdc237 Mon Sep 17 00:00:00 2001 From: Dani Liberman Date: Sun, 3 Oct 2021 15:57:44 +0300 Subject: habanalabs: fix race condition in multi CS completion Race condition occurs when CS fence completes and multi CS did not completed yet, while waiting for multi CS ends and returns indication to user that the CS completed. Next wait for multi CS may be triggered by previous multi CS completion without any current CS completed, causing an error. Example scenario : 1. User do multi CS wait for CSs 1 and 2 on master QID 0 2. CS 1 and 2 reached the "cs release" code. The thread of CS 1 completed both the CS and multi CS handling but the completion thread of CS 2 completed the CS but still did not executed complete_multi_cs (note that in CS completion the sequence is to first do complete all for the CS and then another complete all to signal the multi_cs) 3. User received indication that CS 1 and 2 completed (since we check the CS fence and both indicated as completed) and immediately waits on CS 3 and 4, also on master QID 0. 4. Completion thread of CS2 executed complete_multi_cs before completion of CS 3 and 4 and so will trigger the multi CS wait of CSs 3 and 4 as they wait on master QID 0. This will trigger multi CS completion although none of its current CS has been completed. Fixed by adding multi CS complete handling indication for each CS. CS will be marked to the user as completed only if its fence completed and multi CS handling is done. Signed-off-by: Dani Liberman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_submission.c | 20 +++++++++++++++++++- drivers/misc/habanalabs/common/habanalabs.h | 4 ++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index 4bc24852a283..8e4dc1441fff 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -143,6 +143,7 @@ static void hl_fence_init(struct hl_fence *fence, u64 sequence) fence->cs_sequence = sequence; fence->error = 0; fence->timestamp = ktime_set(0, 0); + fence->mcs_handling_done = false; init_completion(&fence->completion); } @@ -535,10 +536,21 @@ static void complete_multi_cs(struct hl_device *hdev, struct hl_cs *cs) mcs_compl->timestamp = ktime_to_ns(fence->timestamp); complete_all(&mcs_compl->completion); + + /* + * Setting mcs_handling_done inside the lock ensures + * at least one fence have mcs_handling_done set to + * true before wait for mcs finish. This ensures at + * least one CS will be set as completed when polling + * mcs fences. + */ + fence->mcs_handling_done = true; } spin_unlock(&mcs_compl->lock); } + /* In case CS completed without mcs completion initialized */ + fence->mcs_handling_done = true; } static inline void cs_release_sob_reset_handler(struct hl_device *hdev, @@ -2372,7 +2384,13 @@ static int hl_cs_poll_fences(struct multi_cs_data *mcs_data) mcs_data->stream_master_qid_map |= fence->stream_master_qid_map; - if (status == CS_WAIT_STATUS_BUSY) + /* + * Using mcs_handling_done to avoid possibility of mcs_data + * returns to user indicating CS completed before it finished + * all of its mcs handling, to avoid race the next time the + * user waits for mcs. + */ + if (status == CS_WAIT_STATUS_BUSY || !fence->mcs_handling_done) continue; mcs_data->completion_bitmap |= BIT(i); diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index a06135155b57..48f0e52cd5be 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -610,6 +610,9 @@ struct asic_fixed_properties { * @error: mark this fence with error * @timestamp: timestamp upon completion * @take_timestamp: timestamp shall be taken upon completion + * @mcs_handling_done: indicates that corresponding command submission has + * finished msc handling, this does not mean it was part + * of the mcs */ struct hl_fence { struct completion completion; @@ -619,6 +622,7 @@ struct hl_fence { int error; ktime_t timestamp; u8 take_timestamp; + u8 mcs_handling_done; }; /** -- cgit v1.2.3-70-g09d2 From 81f8582ec404b2ab3ec1d412e33703e014fc3121 Mon Sep 17 00:00:00 2001 From: Dani Liberman Date: Wed, 6 Oct 2021 09:36:19 +0300 Subject: habanalabs: fix NULL pointer dereference When polling fences for multi CS, it is possible that fence is no longer exists (its corresponding CS completed and the fence was deleted) but we still accessing its parameters, causing NULL pointer dereference. Fixed by checking if fence exits before accessing its parameters. Signed-off-by: Dani Liberman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_submission.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index 8e4dc1441fff..a3358cc3c877 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -2382,7 +2382,15 @@ static int hl_cs_poll_fences(struct multi_cs_data *mcs_data) break; } - mcs_data->stream_master_qid_map |= fence->stream_master_qid_map; + /* + * It is possible to get an old sequence numbers from user + * which related to already completed CSs and their fences + * already gone. In this case, no need to consider its QID for + * mcs completion. + */ + if (fence) + mcs_data->stream_master_qid_map |= + fence->stream_master_qid_map; /* * Using mcs_handling_done to avoid possibility of mcs_data @@ -2390,7 +2398,8 @@ static int hl_cs_poll_fences(struct multi_cs_data *mcs_data) * all of its mcs handling, to avoid race the next time the * user waits for mcs. */ - if (status == CS_WAIT_STATUS_BUSY || !fence->mcs_handling_done) + if (status == CS_WAIT_STATUS_BUSY || + (fence && !fence->mcs_handling_done)) continue; mcs_data->completion_bitmap |= BIT(i); -- cgit v1.2.3-70-g09d2 From a9498ee575fa116e2891d9a6ff4fc7648dd9d7c8 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Sun, 11 Apr 2021 08:26:50 +0300 Subject: habanalabs: define uAPI to export FD for DMA-BUF User process might want to share the device memory with another driver/device, and to allow it to access it over PCIe (P2P). To enable this, we utilize the dma-buf mechanism and add a dma-buf exporter support, so the other driver can import the device memory and access it. The device memory is allocated using our existing allocation uAPI, where the user will get a handle that represents the allocation. The user will then need to call the new uAPI (HL_MEM_OP_EXPORT_DMABUF_FD) and give the handle as a parameter. The driver will return a FD that represents the DMA-BUF object that was created to match that allocation. Signed-off-by: Oded Gabbay Reviewed-by: Tomer Tayar Reviewed-by: Greg Kroah-Hartman Acked-by: Daniel Vetter --- include/uapi/misc/habanalabs.h | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index fe73630e1a05..00b309590499 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -960,6 +960,10 @@ union hl_wait_cs_args { #define HL_MEM_OP_UNMAP 3 /* Opcode to map a hw block */ #define HL_MEM_OP_MAP_BLOCK 4 +/* Opcode to create DMA-BUF object for an existing device memory allocation + * and to export an FD of that DMA-BUF back to the caller + */ +#define HL_MEM_OP_EXPORT_DMABUF_FD 5 /* Memory flags */ #define HL_MEM_CONTIGUOUS 0x1 @@ -1031,11 +1035,26 @@ struct hl_mem_in { /* Virtual address returned from HL_MEM_OP_MAP */ __u64 device_virt_addr; } unmap; + + /* HL_MEM_OP_EXPORT_DMABUF_FD */ + struct { + /* Handle returned from HL_MEM_OP_ALLOC. In Gaudi, + * where we don't have MMU for the device memory, the + * driver expects a physical address (instead of + * a handle) in the device memory space. + */ + __u64 handle; + /* Size of memory allocation. Relevant only for GAUDI */ + __u64 mem_size; + } export_dmabuf_fd; }; /* HL_MEM_OP_* */ __u32 op; - /* HL_MEM_* flags */ + /* HL_MEM_* flags. + * For the HL_MEM_OP_EXPORT_DMABUF_FD opcode, this field holds the + * DMA-BUF file/FD flags. + */ __u32 flags; /* Context ID - Currently not in use */ __u32 ctx_id; @@ -1072,6 +1091,13 @@ struct hl_mem_out { __u32 pad; }; + + /* Returned in HL_MEM_OP_EXPORT_DMABUF_FD. Represents the + * DMA-BUF object that was created to describe a memory + * allocation on the device's memory space. The FD should be + * passed to the importer driver + */ + __s32 fd; }; }; -- cgit v1.2.3-70-g09d2 From db1a8dd916aac986871f6b873a3aefad906f383a Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Tue, 15 Jun 2021 10:55:43 +0300 Subject: habanalabs: add support for dma-buf exporter Implement the calls to the dma-buf kernel api to create a dma-buf object backed by FD. We block the option to mmap the DMA-BUF object because we don't support DIRECT_IO and implicit P2P. We only implement support for explicit P2P through importing the FD of the DMA-BUF. In the export phase, we provide to the DMA-BUF object an array of pages that represent the device's memory area. During the map callback, we convert the array of pages into an SGT. We split/merge the pages according to the dma max segment size of the importer. To get the DMA address of the PCI bar, we use the dma_map_resources() kernel API, because our device memory is not backed by page struct and this API doesn't need page struct to map the physical address to a DMA address. We set the orig_nents member of the SGT to be 0, to indicate to other drivers that we don't support CPU mappings. Note that in Habanalabs's ASICs, the device memory is pinned and immutable. Therefore, there is no need for dynamic mappings and pinning callbacks. Also note that in GAUDI we don't have an MMU towards the device memory and the user works on physical addresses. Therefore, the user doesn't pass through the kernel driver to allocate memory there. As a result, only for GAUDI we receive from the user a device memory physical address (instead of a handle) and a size. We check the p2p distance using pci_p2pdma_distance_many() and refusing to map dmabuf in case the distance doesn't allow p2p. Signed-off-by: Tomer Tayar Reviewed-by: Oded Gabbay Reviewed-by: Gal Pressman Reviewed-by: Greg Kroah-Hartman Acked-by: Daniel Vetter Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/Kconfig | 1 + drivers/misc/habanalabs/common/habanalabs.h | 22 ++ drivers/misc/habanalabs/common/memory.c | 513 +++++++++++++++++++++++++++- drivers/misc/habanalabs/gaudi/gaudi.c | 1 + drivers/misc/habanalabs/goya/goya.c | 1 + 5 files changed, 535 insertions(+), 3 deletions(-) diff --git a/drivers/misc/habanalabs/Kconfig b/drivers/misc/habanalabs/Kconfig index 293d79811372..c82d2e7b2035 100644 --- a/drivers/misc/habanalabs/Kconfig +++ b/drivers/misc/habanalabs/Kconfig @@ -8,6 +8,7 @@ config HABANA_AI depends on PCI && HAS_IOMEM select GENERIC_ALLOCATOR select HWMON + select DMA_SHARED_BUFFER help Enables PCIe card driver for Habana's AI Processors (AIP) that are designed to accelerate Deep Learning inference and training workloads. diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 48f0e52cd5be..eb1ccb5d3ce3 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -26,6 +26,7 @@ #include #include #include +#include #define HL_NAME "habanalabs" @@ -1366,6 +1367,23 @@ struct hl_cs_counters_atomic { atomic64_t validation_drop_cnt; }; +/** + * struct hl_dmabuf_priv - a dma-buf private object. + * @dmabuf: pointer to dma-buf object. + * @ctx: pointer to the dma-buf owner's context. + * @phys_pg_pack: pointer to physical page pack if the dma-buf was exported for + * memory allocation handle. + * @device_address: physical address of the device's memory. Relevant only + * if phys_pg_pack is NULL (dma-buf was exported from address). + * The total size can be taken from the dmabuf object. + */ +struct hl_dmabuf_priv { + struct dma_buf *dmabuf; + struct hl_ctx *ctx; + struct hl_vm_phys_pg_pack *phys_pg_pack; + uint64_t device_address; +}; + /** * struct hl_ctx - user/kernel context. * @mem_hash: holds mapping from virtual address to virtual memory area @@ -1676,6 +1694,7 @@ struct hl_vm_hw_block_list_node { * @npages: num physical pages in the pack. * @total_size: total size of all the pages in this list. * @mapping_cnt: number of shared mappings. + * @exporting_cnt: number of dma-buf exporting. * @asid: the context related to this list. * @page_size: size of each page in the pack. * @flags: HL_MEM_* flags related to this list. @@ -1690,6 +1709,7 @@ struct hl_vm_phys_pg_pack { u64 npages; u64 total_size; atomic_t mapping_cnt; + u32 exporting_cnt; u32 asid; u32 page_size; u32 flags; @@ -2410,6 +2430,7 @@ struct multi_cs_data { * the error will be ignored by the driver during * device initialization. Mainly used to debug and * workaround firmware bugs + * @dram_pci_bar_start: start bus address of PCIe bar towards DRAM. * @last_successful_open_jif: timestamp (jiffies) of the last successful * device open. * @last_open_session_duration_jif: duration (jiffies) of the last device open @@ -2559,6 +2580,7 @@ struct hl_device { u64 max_power; u64 clock_gating_mask; u64 boot_error_status_mask; + u64 dram_pci_bar_start; u64 last_successful_open_jif; u64 last_open_session_duration_jif; u64 open_counter; diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c index 33986933aa9e..504973330e2e 100644 --- a/drivers/misc/habanalabs/common/memory.c +++ b/drivers/misc/habanalabs/common/memory.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2016-2019 HabanaLabs, Ltd. + * Copyright 2016-2021 HabanaLabs, Ltd. * All Rights Reserved. */ @@ -11,6 +11,7 @@ #include #include +#include #define HL_MMU_DEBUG 0 @@ -347,6 +348,12 @@ static int free_device_memory(struct hl_ctx *ctx, struct hl_mem_in *args) return -EINVAL; } + if (phys_pg_pack->exporting_cnt) { + dev_dbg(hdev->dev, "handle %u is exported, cannot free\n", handle); + spin_unlock(&vm->idr_lock); + return -EINVAL; + } + /* * must remove from idr before the freeing of the physical * pages as the refcount of the pool is also the trigger of the @@ -1487,13 +1494,487 @@ int hl_hw_block_mmap(struct hl_fpriv *hpriv, struct vm_area_struct *vma) return 0; } +static int set_dma_sg(struct scatterlist *sg, u64 bar_address, u64 chunk_size, + struct device *dev, enum dma_data_direction dir) +{ + dma_addr_t addr; + int rc; + + addr = dma_map_resource(dev, bar_address, chunk_size, dir, + DMA_ATTR_SKIP_CPU_SYNC); + rc = dma_mapping_error(dev, addr); + if (rc) + return rc; + + sg_set_page(sg, NULL, chunk_size, 0); + sg_dma_address(sg) = addr; + sg_dma_len(sg) = chunk_size; + + return 0; +} + +static struct sg_table *alloc_sgt_from_device_pages(struct hl_device *hdev, u64 *pages, u64 npages, + u64 page_size, struct device *dev, + enum dma_data_direction dir) +{ + u64 chunk_size, bar_address, dma_max_seg_size; + struct asic_fixed_properties *prop; + int rc, i, j, nents, cur_page; + struct scatterlist *sg; + struct sg_table *sgt; + + prop = &hdev->asic_prop; + + dma_max_seg_size = dma_get_max_seg_size(dev); + + /* We would like to align the max segment size to PAGE_SIZE, so the + * SGL will contain aligned addresses that can be easily mapped to + * an MMU + */ + dma_max_seg_size = ALIGN_DOWN(dma_max_seg_size, PAGE_SIZE); + if (dma_max_seg_size < PAGE_SIZE) { + dev_err_ratelimited(hdev->dev, + "dma_max_seg_size %llu can't be smaller than PAGE_SIZE\n", + dma_max_seg_size); + return ERR_PTR(-EINVAL); + } + + sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); + if (!sgt) + return ERR_PTR(-ENOMEM); + + /* If the size of each page is larger than the dma max segment size, + * then we can't combine pages and the number of entries in the SGL + * will just be the + * * + */ + if (page_size > dma_max_seg_size) + nents = npages * DIV_ROUND_UP_ULL(page_size, dma_max_seg_size); + else + /* Get number of non-contiguous chunks */ + for (i = 1, nents = 1, chunk_size = page_size ; i < npages ; i++) { + if (pages[i - 1] + page_size != pages[i] || + chunk_size + page_size > dma_max_seg_size) { + nents++; + chunk_size = page_size; + continue; + } + + chunk_size += page_size; + } + + rc = sg_alloc_table(sgt, nents, GFP_KERNEL | __GFP_ZERO); + if (rc) + goto error_free; + + cur_page = 0; + + if (page_size > dma_max_seg_size) { + u64 size_left, cur_device_address = 0; + + size_left = page_size; + + /* Need to split each page into the number of chunks of + * dma_max_seg_size + */ + for_each_sgtable_dma_sg(sgt, sg, i) { + if (size_left == page_size) + cur_device_address = + pages[cur_page] - prop->dram_base_address; + else + cur_device_address += dma_max_seg_size; + + chunk_size = min(size_left, dma_max_seg_size); + + bar_address = hdev->dram_pci_bar_start + cur_device_address; + + rc = set_dma_sg(sg, bar_address, chunk_size, dev, dir); + if (rc) + goto error_unmap; + + if (size_left > dma_max_seg_size) { + size_left -= dma_max_seg_size; + } else { + cur_page++; + size_left = page_size; + } + } + } else { + /* Merge pages and put them into the scatterlist */ + for_each_sgtable_dma_sg(sgt, sg, i) { + chunk_size = page_size; + for (j = cur_page + 1 ; j < npages ; j++) { + if (pages[j - 1] + page_size != pages[j] || + chunk_size + page_size > dma_max_seg_size) + break; + + chunk_size += page_size; + } + + bar_address = hdev->dram_pci_bar_start + + (pages[cur_page] - prop->dram_base_address); + + rc = set_dma_sg(sg, bar_address, chunk_size, dev, dir); + if (rc) + goto error_unmap; + + cur_page = j; + } + } + + /* Because we are not going to include a CPU list we want to have some + * chance that other users will detect this by setting the orig_nents + * to 0 and using only nents (length of DMA list) when going over the + * sgl + */ + sgt->orig_nents = 0; + + return sgt; + +error_unmap: + for_each_sgtable_dma_sg(sgt, sg, i) { + if (!sg_dma_len(sg)) + continue; + + dma_unmap_resource(dev, sg_dma_address(sg), + sg_dma_len(sg), dir, + DMA_ATTR_SKIP_CPU_SYNC); + } + + sg_free_table(sgt); + +error_free: + kfree(sgt); + return ERR_PTR(rc); +} + +static int hl_dmabuf_attach(struct dma_buf *dmabuf, + struct dma_buf_attachment *attachment) +{ + struct hl_dmabuf_priv *hl_dmabuf; + struct hl_device *hdev; + int rc; + + hl_dmabuf = dmabuf->priv; + hdev = hl_dmabuf->ctx->hdev; + + rc = pci_p2pdma_distance_many(hdev->pdev, &attachment->dev, 1, true); + + if (rc < 0) + attachment->peer2peer = false; + return 0; +} + +static struct sg_table *hl_map_dmabuf(struct dma_buf_attachment *attachment, + enum dma_data_direction dir) +{ + struct dma_buf *dma_buf = attachment->dmabuf; + struct hl_vm_phys_pg_pack *phys_pg_pack; + struct hl_dmabuf_priv *hl_dmabuf; + struct hl_device *hdev; + struct sg_table *sgt; + + hl_dmabuf = dma_buf->priv; + hdev = hl_dmabuf->ctx->hdev; + phys_pg_pack = hl_dmabuf->phys_pg_pack; + + if (!attachment->peer2peer) { + dev_dbg(hdev->dev, "Failed to map dmabuf because p2p is disabled\n"); + return ERR_PTR(-EPERM); + } + + if (phys_pg_pack) + sgt = alloc_sgt_from_device_pages(hdev, + phys_pg_pack->pages, + phys_pg_pack->npages, + phys_pg_pack->page_size, + attachment->dev, + dir); + else + sgt = alloc_sgt_from_device_pages(hdev, + &hl_dmabuf->device_address, + 1, + hl_dmabuf->dmabuf->size, + attachment->dev, + dir); + + if (IS_ERR(sgt)) + dev_err(hdev->dev, "failed (%ld) to initialize sgt for dmabuf\n", PTR_ERR(sgt)); + + return sgt; +} + +static void hl_unmap_dmabuf(struct dma_buf_attachment *attachment, + struct sg_table *sgt, + enum dma_data_direction dir) +{ + struct scatterlist *sg; + int i; + + /* The memory behind the dma-buf has *always* resided on the device itself, i.e. it lives + * only in the 'device' domain (after all, it maps a PCI bar address which points to the + * device memory). + * + * Therefore, it was never in the 'CPU' domain and hence, there is no need to perform + * a sync of the memory to the CPU's cache, as it never resided inside that cache. + */ + for_each_sgtable_dma_sg(sgt, sg, i) + dma_unmap_resource(attachment->dev, sg_dma_address(sg), + sg_dma_len(sg), dir, + DMA_ATTR_SKIP_CPU_SYNC); + + /* Need to restore orig_nents because sg_free_table use that field */ + sgt->orig_nents = sgt->nents; + sg_free_table(sgt); + kfree(sgt); +} + +static void hl_release_dmabuf(struct dma_buf *dmabuf) +{ + struct hl_dmabuf_priv *hl_dmabuf = dmabuf->priv; + struct hl_ctx *ctx = hl_dmabuf->ctx; + struct hl_device *hdev = ctx->hdev; + struct hl_vm *vm = &hdev->vm; + + if (hl_dmabuf->phys_pg_pack) { + spin_lock(&vm->idr_lock); + hl_dmabuf->phys_pg_pack->exporting_cnt--; + spin_unlock(&vm->idr_lock); + } + + hl_ctx_put(hl_dmabuf->ctx); + + kfree(hl_dmabuf); +} + +static const struct dma_buf_ops habanalabs_dmabuf_ops = { + .attach = hl_dmabuf_attach, + .map_dma_buf = hl_map_dmabuf, + .unmap_dma_buf = hl_unmap_dmabuf, + .release = hl_release_dmabuf, +}; + +static int export_dmabuf_common(struct hl_ctx *ctx, + struct hl_dmabuf_priv *hl_dmabuf, + u64 total_size, int flags, int *dmabuf_fd) +{ + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + struct hl_device *hdev = ctx->hdev; + int rc, fd; + + exp_info.ops = &habanalabs_dmabuf_ops; + exp_info.size = total_size; + exp_info.flags = flags; + exp_info.priv = hl_dmabuf; + + hl_dmabuf->dmabuf = dma_buf_export(&exp_info); + if (IS_ERR(hl_dmabuf->dmabuf)) { + dev_err(hdev->dev, "failed to export dma-buf\n"); + return PTR_ERR(hl_dmabuf->dmabuf); + } + + fd = dma_buf_fd(hl_dmabuf->dmabuf, flags); + if (fd < 0) { + dev_err(hdev->dev, "failed to get a file descriptor for a dma-buf\n"); + rc = fd; + goto err_dma_buf_put; + } + + hl_dmabuf->ctx = ctx; + hl_ctx_get(hdev, hl_dmabuf->ctx); + + *dmabuf_fd = fd; + + return 0; + +err_dma_buf_put: + dma_buf_put(hl_dmabuf->dmabuf); + return rc; +} + +/** + * export_dmabuf_from_addr() - export a dma-buf object for the given memory + * address and size. + * @ctx: pointer to the context structure. + * @device_addr: device memory physical address. + * @size: size of device memory. + * @flags: DMA-BUF file/FD flags. + * @dmabuf_fd: pointer to result FD that represents the dma-buf object. + * + * Create and export a dma-buf object for an existing memory allocation inside + * the device memory, and return a FD which is associated with the dma-buf + * object. + * + * Return: 0 on success, non-zero for failure. + */ +static int export_dmabuf_from_addr(struct hl_ctx *ctx, u64 device_addr, + u64 size, int flags, int *dmabuf_fd) +{ + struct hl_dmabuf_priv *hl_dmabuf; + struct hl_device *hdev = ctx->hdev; + struct asic_fixed_properties *prop; + u64 bar_address; + int rc; + + prop = &hdev->asic_prop; + + if (!IS_ALIGNED(device_addr, PAGE_SIZE)) { + dev_dbg(hdev->dev, + "exported device memory address 0x%llx should be aligned to 0x%lx\n", + device_addr, PAGE_SIZE); + return -EINVAL; + } + + if (size < PAGE_SIZE) { + dev_dbg(hdev->dev, + "exported device memory size %llu should be equal to or greater than %lu\n", + size, PAGE_SIZE); + return -EINVAL; + } + + if (device_addr < prop->dram_user_base_address || + device_addr + size > prop->dram_end_address || + device_addr + size < device_addr) { + dev_dbg(hdev->dev, + "DRAM memory range 0x%llx (+0x%llx) is outside of DRAM boundaries\n", + device_addr, size); + return -EINVAL; + } + + bar_address = hdev->dram_pci_bar_start + + (device_addr - prop->dram_base_address); + + if (bar_address + size > + hdev->dram_pci_bar_start + prop->dram_pci_bar_size || + bar_address + size < bar_address) { + dev_dbg(hdev->dev, + "DRAM memory range 0x%llx (+0x%llx) is outside of PCI BAR boundaries\n", + device_addr, size); + return -EINVAL; + } + + hl_dmabuf = kzalloc(sizeof(*hl_dmabuf), GFP_KERNEL); + if (!hl_dmabuf) + return -ENOMEM; + + hl_dmabuf->device_address = device_addr; + + rc = export_dmabuf_common(ctx, hl_dmabuf, size, flags, dmabuf_fd); + if (rc) + goto err_free_dmabuf_wrapper; + + return 0; + +err_free_dmabuf_wrapper: + kfree(hl_dmabuf); + return rc; +} + +/** + * export_dmabuf_from_handle() - export a dma-buf object for the given memory + * handle. + * @ctx: pointer to the context structure. + * @handle: device memory allocation handle. + * @flags: DMA-BUF file/FD flags. + * @dmabuf_fd: pointer to result FD that represents the dma-buf object. + * + * Create and export a dma-buf object for an existing memory allocation inside + * the device memory, and return a FD which is associated with the dma-buf + * object. + * + * Return: 0 on success, non-zero for failure. + */ +static int export_dmabuf_from_handle(struct hl_ctx *ctx, u64 handle, int flags, + int *dmabuf_fd) +{ + struct hl_vm_phys_pg_pack *phys_pg_pack; + struct hl_dmabuf_priv *hl_dmabuf; + struct hl_device *hdev = ctx->hdev; + struct asic_fixed_properties *prop; + struct hl_vm *vm = &hdev->vm; + u64 bar_address; + int rc, i; + + prop = &hdev->asic_prop; + + if (upper_32_bits(handle)) { + dev_dbg(hdev->dev, "no match for handle 0x%llx\n", handle); + return -EINVAL; + } + + spin_lock(&vm->idr_lock); + + phys_pg_pack = idr_find(&vm->phys_pg_pack_handles, (u32) handle); + if (!phys_pg_pack) { + spin_unlock(&vm->idr_lock); + dev_dbg(hdev->dev, "no match for handle 0x%x\n", (u32) handle); + return -EINVAL; + } + + /* increment now to avoid freeing device memory while exporting */ + phys_pg_pack->exporting_cnt++; + + spin_unlock(&vm->idr_lock); + + if (phys_pg_pack->vm_type != VM_TYPE_PHYS_PACK) { + dev_dbg(hdev->dev, "handle 0x%llx does not represent DRAM memory\n", handle); + rc = -EINVAL; + goto err_dec_exporting_cnt; + } + + for (i = 0 ; i < phys_pg_pack->npages ; i++) { + + bar_address = hdev->dram_pci_bar_start + + (phys_pg_pack->pages[i] - + prop->dram_base_address); + + if (bar_address + phys_pg_pack->page_size > + hdev->dram_pci_bar_start + prop->dram_pci_bar_size || + bar_address + phys_pg_pack->page_size < bar_address) { + + dev_dbg(hdev->dev, + "DRAM memory range 0x%llx (+0x%x) is outside of PCI BAR boundaries\n", + phys_pg_pack->pages[i], + phys_pg_pack->page_size); + + rc = -EINVAL; + goto err_dec_exporting_cnt; + } + } + + hl_dmabuf = kzalloc(sizeof(*hl_dmabuf), GFP_KERNEL); + if (!hl_dmabuf) { + rc = -ENOMEM; + goto err_dec_exporting_cnt; + } + + hl_dmabuf->phys_pg_pack = phys_pg_pack; + + rc = export_dmabuf_common(ctx, hl_dmabuf, phys_pg_pack->total_size, + flags, dmabuf_fd); + if (rc) + goto err_free_dmabuf_wrapper; + + return 0; + +err_free_dmabuf_wrapper: + kfree(hl_dmabuf); + +err_dec_exporting_cnt: + spin_lock(&vm->idr_lock); + phys_pg_pack->exporting_cnt--; + spin_unlock(&vm->idr_lock); + + return rc; +} + static int mem_ioctl_no_mmu(struct hl_fpriv *hpriv, union hl_mem_args *args) { struct hl_device *hdev = hpriv->hdev; struct hl_ctx *ctx = hpriv->ctx; u64 block_handle, device_addr = 0; u32 handle = 0, block_size; - int rc; + int rc, dmabuf_fd = -EBADF; switch (args->in.op) { case HL_MEM_OP_ALLOC: @@ -1542,6 +2023,16 @@ static int mem_ioctl_no_mmu(struct hl_fpriv *hpriv, union hl_mem_args *args) args->out.block_size = block_size; break; + case HL_MEM_OP_EXPORT_DMABUF_FD: + rc = export_dmabuf_from_addr(ctx, + args->in.export_dmabuf_fd.handle, + args->in.export_dmabuf_fd.mem_size, + args->in.flags, + &dmabuf_fd); + memset(args, 0, sizeof(*args)); + args->out.fd = dmabuf_fd; + break; + default: dev_err(hdev->dev, "Unknown opcode for memory IOCTL\n"); rc = -ENOTTY; @@ -1560,7 +2051,7 @@ int hl_mem_ioctl(struct hl_fpriv *hpriv, void *data) struct hl_ctx *ctx = hpriv->ctx; u64 block_handle, device_addr = 0; u32 handle = 0, block_size; - int rc; + int rc, dmabuf_fd = -EBADF; if (!hl_device_operational(hdev, &status)) { dev_warn_ratelimited(hdev->dev, @@ -1651,6 +2142,22 @@ int hl_mem_ioctl(struct hl_fpriv *hpriv, void *data) args->out.block_size = block_size; break; + case HL_MEM_OP_EXPORT_DMABUF_FD: + if (hdev->asic_prop.dram_supports_virtual_memory) + rc = export_dmabuf_from_handle(ctx, + args->in.export_dmabuf_fd.handle, + args->in.flags, + &dmabuf_fd); + else + rc = export_dmabuf_from_addr(ctx, + args->in.export_dmabuf_fd.handle, + args->in.export_dmabuf_fd.mem_size, + args->in.flags, + &dmabuf_fd); + memset(args, 0, sizeof(*args)); + args->out.fd = dmabuf_fd; + break; + default: dev_err(hdev->dev, "Unknown opcode for memory IOCTL\n"); rc = -ENOTTY; diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 654f7959c5ad..7a5bcc81f653 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -795,6 +795,7 @@ static int gaudi_early_init(struct hl_device *hdev) } prop->dram_pci_bar_size = pci_resource_len(pdev, HBM_BAR_ID); + hdev->dram_pci_bar_start = pci_resource_start(pdev, HBM_BAR_ID); /* If FW security is enabled at this point it means no access to ELBI */ if (hdev->asic_prop.fw_security_enabled) { diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 78cf35879680..ad00789348d9 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -622,6 +622,7 @@ static int goya_early_init(struct hl_device *hdev) } prop->dram_pci_bar_size = pci_resource_len(pdev, DDR_BAR_ID); + hdev->dram_pci_bar_start = pci_resource_start(pdev, DDR_BAR_ID); /* If FW security is enabled at this point it means no access to ELBI */ if (hdev->asic_prop.fw_security_enabled) { -- cgit v1.2.3-70-g09d2 From f6fb34390cd047543ff00b34b8ad910bf76c8eb3 Mon Sep 17 00:00:00 2001 From: Vegard Nossum Date: Mon, 11 Oct 2021 17:14:43 +0200 Subject: habanalabs: select CRC32 Fix the following build/link error by adding a dependency on the CRC32 routines: ld: drivers/misc/habanalabs/common/firmware_if.o: in function `hl_fw_dynamic_request_descriptor': firmware_if.c:(.text.unlikely+0xc89): undefined reference to `crc32_le' Fixes: 8a43c83fec12 ("habanalabs: load boot fit to device") Signed-off-by: Vegard Nossum Acked-by: Arnd Bergmann Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/habanalabs/Kconfig b/drivers/misc/habanalabs/Kconfig index c82d2e7b2035..861c81006c6d 100644 --- a/drivers/misc/habanalabs/Kconfig +++ b/drivers/misc/habanalabs/Kconfig @@ -9,6 +9,7 @@ config HABANA_AI select GENERIC_ALLOCATOR select HWMON select DMA_SHARED_BUFFER + select CRC32 help Enables PCIe card driver for Habana's AI Processors (AIP) that are designed to accelerate Deep Learning inference and training workloads. -- cgit v1.2.3-70-g09d2 From 024b7b1d6dcd1960e9b669e981968ca4b6bf80cb Mon Sep 17 00:00:00 2001 From: Rajaravi Krishna Katta Date: Fri, 3 Sep 2021 08:01:30 +0300 Subject: habanalabs: Unify frequency set/get functionality Make the frequency set/get functionality common to all ASICs. This makes more code reusable when adding support for newer ASICs. Signed-off-by: Rajaravi Krishna Katta Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/Makefile | 2 +- drivers/misc/habanalabs/common/habanalabs.h | 10 +++ drivers/misc/habanalabs/common/hwmgr.c | 117 +++++++++++++++++++++++++++ drivers/misc/habanalabs/gaudi/Makefile | 2 +- drivers/misc/habanalabs/gaudi/gaudi.c | 11 +-- drivers/misc/habanalabs/gaudi/gaudiP.h | 4 - drivers/misc/habanalabs/gaudi/gaudi_hwmgr.c | 121 ---------------------------- drivers/misc/habanalabs/goya/goya.c | 4 +- drivers/misc/habanalabs/goya/goyaP.h | 1 - drivers/misc/habanalabs/goya/goya_hwmgr.c | 31 ------- 10 files changed, 138 insertions(+), 165 deletions(-) create mode 100644 drivers/misc/habanalabs/common/hwmgr.c delete mode 100644 drivers/misc/habanalabs/gaudi/gaudi_hwmgr.c diff --git a/drivers/misc/habanalabs/common/Makefile b/drivers/misc/habanalabs/common/Makefile index 6ebe3c7001ff..82c3824cad00 100644 --- a/drivers/misc/habanalabs/common/Makefile +++ b/drivers/misc/habanalabs/common/Makefile @@ -11,4 +11,4 @@ HL_COMMON_FILES := common/habanalabs_drv.o common/device.o common/context.o \ common/command_buffer.o common/hw_queue.o common/irq.o \ common/sysfs.o common/hwmon.o common/memory.o \ common/command_submission.o common/firmware_if.o \ - common/state_dump.o + common/state_dump.o common/hwmgr.o diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index eb1ccb5d3ce3..e7b3b121f24d 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -456,6 +456,9 @@ struct hl_hints_range { * for hints validity check. * device_dma_offset_for_host_access: the offset to add to host DMA addresses * to enable the device to access them. + * @max_freq_value: current max clk frequency. + * @clk_pll_index: clock PLL index that specify which PLL determines the clock + * we display to the user * @mmu_pgt_size: MMU page tables total size. * @mmu_pte_size: PTE size in MMU page tables. * @mmu_hop_table_size: MMU hop table size. @@ -552,6 +555,8 @@ struct asic_fixed_properties { u64 cb_va_end_addr; u64 dram_hints_align_mask; u64 device_dma_offset_for_host_access; + u64 max_freq_value; + u32 clk_pll_index; u32 mmu_pgt_size; u32 mmu_pte_size; u32 mmu_hop_table_size; @@ -3006,6 +3011,11 @@ int hl_set_power(struct hl_device *hdev, int sensor_index, u32 attr, long value); int hl_get_power(struct hl_device *hdev, int sensor_index, u32 attr, long *value); +int hl_get_clk_rate(struct hl_device *hdev, + u32 *cur_clk, u32 *max_clk); +void hl_set_pll_profile(struct hl_device *hdev, enum hl_pll_frequency freq); +void hl_add_device_attr(struct hl_device *hdev, + struct attribute_group *dev_attr_grp); void hw_sob_get(struct hl_hw_sob *hw_sob); void hw_sob_put(struct hl_hw_sob *hw_sob); void hl_encaps_handle_do_release(struct kref *ref); diff --git a/drivers/misc/habanalabs/common/hwmgr.c b/drivers/misc/habanalabs/common/hwmgr.c new file mode 100644 index 000000000000..5451019f143f --- /dev/null +++ b/drivers/misc/habanalabs/common/hwmgr.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright 2019-2021 HabanaLabs, Ltd. + * All Rights Reserved. + */ + +#include "habanalabs.h" + +void hl_set_pll_profile(struct hl_device *hdev, enum hl_pll_frequency freq) +{ + hl_set_frequency(hdev, hdev->asic_prop.clk_pll_index, + hdev->asic_prop.max_freq_value); +} + +int hl_get_clk_rate(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk) +{ + long value; + + if (!hl_device_operational(hdev, NULL)) + return -ENODEV; + + value = hl_get_frequency(hdev, hdev->asic_prop.clk_pll_index, false); + + if (value < 0) { + dev_err(hdev->dev, "Failed to retrieve device max clock %ld\n", + value); + return value; + } + + *max_clk = (value / 1000 / 1000); + + value = hl_get_frequency(hdev, hdev->asic_prop.clk_pll_index, true); + + if (value < 0) { + dev_err(hdev->dev, + "Failed to retrieve device current clock %ld\n", + value); + return value; + } + + *cur_clk = (value / 1000 / 1000); + + return 0; +} + +static ssize_t clk_max_freq_mhz_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + long value; + + if (!hl_device_operational(hdev, NULL)) + return -ENODEV; + + value = hl_get_frequency(hdev, hdev->asic_prop.clk_pll_index, false); + + hdev->asic_prop.max_freq_value = value; + + return sprintf(buf, "%lu\n", (value / 1000 / 1000)); +} + +static ssize_t clk_max_freq_mhz_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + int rc; + u64 value; + + if (!hl_device_operational(hdev, NULL)) { + count = -ENODEV; + goto fail; + } + + rc = kstrtoull(buf, 0, &value); + if (rc) { + count = -EINVAL; + goto fail; + } + + hdev->asic_prop.max_freq_value = value * 1000 * 1000; + + hl_set_frequency(hdev, hdev->asic_prop.clk_pll_index, + hdev->asic_prop.max_freq_value); + +fail: + return count; +} + +static ssize_t clk_cur_freq_mhz_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + long value; + + if (!hl_device_operational(hdev, NULL)) + return -ENODEV; + + value = hl_get_frequency(hdev, hdev->asic_prop.clk_pll_index, true); + + return sprintf(buf, "%lu\n", (value / 1000 / 1000)); +} + +static DEVICE_ATTR_RW(clk_max_freq_mhz); +static DEVICE_ATTR_RO(clk_cur_freq_mhz); + +static struct attribute *hl_dev_attrs[] = { + &dev_attr_clk_max_freq_mhz.attr, + &dev_attr_clk_cur_freq_mhz.attr, + NULL, +}; + +void hl_add_device_attr(struct hl_device *hdev, + struct attribute_group *dev_attr_grp) +{ + dev_attr_grp->attrs = hl_dev_attrs; +} diff --git a/drivers/misc/habanalabs/gaudi/Makefile b/drivers/misc/habanalabs/gaudi/Makefile index c9f4703cff24..10577c33a816 100644 --- a/drivers/misc/habanalabs/gaudi/Makefile +++ b/drivers/misc/habanalabs/gaudi/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -HL_GAUDI_FILES := gaudi/gaudi.o gaudi/gaudi_hwmgr.o gaudi/gaudi_security.o \ +HL_GAUDI_FILES := gaudi/gaudi.o gaudi/gaudi_security.o \ gaudi/gaudi_coresight.o diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 7a5bcc81f653..825737dfe381 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -661,6 +661,9 @@ static int gaudi_set_fixed_properties(struct hl_device *hdev) prop->server_type = HL_SERVER_TYPE_UNKNOWN; + prop->clk_pll_index = HL_GAUDI_MME_PLL; + prop->max_freq_value = GAUDI_MAX_CLK_FREQ; + return 0; } @@ -1838,8 +1841,6 @@ static int gaudi_sw_init(struct hl_device *hdev) gaudi->cpucp_info_get = gaudi_cpucp_info_get; - gaudi->max_freq_value = GAUDI_MAX_CLK_FREQ; - hdev->asic_specific = gaudi; /* Create DMA pool for small allocations */ @@ -9444,9 +9445,9 @@ static const struct hl_asic_funcs gaudi_funcs = { .debugfs_read64 = gaudi_debugfs_read64, .debugfs_write64 = gaudi_debugfs_write64, .debugfs_read_dma = gaudi_debugfs_read_dma, - .add_device_attr = gaudi_add_device_attr, + .add_device_attr = hl_add_device_attr, .handle_eqe = gaudi_handle_eqe, - .set_pll_profile = gaudi_set_pll_profile, + .set_pll_profile = hl_set_pll_profile, .get_events_stat = gaudi_get_events_stat, .read_pte = gaudi_read_pte, .write_pte = gaudi_write_pte, @@ -9470,7 +9471,7 @@ static const struct hl_asic_funcs gaudi_funcs = { .halt_coresight = gaudi_halt_coresight, .ctx_init = gaudi_ctx_init, .ctx_fini = gaudi_ctx_fini, - .get_clk_rate = gaudi_get_clk_rate, + .get_clk_rate = hl_get_clk_rate, .get_queue_id_for_cq = gaudi_get_queue_id_for_cq, .load_firmware_to_device = gaudi_load_firmware_to_device, .load_boot_fit_to_device = gaudi_load_boot_fit_to_device, diff --git a/drivers/misc/habanalabs/gaudi/gaudiP.h b/drivers/misc/habanalabs/gaudi/gaudiP.h index bbbf1c343e75..f325e36a71e6 100644 --- a/drivers/misc/habanalabs/gaudi/gaudiP.h +++ b/drivers/misc/habanalabs/gaudi/gaudiP.h @@ -319,7 +319,6 @@ struct gaudi_internal_qman_info { * the actual number of internal queues because they are not in * consecutive order. * @hbm_bar_cur_addr: current address of HBM PCI bar. - * @max_freq_value: current max clk frequency. * @events: array that holds all event id's * @events_stat: array that holds histogram of all received events. * @events_stat_aggregate: same as events_stat but doesn't get cleared on reset @@ -345,7 +344,6 @@ struct gaudi_device { struct gaudi_collective_properties collective_props; u64 hbm_bar_cur_addr; - u64 max_freq_value; u32 events[GAUDI_EVENT_SIZE]; u32 events_stat[GAUDI_EVENT_SIZE]; @@ -359,10 +357,8 @@ void gaudi_init_security(struct hl_device *hdev); void gaudi_ack_protection_bits_errors(struct hl_device *hdev); void gaudi_add_device_attr(struct hl_device *hdev, struct attribute_group *dev_attr_grp); -void gaudi_set_pll_profile(struct hl_device *hdev, enum hl_pll_frequency freq); int gaudi_debug_coresight(struct hl_device *hdev, void *data); void gaudi_halt_coresight(struct hl_device *hdev); -int gaudi_get_clk_rate(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk); void gaudi_mmu_prepare_reg(struct hl_device *hdev, u64 reg, u32 asid); #endif /* GAUDIP_H_ */ diff --git a/drivers/misc/habanalabs/gaudi/gaudi_hwmgr.c b/drivers/misc/habanalabs/gaudi/gaudi_hwmgr.c deleted file mode 100644 index 9b60eadd4c35..000000000000 --- a/drivers/misc/habanalabs/gaudi/gaudi_hwmgr.c +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/* - * Copyright 2016-2018 HabanaLabs, Ltd. - * All Rights Reserved. - */ - -#include "gaudiP.h" -#include "../include/gaudi/gaudi_fw_if.h" - -void gaudi_set_pll_profile(struct hl_device *hdev, enum hl_pll_frequency freq) -{ - struct gaudi_device *gaudi = hdev->asic_specific; - - if (freq == PLL_LAST) - hl_set_frequency(hdev, HL_GAUDI_MME_PLL, gaudi->max_freq_value); -} - -int gaudi_get_clk_rate(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk) -{ - long value; - - if (!hl_device_operational(hdev, NULL)) - return -ENODEV; - - value = hl_get_frequency(hdev, HL_GAUDI_MME_PLL, false); - - if (value < 0) { - dev_err(hdev->dev, "Failed to retrieve device max clock %ld\n", - value); - return value; - } - - *max_clk = (value / 1000 / 1000); - - value = hl_get_frequency(hdev, HL_GAUDI_MME_PLL, true); - - if (value < 0) { - dev_err(hdev->dev, - "Failed to retrieve device current clock %ld\n", - value); - return value; - } - - *cur_clk = (value / 1000 / 1000); - - return 0; -} - -static ssize_t clk_max_freq_mhz_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - struct gaudi_device *gaudi = hdev->asic_specific; - long value; - - if (!hl_device_operational(hdev, NULL)) - return -ENODEV; - - value = hl_get_frequency(hdev, HL_GAUDI_MME_PLL, false); - - gaudi->max_freq_value = value; - - return sprintf(buf, "%lu\n", (value / 1000 / 1000)); -} - -static ssize_t clk_max_freq_mhz_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - struct gaudi_device *gaudi = hdev->asic_specific; - int rc; - u64 value; - - if (!hl_device_operational(hdev, NULL)) { - count = -ENODEV; - goto fail; - } - - rc = kstrtoull(buf, 0, &value); - if (rc) { - count = -EINVAL; - goto fail; - } - - gaudi->max_freq_value = value * 1000 * 1000; - - hl_set_frequency(hdev, HL_GAUDI_MME_PLL, gaudi->max_freq_value); - -fail: - return count; -} - -static ssize_t clk_cur_freq_mhz_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - long value; - - if (!hl_device_operational(hdev, NULL)) - return -ENODEV; - - value = hl_get_frequency(hdev, HL_GAUDI_MME_PLL, true); - - return sprintf(buf, "%lu\n", (value / 1000 / 1000)); -} - -static DEVICE_ATTR_RW(clk_max_freq_mhz); -static DEVICE_ATTR_RO(clk_cur_freq_mhz); - -static struct attribute *gaudi_dev_attrs[] = { - &dev_attr_clk_max_freq_mhz.attr, - &dev_attr_clk_cur_freq_mhz.attr, - NULL, -}; - -void gaudi_add_device_attr(struct hl_device *hdev, - struct attribute_group *dev_attr_grp) -{ - dev_attr_grp->attrs = gaudi_dev_attrs; -} diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index ad00789348d9..5536e8c27bd5 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -471,6 +471,8 @@ int goya_set_fixed_properties(struct hl_device *hdev) prop->server_type = HL_SERVER_TYPE_UNKNOWN; + prop->clk_pll_index = HL_GOYA_MME_PLL; + return 0; } @@ -5656,7 +5658,7 @@ static const struct hl_asic_funcs goya_funcs = { .halt_coresight = goya_halt_coresight, .ctx_init = goya_ctx_init, .ctx_fini = goya_ctx_fini, - .get_clk_rate = goya_get_clk_rate, + .get_clk_rate = hl_get_clk_rate, .get_queue_id_for_cq = goya_get_queue_id_for_cq, .load_firmware_to_device = goya_load_firmware_to_device, .load_boot_fit_to_device = goya_load_boot_fit_to_device, diff --git a/drivers/misc/habanalabs/goya/goyaP.h b/drivers/misc/habanalabs/goya/goyaP.h index 0b05da614729..97add7b04f82 100644 --- a/drivers/misc/habanalabs/goya/goyaP.h +++ b/drivers/misc/habanalabs/goya/goyaP.h @@ -235,7 +235,6 @@ void goya_cpu_accessible_dma_pool_free(struct hl_device *hdev, size_t size, void *vaddr); void goya_mmu_remove_device_cpu_mappings(struct hl_device *hdev); -int goya_get_clk_rate(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk); u32 goya_get_queue_id_for_cq(struct hl_device *hdev, u32 cq_idx); u64 goya_get_device_time(struct hl_device *hdev); diff --git a/drivers/misc/habanalabs/goya/goya_hwmgr.c b/drivers/misc/habanalabs/goya/goya_hwmgr.c index 7d007125727f..59b2624ff81a 100644 --- a/drivers/misc/habanalabs/goya/goya_hwmgr.c +++ b/drivers/misc/habanalabs/goya/goya_hwmgr.c @@ -32,37 +32,6 @@ void goya_set_pll_profile(struct hl_device *hdev, enum hl_pll_frequency freq) } } -int goya_get_clk_rate(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk) -{ - long value; - - if (!hl_device_operational(hdev, NULL)) - return -ENODEV; - - value = hl_get_frequency(hdev, HL_GOYA_MME_PLL, false); - - if (value < 0) { - dev_err(hdev->dev, "Failed to retrieve device max clock %ld\n", - value); - return value; - } - - *max_clk = (value / 1000 / 1000); - - value = hl_get_frequency(hdev, HL_GOYA_MME_PLL, true); - - if (value < 0) { - dev_err(hdev->dev, - "Failed to retrieve device current clock %ld\n", - value); - return value; - } - - *cur_clk = (value / 1000 / 1000); - - return 0; -} - static ssize_t mme_clk_show(struct device *dev, struct device_attribute *attr, char *buf) { -- cgit v1.2.3-70-g09d2 From 4a18dde5e4c6348d48e3021cfbb01ef699005f19 Mon Sep 17 00:00:00 2001 From: Moti Haimovski Date: Mon, 16 Aug 2021 14:39:46 +0300 Subject: habanalabs: initialize hpriv fields before adding new node When adding a new node to the hpriv list, the driver should initialize its fields before adding the new node. Otherwise, there may be some small chance of another thread traversing that list and accessing the new node's fields without them being initialized. Signed-off-by: Moti Haimovski Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/habanalabs_drv.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/drivers/misc/habanalabs/common/habanalabs_drv.c b/drivers/misc/habanalabs/common/habanalabs_drv.c index 1da56069750a..949d1b5c5c41 100644 --- a/drivers/misc/habanalabs/common/habanalabs_drv.c +++ b/drivers/misc/habanalabs/common/habanalabs_drv.c @@ -225,6 +225,17 @@ int hl_device_open_ctrl(struct inode *inode, struct file *filp) if (!hpriv) return -ENOMEM; + /* Prevent other routines from reading partial hpriv data by + * initializing hpriv fields before inserting it to the list + */ + hpriv->hdev = hdev; + filp->private_data = hpriv; + hpriv->filp = filp; + hpriv->is_control = true; + nonseekable_open(inode, filp); + + hpriv->taskpid = find_get_pid(current->pid); + mutex_lock(&hdev->fpriv_list_lock); if (!hl_device_operational(hdev, NULL)) { @@ -238,19 +249,15 @@ int hl_device_open_ctrl(struct inode *inode, struct file *filp) list_add(&hpriv->dev_node, &hdev->fpriv_list); mutex_unlock(&hdev->fpriv_list_lock); - hpriv->hdev = hdev; - filp->private_data = hpriv; - hpriv->filp = filp; - hpriv->is_control = true; - nonseekable_open(inode, filp); - - hpriv->taskpid = find_get_pid(current->pid); - return 0; out_err: mutex_unlock(&hdev->fpriv_list_lock); + filp->private_data = NULL; + put_pid(hpriv->taskpid); + kfree(hpriv); + return rc; } -- cgit v1.2.3-70-g09d2 From d2f5684b8f28da52b8df9ac92a84d0f1c6983f5f Mon Sep 17 00:00:00 2001 From: Yuri Nudelman Date: Wed, 6 Oct 2021 11:58:02 +0300 Subject: habanalabs: simplify wait for interrupt with timestamp flow Remove the flag that determines whether to take a timestamp once the interrupt arrives. Instead, always take the timestamp once per interrupt. This is a must for the user-space to measure its graph operations to evaluate the graph computation time. Signed-off-by: Yuri Nudelman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_submission.c | 11 +++++------ drivers/misc/habanalabs/common/habanalabs.h | 2 -- drivers/misc/habanalabs/common/irq.c | 4 ++-- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index a3358cc3c877..f94ac2350a9b 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -2768,7 +2768,7 @@ static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx, u32 timeout_us, u64 user_address, u64 target_value, u16 interrupt_offset, enum hl_cs_wait_status *status, - bool take_timestamp, u64 *timestamp) + u64 *timestamp) { struct hl_user_pending_interrupt *pend; struct hl_user_interrupt *interrupt; @@ -2792,8 +2792,6 @@ static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx, hl_fence_init(&pend->fence, ULONG_MAX); - pend->fence.take_timestamp = take_timestamp; - if (interrupt_offset == HL_COMMON_USER_INTERRUPT_ID) interrupt = &hdev->common_user_interrupt; else @@ -2815,9 +2813,11 @@ static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx, goto remove_pending_user_interrupt; } - if (completion_value >= target_value) + if (completion_value >= target_value) { *status = CS_WAIT_STATUS_COMPLETED; - else + /* There was no interrupt, we assume the completion is now. */ + pend->fence.timestamp = ktime_get(); + } else *status = CS_WAIT_STATUS_BUSY; if (!timeout_us || (*status == CS_WAIT_STATUS_COMPLETED)) @@ -2914,7 +2914,6 @@ static int hl_interrupt_wait_ioctl(struct hl_fpriv *hpriv, void *data) rc = _hl_interrupt_wait_ioctl(hdev, hpriv->ctx, args->in.interrupt_timeout_us, args->in.addr, args->in.target, interrupt_offset, &status, - args->in.flags & HL_CS_FLAGS_TIMESTAMP, ×tamp); if (rc) { diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index e7b3b121f24d..a2002cbf794b 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -615,7 +615,6 @@ struct asic_fixed_properties { * masters QIDs that multi cs is waiting on * @error: mark this fence with error * @timestamp: timestamp upon completion - * @take_timestamp: timestamp shall be taken upon completion * @mcs_handling_done: indicates that corresponding command submission has * finished msc handling, this does not mean it was part * of the mcs @@ -627,7 +626,6 @@ struct hl_fence { u32 stream_master_qid_map; int error; ktime_t timestamp; - u8 take_timestamp; u8 mcs_handling_done; }; diff --git a/drivers/misc/habanalabs/common/irq.c b/drivers/misc/habanalabs/common/irq.c index 0dd00ffd1809..96d82b682674 100644 --- a/drivers/misc/habanalabs/common/irq.c +++ b/drivers/misc/habanalabs/common/irq.c @@ -141,11 +141,11 @@ static void handle_user_cq(struct hl_device *hdev, struct hl_user_interrupt *user_cq) { struct hl_user_pending_interrupt *pend; + ktime_t now = ktime_get(); spin_lock(&user_cq->wait_list_lock); list_for_each_entry(pend, &user_cq->wait_list_head, wait_list_node) { - if (pend->fence.take_timestamp) - pend->fence.timestamp = ktime_get(); + pend->fence.timestamp = now; complete_all(&pend->fence.completion); } spin_unlock(&user_cq->wait_list_lock); -- cgit v1.2.3-70-g09d2 From fae132632c55fdab81a81e0fd3d578d38ce43f78 Mon Sep 17 00:00:00 2001 From: Omer Shpigelman Date: Mon, 11 Oct 2021 10:03:44 +0300 Subject: habanalabs: context cleanup cosmetics No need to check the return value if the following action is the same for both cases. In addition, now that hl_ctx_free() doesn't print if the context is not released, its name can be misleading as the context might stay alive after it is executed with no indication for that. Hence we can discard it and simply put the refcount. Signed-off-by: Omer Shpigelman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/context.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/misc/habanalabs/common/context.c b/drivers/misc/habanalabs/common/context.c index 22978303ad63..d0aaccd4df2c 100644 --- a/drivers/misc/habanalabs/common/context.c +++ b/drivers/misc/habanalabs/common/context.c @@ -181,12 +181,6 @@ out_err: return rc; } -void hl_ctx_free(struct hl_device *hdev, struct hl_ctx *ctx) -{ - if (kref_put(&ctx->refcount, hl_ctx_do_release) == 1) - return; -} - int hl_ctx_init(struct hl_device *hdev, struct hl_ctx *ctx, bool is_kernel_ctx) { int rc = 0; @@ -392,7 +386,7 @@ void hl_ctx_mgr_fini(struct hl_device *hdev, struct hl_ctx_mgr *mgr) idp = &mgr->ctx_handles; idr_for_each_entry(idp, ctx, id) - hl_ctx_free(hdev, ctx); + kref_put(&ctx->refcount, hl_ctx_do_release); idr_destroy(&mgr->ctx_handles); mutex_destroy(&mgr->ctx_lock); -- cgit v1.2.3-70-g09d2 From b2faac3887df87a6e4bc3356280ea35dc7459c0b Mon Sep 17 00:00:00 2001 From: Dani Liberman Date: Tue, 12 Oct 2021 13:30:27 +0300 Subject: habanalabs: refactor fence handling in hl_cs_poll_fences To avoid checking if fence exists multipled times, changed fence handling to depend only on the fence status field: Busy, which means CS still did not completed : Add its QID so multi CS wait on its completion. Finished, which means CS completed and fence exists: Raise its completion bit if it finished mcs handling and update if necessary the earliest timestamp. Gone, which means CS already completed and fence deleted: Update multi CS data to ignore timestamp and raise its completion bit. Signed-off-by: Dani Liberman Reported-by: kernel test robot Reported-by: Dan Carpenter Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- .../misc/habanalabs/common/command_submission.c | 71 +++++++++++----------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index f94ac2350a9b..4c8000fd246c 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -2382,47 +2382,48 @@ static int hl_cs_poll_fences(struct multi_cs_data *mcs_data) break; } - /* - * It is possible to get an old sequence numbers from user - * which related to already completed CSs and their fences - * already gone. In this case, no need to consider its QID for - * mcs completion. - */ - if (fence) + switch (status) { + case CS_WAIT_STATUS_BUSY: + /* CS did not finished, keep waiting on its QID*/ mcs_data->stream_master_qid_map |= fence->stream_master_qid_map; + break; + case CS_WAIT_STATUS_COMPLETED: + /* + * Using mcs_handling_done to avoid possibility of mcs_data + * returns to user indicating CS completed before it finished + * all of its mcs handling, to avoid race the next time the + * user waits for mcs. + */ + if (!fence->mcs_handling_done) + break; - /* - * Using mcs_handling_done to avoid possibility of mcs_data - * returns to user indicating CS completed before it finished - * all of its mcs handling, to avoid race the next time the - * user waits for mcs. - */ - if (status == CS_WAIT_STATUS_BUSY || - (fence && !fence->mcs_handling_done)) - continue; - - mcs_data->completion_bitmap |= BIT(i); - - /* - * best effort to extract timestamp. few notes: - * - if even single fence is gone we cannot extract timestamp - * (as fence not exist anymore) - * - for all completed CSs we take the earliest timestamp. - * for this we have to validate that: - * 1. given timestamp was indeed set - * 2. the timestamp is earliest of all timestamps so far - */ - - if (status == CS_WAIT_STATUS_GONE) { + mcs_data->completion_bitmap |= BIT(i); + /* + * For all completed CSs we take the earliest timestamp. + * For this we have to validate that the timestamp is + * earliest of all timestamps so far. + */ + if (mcs_data->update_ts && + (ktime_compare(fence->timestamp, first_cs_time) < 0)) + first_cs_time = fence->timestamp; + break; + case CS_WAIT_STATUS_GONE: mcs_data->update_ts = false; mcs_data->gone_cs = true; - } else if (mcs_data->update_ts && - (ktime_compare(fence->timestamp, - ktime_set(0, 0)) > 0) && - (ktime_compare(fence->timestamp, first_cs_time) < 0)) { - first_cs_time = fence->timestamp; + /* + * It is possible to get an old sequence numbers from user + * which related to already completed CSs and their fences + * already gone. In this case, CS set as completed but + * no need to consider its QID for mcs completion. + */ + mcs_data->completion_bitmap |= BIT(i); + break; + default: + dev_err(hdev->dev, "Invalid fence status\n"); + return -EINVAL; } + } hl_fences_put(mcs_data->fence_arr, arr_len); -- cgit v1.2.3-70-g09d2 From 7110acbdab462b8f2bc30e216c331cbd68c00af9 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 4 Oct 2021 22:51:57 -0700 Subject: counter: fix docum. build problems after filename change Fix documentation build warnings due to a source file being renamed. WARNING: kernel-doc '../scripts/kernel-doc -rst -enable-lineno -sphinx-version 1.8.5 -export ../drivers/counter/counter.c' failed with return code 2 Error: Cannot open file ../drivers/counter/counter.c Fixes: aaec1a0f76ec ("counter: Internalize sysfs interface code") Signed-off-by: Randy Dunlap Cc: William Breathitt Gray Cc: linux-iio@vger.kernel.org Cc: Jonathan Cameron Cc: Jonathan Corbet Cc: linux-doc@vger.kernel.org Acked-by: William Breathitt Gray Link: https://lore.kernel.org/r/20211005055157.22937-1-rdunlap@infradead.org Signed-off-by: Jonathan Cameron --- Documentation/driver-api/generic-counter.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst index 25932dcb6e67..ae9d3c2079f1 100644 --- a/Documentation/driver-api/generic-counter.rst +++ b/Documentation/driver-api/generic-counter.rst @@ -234,7 +234,7 @@ for defining a counter device. .. kernel-doc:: include/linux/counter.h :internal: -.. kernel-doc:: drivers/counter/counter.c +.. kernel-doc:: drivers/counter/counter-core.c :export: Driver Implementation -- cgit v1.2.3-70-g09d2 From 49af37fc7d3c50ca98143d1f904638a548685622 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Tue, 5 Oct 2021 15:14:37 +0900 Subject: docs: counter: Include counter-chrdev kernel-doc to generic-counter.rst The counter-chrdev.c file exports the counter_push_event() function. Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/20211005061437.4846-1-vilhelm.gray@gmail.com Signed-off-by: Jonathan Cameron --- Documentation/driver-api/generic-counter.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst index ae9d3c2079f1..1b487a331467 100644 --- a/Documentation/driver-api/generic-counter.rst +++ b/Documentation/driver-api/generic-counter.rst @@ -237,6 +237,9 @@ for defining a counter device. .. kernel-doc:: drivers/counter/counter-core.c :export: +.. kernel-doc:: drivers/counter/counter-chrdev.c + :export: + Driver Implementation ===================== -- cgit v1.2.3-70-g09d2 From 42e1e8244118e8c41bd4a26c06b037151c4f4ff8 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Mon, 13 Sep 2021 20:19:49 +0200 Subject: dt-bindings: iio: magnetometer: asahi-kasei,ak8975 add vid reg Driver and device-tree also use vid-supply regulator. Fixes: 7e000fbff7a0 ("dt-bindings: iio: magnetometer: ak8975: convert format to yaml, add maintainer") Signed-off-by: David Heidelberg Acked-by: Rob Herring Link: https://lore.kernel.org/r/20210913181949.83179-1-david@ixit.cz Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml b/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml index c552b2b7751a..9790f75fc669 100644 --- a/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml +++ b/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml @@ -45,6 +45,11 @@ properties: an optional regulator that needs to be on to provide VDD power to the sensor. + vid-supply: + description: | + an optional regulator that needs to be on to provide VID power to + the sensor. + mount-matrix: description: an optional 3x3 mounting rotation matrix. -- cgit v1.2.3-70-g09d2 From c6cb6ac7b324127d66074d72874b6dbcda3c980f Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 20 Sep 2021 15:54:10 +0200 Subject: dt-bindings: iio: chemical: Document senseair,sunrise CO2 sensor Add documentation for the Senseair Sunrise 006-0-0007 CO2 NDIR sensor. Signed-off-by: Jacopo Mondi Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20210920135413.140310-2-jacopo+renesas@jmondi.org Signed-off-by: Jonathan Cameron --- .../bindings/iio/chemical/senseair,sunrise.yaml | 55 ++++++++++++++++++++++ .../devicetree/bindings/vendor-prefixes.yaml | 2 + 2 files changed, 57 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/chemical/senseair,sunrise.yaml diff --git a/Documentation/devicetree/bindings/iio/chemical/senseair,sunrise.yaml b/Documentation/devicetree/bindings/iio/chemical/senseair,sunrise.yaml new file mode 100644 index 000000000000..337fe09e4bb8 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/chemical/senseair,sunrise.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/chemical/senseair,sunrise.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Senseair Sunrise 006-0-0007 CO2 Sensor + +maintainers: + - Jacopo Mondi + +description: | + Senseair Sunrise 006-0-0007 is a NDIR CO2 sensor. It supports I2C or UART buses + for communications and control. + + Datasheets: + https://rmtplusstoragesenseair.blob.core.windows.net/docs/Dev/publicerat/PSP11704.pdf + https://rmtplusstoragesenseair.blob.core.windows.net/docs/Dev/publicerat/PSH11649.pdf + https://rmtplusstoragesenseair.blob.core.windows.net/docs/Dev/publicerat/TDE5531.pdf + https://rmtplusstoragesenseair.blob.core.windows.net/docs/Market/publicerat/TDE7318.pdf + +properties: + compatible: + const: senseair,sunrise-006-0-0007 + + reg: + maxItems: 1 + + ndry-gpios: + maxItems: 1 + description: + Phandle to the GPIO line connected to the nDRY pin. Typically active low. + + en-gpios: + maxItems: 1 + description: + Phandle to the GPIO line connected to the EN pin. Typically active high. + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + co2-sensor@68 { + compatible = "senseair,sunrise-006-0-0007"; + reg = <0x68>; + }; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index a867f7102c35..42b529a8e5db 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -1018,6 +1018,8 @@ patternProperties: description: Shenzhen SEI Robotics Co., Ltd "^semtech,.*": description: Semtech Corporation + "^senseair,.*": + description: Senseair AB "^sensirion,.*": description: Sensirion AG "^sensortek,.*": -- cgit v1.2.3-70-g09d2 From c3c780ef765cc54d0f2e5f972ec5e8f71c89030a Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 20 Sep 2021 15:54:11 +0200 Subject: iio: ABI: docs: Document Senseair Sunrise ABI Add documentation for the sysfs attributes of the sunrise_co2 driver. Signed-off-by: Jacopo Mondi Link: https://lore.kernel.org/r/20210920135413.140310-3-jacopo+renesas@jmondi.org Signed-off-by: Jonathan Cameron --- .../ABI/testing/sysfs-bus-iio-chemical-sunrise-co2 | 38 ++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-chemical-sunrise-co2 diff --git a/Documentation/ABI/testing/sysfs-bus-iio-chemical-sunrise-co2 b/Documentation/ABI/testing/sysfs-bus-iio-chemical-sunrise-co2 new file mode 100644 index 000000000000..ee7aeb11709b --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-chemical-sunrise-co2 @@ -0,0 +1,38 @@ +What: /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_calibration_factory +Date: August 2021 +KernelVersion: 5.16 +Contact: Jacopo Mondi +Description: + Writing '1' triggers a 'Factory' calibration cycle. + +What: /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_calibration_background +Date: August 2021 +KernelVersion: 5.16 +Contact: Jacopo Mondi +Description: + Writing '1' triggers a 'Background' calibration cycle. + +What: /sys/bus/iio/devices/iio:deviceX/error_status_available +Date: August 2021 +KernelVersion: 5.16 +Contact: Jacopo Mondi +Description: + Reading returns the list of possible chip error status. + Available options are: + - 'error_fatal': Analog front-end initialization error + - 'error_i2c': Read/write to non-existing register + - 'error_algorithm': Corrupted parameters + - 'error_calibration': Calibration has failed + - 'error_self_diagnostic': Internal interface failure + - 'error_out_of_range': Measured concentration out of scale + - 'error_memory': Error during memory operations + - 'error_no_measurement': Cleared at first measurement + - 'error_low_voltage': Sensor regulated voltage too low + - 'error_measurement_timeout': Unable to complete measurement + +What: /sys/bus/iio/devices/iio:deviceX/error_status +Date: August 2021 +KernelVersion: 5.16 +Contact: Jacopo Mondi +Description: + Reading returns the current chip error status. -- cgit v1.2.3-70-g09d2 From c397894e24f1c7281376c14dfdd4df8fe0e84754 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 20 Sep 2021 15:54:12 +0200 Subject: iio: chemical: Add Senseair Sunrise 006-0-007 driver Add support for the Senseair Sunrise 006-0-0007 driver through the IIO subsystem. Datasheet: https://rmtplusstoragesenseair.blob.core.windows.net/docs/Dev/publicerat/TDE5531.pdf Signed-off-by: Jacopo Mondi Link: https://lore.kernel.org/r/20210920135413.140310-4-jacopo+renesas@jmondi.org Signed-off-by: Jonathan Cameron --- MAINTAINERS | 7 + drivers/iio/chemical/Kconfig | 10 + drivers/iio/chemical/Makefile | 1 + drivers/iio/chemical/sunrise_co2.c | 537 +++++++++++++++++++++++++++++++++++++ 4 files changed, 555 insertions(+) create mode 100644 drivers/iio/chemical/sunrise_co2.c diff --git a/MAINTAINERS b/MAINTAINERS index 41b969921d0a..2429e27daece 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16887,6 +16887,13 @@ S: Maintained F: drivers/misc/phantom.c F: include/uapi/linux/phantom.h +SENSEAIR SUNRISE 006-0-0007 +M: Jacopo Mondi +S: Maintained +F: Documentation/ABI/testing/sysfs-bus-iio-chemical-sunrise-co2 +F: Documentation/devicetree/bindings/iio/chemical/senseair,sunrise.yaml +F: drivers/iio/chemical/sunrise_co2.c + SENSIRION SCD30 CARBON DIOXIDE SENSOR DRIVER M: Tomasz Duszynski S: Maintained diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig index c03667e62732..dc169f9ad4e9 100644 --- a/drivers/iio/chemical/Kconfig +++ b/drivers/iio/chemical/Kconfig @@ -170,6 +170,16 @@ config SPS30_SERIAL To compile this driver as a module, choose M here: the module will be called sps30_serial. +config SENSEAIR_SUNRISE_CO2 + tristate "Senseair Sunrise 006-0-0007 CO2 sensor" + select REGMAP_I2C + help + Say yes here to build support for Senseair Sunrise 006-0-0007 CO2 + sensor. + + To compile this driver as a module, choose M here: the + module will be called sunrise_co2. + config VZ89X tristate "SGX Sensortech MiCS VZ89X VOC sensor" depends on I2C diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile index d07af581f234..2569f52432f0 100644 --- a/drivers/iio/chemical/Makefile +++ b/drivers/iio/chemical/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_PMS7003) += pms7003.o obj-$(CONFIG_SCD30_CORE) += scd30_core.o obj-$(CONFIG_SCD30_I2C) += scd30_i2c.o obj-$(CONFIG_SCD30_SERIAL) += scd30_serial.o +obj-$(CONFIG_SENSEAIR_SUNRISE_CO2) += sunrise_co2.o obj-$(CONFIG_SENSIRION_SGP30) += sgp30.o obj-$(CONFIG_SENSIRION_SGP40) += sgp40.o obj-$(CONFIG_SPS30) += sps30.o diff --git a/drivers/iio/chemical/sunrise_co2.c b/drivers/iio/chemical/sunrise_co2.c new file mode 100644 index 000000000000..233bd0f379c9 --- /dev/null +++ b/drivers/iio/chemical/sunrise_co2.c @@ -0,0 +1,537 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Senseair Sunrise 006-0-0007 CO2 sensor driver. + * + * Copyright (C) 2021 Jacopo Mondi + * + * List of features not yet supported by the driver: + * - controllable EN pin + * - single-shot operations using the nDRY pin. + * - ABC/target calibration + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRIVER_NAME "sunrise_co2" + +#define SUNRISE_ERROR_STATUS_REG 0x00 +#define SUNRISE_CO2_FILTERED_COMP_REG 0x06 +#define SUNRISE_CHIP_TEMPERATURE_REG 0x08 +#define SUNRISE_CALIBRATION_STATUS_REG 0x81 +#define SUNRISE_CALIBRATION_COMMAND_REG 0x82 +#define SUNRISE_CALIBRATION_FACTORY_CMD 0x7c02 +#define SUNRISE_CALIBRATION_BACKGROUND_CMD 0x7c06 +/* + * The calibration timeout is not characterized in the datasheet. + * Use 30 seconds as a reasonable upper limit. + */ +#define SUNRISE_CALIBRATION_TIMEOUT_US (30 * USEC_PER_SEC) + +struct sunrise_dev { + struct i2c_client *client; + struct regmap *regmap; + /* Protects access to IIO attributes. */ + struct mutex lock; + bool ignore_nak; +}; + +/* Custom regmap read/write operations: perform unlocked access to the i2c bus. */ + +static int sunrise_regmap_read(void *context, const void *reg_buf, + size_t reg_size, void *val_buf, size_t val_size) +{ + struct i2c_client *client = context; + struct sunrise_dev *sunrise = i2c_get_clientdata(client); + union i2c_smbus_data data; + int ret; + + if (reg_size != 1 || !val_size) + return -EINVAL; + + memset(&data, 0, sizeof(data)); + data.block[0] = val_size; + + /* + * Wake up sensor by sending sensor address: START, sensor address, + * STOP. Sensor will not ACK this byte. + * + * The chip enters a low power state after 15ms without + * communications or after a complete read/write sequence. + */ + __i2c_smbus_xfer(client->adapter, client->addr, + sunrise->ignore_nak ? I2C_M_IGNORE_NAK : 0, + I2C_SMBUS_WRITE, 0, I2C_SMBUS_BYTE_DATA, &data); + + usleep_range(500, 1500); + + ret = __i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, ((u8 *)reg_buf)[0], + I2C_SMBUS_I2C_BLOCK_DATA, &data); + if (ret < 0) + return ret; + + memcpy(val_buf, &data.block[1], data.block[0]); + + return 0; +} + +static int sunrise_regmap_write(void *context, const void *val_buf, size_t count) +{ + struct i2c_client *client = context; + struct sunrise_dev *sunrise = i2c_get_clientdata(client); + union i2c_smbus_data data; + + /* Discard reg address from values count. */ + if (!count) + return -EINVAL; + count--; + + memset(&data, 0, sizeof(data)); + data.block[0] = count; + memcpy(&data.block[1], (u8 *)val_buf + 1, count); + + __i2c_smbus_xfer(client->adapter, client->addr, + sunrise->ignore_nak ? I2C_M_IGNORE_NAK : 0, + I2C_SMBUS_WRITE, 0, I2C_SMBUS_BYTE_DATA, &data); + + usleep_range(500, 1500); + + return __i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, ((u8 *)val_buf)[0], + I2C_SMBUS_I2C_BLOCK_DATA, &data); +} + +/* + * Sunrise i2c read/write operations: lock the i2c segment to avoid losing the + * wake up session. Use custom regmap operations that perform unlocked access to + * the i2c bus. + */ +static int sunrise_read_byte(struct sunrise_dev *sunrise, u8 reg) +{ + const struct i2c_client *client = sunrise->client; + const struct device *dev = &client->dev; + unsigned int val; + int ret; + + i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT); + ret = regmap_read(sunrise->regmap, reg, &val); + i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT); + if (ret) { + dev_err(dev, "Read byte failed: reg 0x%02x (%d)\n", reg, ret); + return ret; + } + + return val; +} + +static int sunrise_read_word(struct sunrise_dev *sunrise, u8 reg, u16 *val) +{ + const struct i2c_client *client = sunrise->client; + const struct device *dev = &client->dev; + __be16 be_val; + int ret; + + i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT); + ret = regmap_bulk_read(sunrise->regmap, reg, &be_val, sizeof(be_val)); + i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT); + if (ret) { + dev_err(dev, "Read word failed: reg 0x%02x (%d)\n", reg, ret); + return ret; + } + + *val = be16_to_cpu(be_val); + + return 0; +} + +static int sunrise_write_byte(struct sunrise_dev *sunrise, u8 reg, u8 val) +{ + const struct i2c_client *client = sunrise->client; + const struct device *dev = &client->dev; + int ret; + + i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT); + ret = regmap_write(sunrise->regmap, reg, val); + i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT); + if (ret) + dev_err(dev, "Write byte failed: reg 0x%02x (%d)\n", reg, ret); + + return ret; +} + +static int sunrise_write_word(struct sunrise_dev *sunrise, u8 reg, u16 data) +{ + const struct i2c_client *client = sunrise->client; + const struct device *dev = &client->dev; + __be16 be_data = cpu_to_be16(data); + int ret; + + i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT); + ret = regmap_bulk_write(sunrise->regmap, reg, &be_data, sizeof(be_data)); + i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT); + if (ret) + dev_err(dev, "Write word failed: reg 0x%02x (%d)\n", reg, ret); + + return ret; +} + +/* Trigger a calibration cycle. */ + +enum { + SUNRISE_CALIBRATION_FACTORY, + SUNRISE_CALIBRATION_BACKGROUND, +}; + +static const struct sunrise_calib_data { + u16 cmd; + u8 bit; + const char * const name; +} calib_data[] = { + [SUNRISE_CALIBRATION_FACTORY] = { + SUNRISE_CALIBRATION_FACTORY_CMD, + BIT(2), + "factory_calibration", + }, + [SUNRISE_CALIBRATION_BACKGROUND] = { + SUNRISE_CALIBRATION_BACKGROUND_CMD, + BIT(5), + "background_calibration", + }, +}; + +static int sunrise_calibrate(struct sunrise_dev *sunrise, + const struct sunrise_calib_data *data) +{ + unsigned int status; + int ret; + + /* Reset the calibration status reg. */ + ret = sunrise_write_byte(sunrise, SUNRISE_CALIBRATION_STATUS_REG, 0x00); + if (ret) + return ret; + + /* Write a calibration command and poll the calibration status bit. */ + ret = sunrise_write_word(sunrise, SUNRISE_CALIBRATION_COMMAND_REG, data->cmd); + if (ret) + return ret; + + dev_dbg(&sunrise->client->dev, "%s in progress\n", data->name); + + /* + * Calibration takes several seconds, so the sleep time between reads + * can be pretty relaxed. + */ + return read_poll_timeout(sunrise_read_byte, status, status & data->bit, + 200000, SUNRISE_CALIBRATION_TIMEOUT_US, false, + sunrise, SUNRISE_CALIBRATION_STATUS_REG); +} + +static ssize_t sunrise_cal_factory_write(struct iio_dev *iiodev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct sunrise_dev *sunrise = iio_priv(iiodev); + bool enable; + int ret; + + ret = kstrtobool(buf, &enable); + if (ret) + return ret; + + if (!enable) + return len; + + mutex_lock(&sunrise->lock); + ret = sunrise_calibrate(sunrise, &calib_data[SUNRISE_CALIBRATION_FACTORY]); + mutex_unlock(&sunrise->lock); + if (ret) + return ret; + + return len; +} + +static ssize_t sunrise_cal_background_write(struct iio_dev *iiodev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct sunrise_dev *sunrise = iio_priv(iiodev); + bool enable; + int ret; + + ret = kstrtobool(buf, &enable); + if (ret) + return ret; + + if (!enable) + return len; + + mutex_lock(&sunrise->lock); + ret = sunrise_calibrate(sunrise, &calib_data[SUNRISE_CALIBRATION_BACKGROUND]); + mutex_unlock(&sunrise->lock); + if (ret) + return ret; + + return len; +} + + /* Enumerate and retrieve the chip error status. */ +enum { + SUNRISE_ERROR_FATAL, + SUNRISE_ERROR_I2C, + SUNRISE_ERROR_ALGORITHM, + SUNRISE_ERROR_CALIBRATION, + SUNRISE_ERROR_SELF_DIAGNOSTIC, + SUNRISE_ERROR_OUT_OF_RANGE, + SUNRISE_ERROR_MEMORY, + SUNRISE_ERROR_NO_MEASUREMENT, + SUNRISE_ERROR_LOW_VOLTAGE, + SUNRISE_ERROR_MEASUREMENT_TIMEOUT, +}; + +static const char * const sunrise_error_statuses[] = { + [SUNRISE_ERROR_FATAL] = "error_fatal", + [SUNRISE_ERROR_I2C] = "error_i2c", + [SUNRISE_ERROR_ALGORITHM] = "error_algorithm", + [SUNRISE_ERROR_CALIBRATION] = "error_calibration", + [SUNRISE_ERROR_SELF_DIAGNOSTIC] = "error_self_diagnostic", + [SUNRISE_ERROR_OUT_OF_RANGE] = "error_out_of_range", + [SUNRISE_ERROR_MEMORY] = "error_memory", + [SUNRISE_ERROR_NO_MEASUREMENT] = "error_no_measurement", + [SUNRISE_ERROR_LOW_VOLTAGE] = "error_low_voltage", + [SUNRISE_ERROR_MEASUREMENT_TIMEOUT] = "error_measurement_timeout", +}; + +static const struct iio_enum sunrise_error_statuses_enum = { + .items = sunrise_error_statuses, + .num_items = ARRAY_SIZE(sunrise_error_statuses), +}; + +static ssize_t sunrise_error_status_read(struct iio_dev *iiodev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct sunrise_dev *sunrise = iio_priv(iiodev); + unsigned long errors; + ssize_t len = 0; + u16 value; + int ret; + u8 i; + + mutex_lock(&sunrise->lock); + ret = sunrise_read_word(sunrise, SUNRISE_ERROR_STATUS_REG, &value); + if (ret) { + mutex_unlock(&sunrise->lock); + return ret; + } + + errors = value; + for_each_set_bit(i, &errors, ARRAY_SIZE(sunrise_error_statuses)) + len += sysfs_emit_at(buf, len, "%s ", sunrise_error_statuses[i]); + + if (len) + buf[len - 1] = '\n'; + + mutex_unlock(&sunrise->lock); + + return len; +} + +static const struct iio_chan_spec_ext_info sunrise_concentration_ext_info[] = { + /* Calibration triggers. */ + { + .name = "calibration_factory", + .write = sunrise_cal_factory_write, + .shared = IIO_SEPARATE, + }, + { + .name = "calibration_background", + .write = sunrise_cal_background_write, + .shared = IIO_SEPARATE, + }, + + /* Error statuses. */ + { + .name = "error_status", + .read = sunrise_error_status_read, + .shared = IIO_SHARED_BY_ALL, + }, + { + .name = "error_status_available", + .shared = IIO_SHARED_BY_ALL, + .read = iio_enum_available_read, + .private = (uintptr_t)&sunrise_error_statuses_enum, + }, + {} +}; + +static const struct iio_chan_spec sunrise_channels[] = { + { + .type = IIO_CONCENTRATION, + .modified = 1, + .channel2 = IIO_MOD_CO2, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .ext_info = sunrise_concentration_ext_info, + }, + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static int sunrise_read_raw(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct sunrise_dev *sunrise = iio_priv(iio_dev); + u16 value; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_CONCENTRATION: + mutex_lock(&sunrise->lock); + ret = sunrise_read_word(sunrise, SUNRISE_CO2_FILTERED_COMP_REG, + &value); + *val = value; + mutex_unlock(&sunrise->lock); + + if (ret) + return ret; + + return IIO_VAL_INT; + + case IIO_TEMP: + mutex_lock(&sunrise->lock); + ret = sunrise_read_word(sunrise, SUNRISE_CHIP_TEMPERATURE_REG, + &value); + *val = value; + mutex_unlock(&sunrise->lock); + + if (ret) + return ret; + + return IIO_VAL_INT; + + default: + return -EINVAL; + } + + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_CONCENTRATION: + /* + * 1 / 10^4 to comply with IIO scale for CO2 + * (percentage). The chip CO2 reading range is [400 - + * 5000] ppm which corresponds to [0,004 - 0,5] %. + */ + *val = 1; + *val2 = 10000; + return IIO_VAL_FRACTIONAL; + + case IIO_TEMP: + /* x10 to comply with IIO scale (millidegrees celsius). */ + *val = 10; + return IIO_VAL_INT; + + default: + return -EINVAL; + } + + default: + return -EINVAL; + } +} + +static const struct iio_info sunrise_info = { + .read_raw = sunrise_read_raw, +}; + +static const struct regmap_bus sunrise_regmap_bus = { + .read = sunrise_regmap_read, + .write = sunrise_regmap_write, +}; + +static const struct regmap_config sunrise_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int sunrise_probe(struct i2c_client *client) +{ + struct sunrise_dev *sunrise; + struct iio_dev *iio_dev; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA)) { + dev_err(&client->dev, + "Adapter does not support required functionalities\n"); + return -EOPNOTSUPP; + } + + iio_dev = devm_iio_device_alloc(&client->dev, sizeof(*sunrise)); + if (!iio_dev) + return -ENOMEM; + + sunrise = iio_priv(iio_dev); + sunrise->client = client; + mutex_init(&sunrise->lock); + + i2c_set_clientdata(client, sunrise); + + sunrise->regmap = devm_regmap_init(&client->dev, &sunrise_regmap_bus, + client, &sunrise_regmap_config); + if (IS_ERR(sunrise->regmap)) { + dev_err(&client->dev, "Failed to initialize regmap\n"); + return PTR_ERR(sunrise->regmap); + } + + /* + * The chip nacks the wake up message. If the adapter does not support + * protocol mangling do not set the I2C_M_IGNORE_NAK flag at the expense + * of possible cruft in the logs. + */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_PROTOCOL_MANGLING)) + sunrise->ignore_nak = true; + + iio_dev->info = &sunrise_info; + iio_dev->name = DRIVER_NAME; + iio_dev->channels = sunrise_channels; + iio_dev->num_channels = ARRAY_SIZE(sunrise_channels); + iio_dev->modes = INDIO_DIRECT_MODE; + + return devm_iio_device_register(&client->dev, iio_dev); +} + +static const struct of_device_id sunrise_of_match[] = { + { .compatible = "senseair,sunrise-006-0-0007" }, + {} +}; +MODULE_DEVICE_TABLE(of, sunrise_of_match); + +static struct i2c_driver sunrise_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = sunrise_of_match, + }, + .probe_new = sunrise_probe, +}; +module_i2c_driver(sunrise_driver); + +MODULE_AUTHOR("Jacopo Mondi "); +MODULE_DESCRIPTION("Senseair Sunrise 006-0-0007 CO2 sensor IIO driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-70-g09d2 From f840f41fa5cba4c6d794f0b135c24475c33cbc39 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 20 Sep 2021 15:54:13 +0200 Subject: iio: ABI: Document in_concentration_co2_scale Document the 'in_concentration_co2_scale' standard IIO attribute. Signed-off-by: Jacopo Mondi Link: https://lore.kernel.org/r/20210920135413.140310-5-jacopo+renesas@jmondi.org Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 6ad47a67521c..f19ff76a3b54 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -429,6 +429,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_angl_scale What: /sys/bus/iio/devices/iio:deviceX/in_intensity_x_scale What: /sys/bus/iio/devices/iio:deviceX/in_intensity_y_scale What: /sys/bus/iio/devices/iio:deviceX/in_intensity_z_scale +What: /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_scale KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: -- cgit v1.2.3-70-g09d2 From 89c65417da9065f79b9785f1cdb89d5b67e9dad5 Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Wed, 22 Sep 2021 16:15:10 +0800 Subject: iio: adc: aspeed: Keep model data to driver data. Keep the model data pointer to driver data for reducing the usage of of_device_get_match_data(). Signed-off-by: Billy Tsai Link: https://lore.kernel.org/r/20210922081520.30580-2-billy_tsai@aspeedtech.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/aspeed_adc.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index d893151efd2f..d41fd3166e6b 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -77,6 +77,7 @@ struct aspeed_adc_model_data { struct aspeed_adc_data { struct device *dev; + const struct aspeed_adc_model_data *model_data; void __iomem *base; spinlock_t clk_lock; struct clk_hw *clk_prescaler; @@ -118,8 +119,6 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct aspeed_adc_data *data = iio_priv(indio_dev); - const struct aspeed_adc_model_data *model_data = - of_device_get_match_data(data->dev); switch (mask) { case IIO_CHAN_INFO_RAW: @@ -127,7 +126,7 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = model_data->vref_voltage; + *val = data->model_data->vref_voltage; *val2 = ASPEED_RESOLUTION_BITS; return IIO_VAL_FRACTIONAL_LOG2; @@ -146,13 +145,11 @@ static int aspeed_adc_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct aspeed_adc_data *data = iio_priv(indio_dev); - const struct aspeed_adc_model_data *model_data = - of_device_get_match_data(data->dev); switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: - if (val < model_data->min_sampling_rate || - val > model_data->max_sampling_rate) + if (val < data->model_data->min_sampling_rate || + val > data->model_data->max_sampling_rate) return -EINVAL; clk_set_rate(data->clk_scaler->clk, @@ -198,7 +195,6 @@ static int aspeed_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; struct aspeed_adc_data *data; - const struct aspeed_adc_model_data *model_data; const char *clk_parent_name; int ret; u32 adc_engine_control_reg_val; @@ -209,6 +205,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) data = iio_priv(indio_dev); data->dev = &pdev->dev; + data->model_data = of_device_get_match_data(&pdev->dev); data->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(data->base)) @@ -248,9 +245,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) } reset_control_deassert(data->rst); - model_data = of_device_get_match_data(&pdev->dev); - - if (model_data->wait_init_sequence) { + if (data->model_data->wait_init_sequence) { /* Enable engine in normal mode. */ writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | @@ -280,8 +275,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) writel(adc_engine_control_reg_val, data->base + ASPEED_REG_ENGINE_CONTROL); - model_data = of_device_get_match_data(&pdev->dev); - indio_dev->name = model_data->model_name; + indio_dev->name = data->model_data->model_name; indio_dev->info = &aspeed_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = aspeed_adc_iio_channels; -- cgit v1.2.3-70-g09d2 From eaa74a8d510d05a98b5839958ff3c70a30ba8d71 Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Wed, 22 Sep 2021 16:15:11 +0800 Subject: iio: adc: aspeed: Restructure the model data This patch refactors the model data structure to distinguish the function form different versions of aspeed ADC. - Rename the vref_voltage to vref_fixed_mv and add vref_mv driver data When driver probe will check vref_fixed_mv value and store it to vref_mv which isn't const value. - Add num_channels Make num_channles of iio device can be changed by different model_data - Add need_prescaler flag and scaler_bit_width The need_prescaler flag is used to tell the driver the clock divider needs another Prescaler and the scaler_bit_width to set the clock divider bitfield width. Signed-off-by: Billy Tsai Link: https://lore.kernel.org/r/20210922081520.30580-3-billy_tsai@aspeedtech.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/aspeed_adc.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index d41fd3166e6b..c1eadd670cc9 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -71,8 +71,11 @@ struct aspeed_adc_model_data { const char *model_name; unsigned int min_sampling_rate; // Hz unsigned int max_sampling_rate; // Hz - unsigned int vref_voltage; // mV + unsigned int vref_fixed_mv; bool wait_init_sequence; + bool need_prescaler; + u8 scaler_bit_width; + unsigned int num_channels; }; struct aspeed_adc_data { @@ -83,6 +86,7 @@ struct aspeed_adc_data { struct clk_hw *clk_prescaler; struct clk_hw *clk_scaler; struct reset_control *rst; + int vref_mv; }; #define ASPEED_CHAN(_idx, _data_reg_addr) { \ @@ -126,7 +130,7 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = data->model_data->vref_voltage; + *val = data->model_data->vref_fixed_mv; *val2 = ASPEED_RESOLUTION_BITS; return IIO_VAL_FRACTIONAL_LOG2; @@ -279,7 +283,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) indio_dev->info = &aspeed_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = aspeed_adc_iio_channels; - indio_dev->num_channels = ARRAY_SIZE(aspeed_adc_iio_channels); + indio_dev->num_channels = data->model_data->num_channels; ret = iio_device_register(indio_dev); if (ret) @@ -319,17 +323,23 @@ static int aspeed_adc_remove(struct platform_device *pdev) static const struct aspeed_adc_model_data ast2400_model_data = { .model_name = "ast2400-adc", - .vref_voltage = 2500, // mV + .vref_fixed_mv = 2500, .min_sampling_rate = 10000, .max_sampling_rate = 500000, + .need_prescaler = true, + .scaler_bit_width = 10, + .num_channels = 16, }; static const struct aspeed_adc_model_data ast2500_model_data = { .model_name = "ast2500-adc", - .vref_voltage = 1800, // mV + .vref_fixed_mv = 1800, .min_sampling_rate = 1, .max_sampling_rate = 1000000, .wait_init_sequence = true, + .need_prescaler = true, + .scaler_bit_width = 10, + .num_channels = 16, }; static const struct of_device_id aspeed_adc_matches[] = { -- cgit v1.2.3-70-g09d2 From 1de952a4b1cdcbaeb1347744d7e0f27d5b0e6647 Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Wed, 22 Sep 2021 16:15:12 +0800 Subject: iio: adc: aspeed: Add vref config function Add the function to check the vref_fixed_mv and set the value to driver data. Signed-off-by: Billy Tsai Link: https://lore.kernel.org/r/20210922081520.30580-4-billy_tsai@aspeedtech.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/aspeed_adc.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index c1eadd670cc9..c33563046f40 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -130,7 +130,7 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = data->model_data->vref_fixed_mv; + *val = data->vref_mv; *val2 = ASPEED_RESOLUTION_BITS; return IIO_VAL_FRACTIONAL_LOG2; @@ -195,6 +195,17 @@ static const struct iio_info aspeed_adc_iio_info = { .debugfs_reg_access = aspeed_adc_reg_access, }; +static int aspeed_adc_vref_config(struct iio_dev *indio_dev) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + + if (data->model_data->vref_fixed_mv) { + data->vref_mv = data->model_data->vref_fixed_mv; + return 0; + } + return 0; +} + static int aspeed_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; @@ -249,6 +260,10 @@ static int aspeed_adc_probe(struct platform_device *pdev) } reset_control_deassert(data->rst); + ret = aspeed_adc_vref_config(indio_dev); + if (ret) + goto vref_config_error; + if (data->model_data->wait_init_sequence) { /* Enable engine in normal mode. */ writel(FIELD_PREP(ASPEED_ADC_OP_MODE, @@ -297,6 +312,7 @@ iio_register_error: clk_disable_unprepare(data->clk_scaler->clk); clk_enable_error: poll_timeout_error: +vref_config_error: reset_control_assert(data->rst); reset_error: clk_hw_unregister_divider(data->clk_scaler); -- cgit v1.2.3-70-g09d2 From 9223bd0471bb078377a98cfc188dae47448f0456 Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Wed, 22 Sep 2021 16:15:13 +0800 Subject: iio: adc: aspeed: Use model_data to set clk scaler. This patch uses need_prescaler and scaler_bit_width to set the ADC clock scaler. Reported-by: kernel test robot Signed-off-by: Billy Tsai Link: https://lore.kernel.org/r/20210922081520.30580-5-billy_tsai@aspeedtech.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/aspeed_adc.c | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index c33563046f40..cae5ff905cad 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -210,9 +210,10 @@ static int aspeed_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; struct aspeed_adc_data *data; - const char *clk_parent_name; int ret; u32 adc_engine_control_reg_val; + unsigned long scaler_flags = 0; + char clk_name[32], clk_parent_name[32]; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data)); if (!indio_dev) @@ -228,24 +229,32 @@ static int aspeed_adc_probe(struct platform_device *pdev) /* Register ADC clock prescaler with source specified by device tree. */ spin_lock_init(&data->clk_lock); - clk_parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0); - - data->clk_prescaler = clk_hw_register_divider( - &pdev->dev, "prescaler", clk_parent_name, 0, - data->base + ASPEED_REG_CLOCK_CONTROL, - 17, 15, 0, &data->clk_lock); - if (IS_ERR(data->clk_prescaler)) - return PTR_ERR(data->clk_prescaler); - + snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), "%s", + of_clk_get_parent_name(pdev->dev.of_node, 0)); + + if (data->model_data->need_prescaler) { + snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-prescaler", + data->model_data->model_name); + data->clk_prescaler = clk_hw_register_divider( + &pdev->dev, clk_name, clk_parent_name, 0, + data->base + ASPEED_REG_CLOCK_CONTROL, 17, 15, 0, + &data->clk_lock); + if (IS_ERR(data->clk_prescaler)) + return PTR_ERR(data->clk_prescaler); + snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), + clk_name); + scaler_flags = CLK_SET_RATE_PARENT; + } /* * Register ADC clock scaler downstream from the prescaler. Allow rate * setting to adjust the prescaler as well. */ + snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-scaler", + data->model_data->model_name); data->clk_scaler = clk_hw_register_divider( - &pdev->dev, "scaler", "prescaler", - CLK_SET_RATE_PARENT, - data->base + ASPEED_REG_CLOCK_CONTROL, - 0, 10, 0, &data->clk_lock); + &pdev->dev, clk_name, clk_parent_name, scaler_flags, + data->base + ASPEED_REG_CLOCK_CONTROL, 0, + data->model_data->scaler_bit_width, 0, &data->clk_lock); if (IS_ERR(data->clk_scaler)) { ret = PTR_ERR(data->clk_scaler); goto scaler_error; @@ -317,7 +326,8 @@ vref_config_error: reset_error: clk_hw_unregister_divider(data->clk_scaler); scaler_error: - clk_hw_unregister_divider(data->clk_prescaler); + if (data->model_data->need_prescaler) + clk_hw_unregister_divider(data->clk_prescaler); return ret; } @@ -332,7 +342,8 @@ static int aspeed_adc_remove(struct platform_device *pdev) clk_disable_unprepare(data->clk_scaler->clk); reset_control_assert(data->rst); clk_hw_unregister_divider(data->clk_scaler); - clk_hw_unregister_divider(data->clk_prescaler); + if (data->model_data->need_prescaler) + clk_hw_unregister_divider(data->clk_prescaler); return 0; } -- cgit v1.2.3-70-g09d2 From 4c56572c26f5888ca70caa84f2e579346a21cfed Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Wed, 22 Sep 2021 16:15:14 +0800 Subject: iio: adc: aspeed: Use devm_add_action_or_reset. This patch use devm_add_action_or_reset to handle the error in probe phase. Signed-off-by: Billy Tsai Link: https://lore.kernel.org/r/20210922081520.30580-6-billy_tsai@aspeedtech.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/aspeed_adc.c | 113 +++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 58 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index cae5ff905cad..7c9ac725843b 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -195,6 +195,28 @@ static const struct iio_info aspeed_adc_iio_info = { .debugfs_reg_access = aspeed_adc_reg_access, }; +static void aspeed_adc_reset_assert(void *data) +{ + struct reset_control *rst = data; + + reset_control_assert(rst); +} + +static void aspeed_adc_clk_disable_unprepare(void *data) +{ + struct clk *clk = data; + + clk_disable_unprepare(clk); +} + +static void aspeed_adc_power_down(void *data) +{ + struct aspeed_adc_data *priv_data = data; + + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), + priv_data->base + ASPEED_REG_ENGINE_CONTROL); +} + static int aspeed_adc_vref_config(struct iio_dev *indio_dev) { struct aspeed_adc_data *data = iio_priv(indio_dev); @@ -235,7 +257,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (data->model_data->need_prescaler) { snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-prescaler", data->model_data->model_name); - data->clk_prescaler = clk_hw_register_divider( + data->clk_prescaler = devm_clk_hw_register_divider( &pdev->dev, clk_name, clk_parent_name, 0, data->base + ASPEED_REG_CLOCK_CONTROL, 17, 15, 0, &data->clk_lock); @@ -251,35 +273,41 @@ static int aspeed_adc_probe(struct platform_device *pdev) */ snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-scaler", data->model_data->model_name); - data->clk_scaler = clk_hw_register_divider( + data->clk_scaler = devm_clk_hw_register_divider( &pdev->dev, clk_name, clk_parent_name, scaler_flags, data->base + ASPEED_REG_CLOCK_CONTROL, 0, data->model_data->scaler_bit_width, 0, &data->clk_lock); - if (IS_ERR(data->clk_scaler)) { - ret = PTR_ERR(data->clk_scaler); - goto scaler_error; - } + if (IS_ERR(data->clk_scaler)) + return PTR_ERR(data->clk_scaler); data->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(data->rst)) { dev_err(&pdev->dev, "invalid or missing reset controller device tree entry"); - ret = PTR_ERR(data->rst); - goto reset_error; + return PTR_ERR(data->rst); } reset_control_deassert(data->rst); + ret = devm_add_action_or_reset(data->dev, aspeed_adc_reset_assert, + data->rst); + if (ret) + return ret; + ret = aspeed_adc_vref_config(indio_dev); if (ret) - goto vref_config_error; + return ret; - if (data->model_data->wait_init_sequence) { - /* Enable engine in normal mode. */ - writel(FIELD_PREP(ASPEED_ADC_OP_MODE, - ASPEED_ADC_OP_MODE_NORMAL) | - ASPEED_ADC_ENGINE_ENABLE, - data->base + ASPEED_REG_ENGINE_CONTROL); + /* Enable engine in normal mode. */ + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | + ASPEED_ADC_ENGINE_ENABLE, + data->base + ASPEED_REG_ENGINE_CONTROL); + + ret = devm_add_action_or_reset(data->dev, aspeed_adc_power_down, + data); + if (ret) + return ret; + if (data->model_data->wait_init_sequence) { /* Wait for initial sequence complete. */ ret = readl_poll_timeout(data->base + ASPEED_REG_ENGINE_CONTROL, adc_engine_control_reg_val, @@ -288,18 +316,23 @@ static int aspeed_adc_probe(struct platform_device *pdev) ASPEED_ADC_INIT_POLLING_TIME, ASPEED_ADC_INIT_TIMEOUT); if (ret) - goto poll_timeout_error; + return ret; } - /* Start all channels in normal mode. */ ret = clk_prepare_enable(data->clk_scaler->clk); if (ret) - goto clk_enable_error; + return ret; + ret = devm_add_action_or_reset(data->dev, + aspeed_adc_clk_disable_unprepare, + data->clk_scaler->clk); + if (ret) + return ret; + + /* Start all channels in normal mode. */ adc_engine_control_reg_val = - ASPEED_ADC_CTRL_CHANNEL | - FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | - ASPEED_ADC_ENGINE_ENABLE; + readl(data->base + ASPEED_REG_ENGINE_CONTROL); + adc_engine_control_reg_val |= ASPEED_ADC_CTRL_CHANNEL; writel(adc_engine_control_reg_val, data->base + ASPEED_REG_ENGINE_CONTROL); @@ -309,45 +342,10 @@ static int aspeed_adc_probe(struct platform_device *pdev) indio_dev->channels = aspeed_adc_iio_channels; indio_dev->num_channels = data->model_data->num_channels; - ret = iio_device_register(indio_dev); - if (ret) - goto iio_register_error; - - return 0; - -iio_register_error: - writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), - data->base + ASPEED_REG_ENGINE_CONTROL); - clk_disable_unprepare(data->clk_scaler->clk); -clk_enable_error: -poll_timeout_error: -vref_config_error: - reset_control_assert(data->rst); -reset_error: - clk_hw_unregister_divider(data->clk_scaler); -scaler_error: - if (data->model_data->need_prescaler) - clk_hw_unregister_divider(data->clk_prescaler); + ret = devm_iio_device_register(data->dev, indio_dev); return ret; } -static int aspeed_adc_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - struct aspeed_adc_data *data = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), - data->base + ASPEED_REG_ENGINE_CONTROL); - clk_disable_unprepare(data->clk_scaler->clk); - reset_control_assert(data->rst); - clk_hw_unregister_divider(data->clk_scaler); - if (data->model_data->need_prescaler) - clk_hw_unregister_divider(data->clk_prescaler); - - return 0; -} - static const struct aspeed_adc_model_data ast2400_model_data = { .model_name = "ast2400-adc", .vref_fixed_mv = 2500, @@ -378,7 +376,6 @@ MODULE_DEVICE_TABLE(of, aspeed_adc_matches); static struct platform_driver aspeed_adc_driver = { .probe = aspeed_adc_probe, - .remove = aspeed_adc_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = aspeed_adc_matches, -- cgit v1.2.3-70-g09d2 From 1b5ceb55fec2aac929672eb8e24f98a12a10f695 Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Wed, 22 Sep 2021 16:15:15 +0800 Subject: iio: adc: aspeed: Support ast2600 adc. Make driver to support ast2600 adc device. - Use shared reset controller - Complete the vref configure function - Add the model data for ast2600 adc Signed-off-by: Billy Tsai Link: https://lore.kernel.org/r/20210922081520.30580-7-billy_tsai@aspeedtech.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/aspeed_adc.c | 101 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 96 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 7c9ac725843b..846fead557dc 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Aspeed AST2400/2500 ADC + * Aspeed AST2400/2500/2600 ADC * * Copyright (C) 2017 Google, Inc. * Copyright (C) 2021 Aspeed Technology Inc. @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -81,6 +82,7 @@ struct aspeed_adc_model_data { struct aspeed_adc_data { struct device *dev; const struct aspeed_adc_model_data *model_data; + struct regulator *regulator; void __iomem *base; spinlock_t clk_lock; struct clk_hw *clk_prescaler; @@ -217,14 +219,79 @@ static void aspeed_adc_power_down(void *data) priv_data->base + ASPEED_REG_ENGINE_CONTROL); } +static void aspeed_adc_reg_disable(void *data) +{ + struct regulator *reg = data; + + regulator_disable(reg); +} + static int aspeed_adc_vref_config(struct iio_dev *indio_dev) { struct aspeed_adc_data *data = iio_priv(indio_dev); + int ret; + u32 adc_engine_control_reg_val; if (data->model_data->vref_fixed_mv) { data->vref_mv = data->model_data->vref_fixed_mv; return 0; } + adc_engine_control_reg_val = + readl(data->base + ASPEED_REG_ENGINE_CONTROL); + data->regulator = devm_regulator_get_optional(data->dev, "vref"); + if (!IS_ERR(data->regulator)) { + ret = regulator_enable(data->regulator); + if (ret) + return ret; + ret = devm_add_action_or_reset( + data->dev, aspeed_adc_reg_disable, data->regulator); + if (ret) + return ret; + data->vref_mv = regulator_get_voltage(data->regulator); + /* Conversion from uV to mV */ + data->vref_mv /= 1000; + if ((data->vref_mv >= 1550) && (data->vref_mv <= 2700)) + writel(adc_engine_control_reg_val | + FIELD_PREP( + ASPEED_ADC_REF_VOLTAGE, + ASPEED_ADC_REF_VOLTAGE_EXT_HIGH), + data->base + ASPEED_REG_ENGINE_CONTROL); + else if ((data->vref_mv >= 900) && (data->vref_mv <= 1650)) + writel(adc_engine_control_reg_val | + FIELD_PREP( + ASPEED_ADC_REF_VOLTAGE, + ASPEED_ADC_REF_VOLTAGE_EXT_LOW), + data->base + ASPEED_REG_ENGINE_CONTROL); + else { + dev_err(data->dev, "Regulator voltage %d not support", + data->vref_mv); + return -EOPNOTSUPP; + } + } else { + if (PTR_ERR(data->regulator) != -ENODEV) + return PTR_ERR(data->regulator); + data->vref_mv = 2500000; + of_property_read_u32(data->dev->of_node, + "aspeed,int-vref-microvolt", + &data->vref_mv); + /* Conversion from uV to mV */ + data->vref_mv /= 1000; + if (data->vref_mv == 2500) + writel(adc_engine_control_reg_val | + FIELD_PREP(ASPEED_ADC_REF_VOLTAGE, + ASPEED_ADC_REF_VOLTAGE_2500mV), + data->base + ASPEED_REG_ENGINE_CONTROL); + else if (data->vref_mv == 1200) + writel(adc_engine_control_reg_val | + FIELD_PREP(ASPEED_ADC_REF_VOLTAGE, + ASPEED_ADC_REF_VOLTAGE_1200mV), + data->base + ASPEED_REG_ENGINE_CONTROL); + else { + dev_err(data->dev, "Voltage %d not support", data->vref_mv); + return -EOPNOTSUPP; + } + } + return 0; } @@ -280,7 +347,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (IS_ERR(data->clk_scaler)) return PTR_ERR(data->clk_scaler); - data->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); + data->rst = devm_reset_control_get_shared(&pdev->dev, NULL); if (IS_ERR(data->rst)) { dev_err(&pdev->dev, "invalid or missing reset controller device tree entry"); @@ -297,9 +364,13 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (ret) return ret; + adc_engine_control_reg_val = + readl(data->base + ASPEED_REG_ENGINE_CONTROL); + adc_engine_control_reg_val |= + FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | + ASPEED_ADC_ENGINE_ENABLE; /* Enable engine in normal mode. */ - writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | - ASPEED_ADC_ENGINE_ENABLE, + writel(adc_engine_control_reg_val, data->base + ASPEED_REG_ENGINE_CONTROL); ret = devm_add_action_or_reset(data->dev, aspeed_adc_power_down, @@ -367,9 +438,29 @@ static const struct aspeed_adc_model_data ast2500_model_data = { .num_channels = 16, }; +static const struct aspeed_adc_model_data ast2600_adc0_model_data = { + .model_name = "ast2600-adc0", + .min_sampling_rate = 10000, + .max_sampling_rate = 500000, + .wait_init_sequence = true, + .scaler_bit_width = 16, + .num_channels = 8, +}; + +static const struct aspeed_adc_model_data ast2600_adc1_model_data = { + .model_name = "ast2600-adc1", + .min_sampling_rate = 10000, + .max_sampling_rate = 500000, + .wait_init_sequence = true, + .scaler_bit_width = 16, + .num_channels = 8, +}; + static const struct of_device_id aspeed_adc_matches[] = { { .compatible = "aspeed,ast2400-adc", .data = &ast2400_model_data }, { .compatible = "aspeed,ast2500-adc", .data = &ast2500_model_data }, + { .compatible = "aspeed,ast2600-adc0", .data = &ast2600_adc0_model_data }, + { .compatible = "aspeed,ast2600-adc1", .data = &ast2600_adc1_model_data }, {}, }; MODULE_DEVICE_TABLE(of, aspeed_adc_matches); @@ -385,5 +476,5 @@ static struct platform_driver aspeed_adc_driver = { module_platform_driver(aspeed_adc_driver); MODULE_AUTHOR("Rick Altherr "); -MODULE_DESCRIPTION("Aspeed AST2400/2500 ADC Driver"); +MODULE_DESCRIPTION("Aspeed AST2400/2500/2600 ADC Driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From 90f9647753de30708da3891296812c4ec97cb404 Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Wed, 22 Sep 2021 16:15:16 +0800 Subject: iio: adc: aspeed: Fix the calculate error of clock. The ADC clock formula is ast2400/2500: ADC clock period = PCLK * 2 * (ADC0C[31:17] + 1) * (ADC0C[9:0] + 1) ast2600: ADC clock period = PCLK * 2 * (ADC0C[15:0] + 1) They all have one fixed divided 2 and the legacy driver didn't handle it. This patch register the fixed factory clock device as the parent of ADC clock scaler to fix this issue. Signed-off-by: Billy Tsai Link: https://lore.kernel.org/r/20210922081520.30580-8-billy_tsai@aspeedtech.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/aspeed_adc.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 846fead557dc..da530c420211 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -4,6 +4,12 @@ * * Copyright (C) 2017 Google, Inc. * Copyright (C) 2021 Aspeed Technology Inc. + * + * ADC clock formula: + * Ast2400/Ast2500: + * clock period = period of PCLK * 2 * (ADC0C[31:17] + 1) * (ADC0C[9:0] + 1) + * Ast2600: + * clock period = period of PCLK * 2 * (ADC0C[15:0] + 1) */ #include @@ -85,6 +91,7 @@ struct aspeed_adc_data { struct regulator *regulator; void __iomem *base; spinlock_t clk_lock; + struct clk_hw *fixed_div_clk; struct clk_hw *clk_prescaler; struct clk_hw *clk_scaler; struct reset_control *rst; @@ -197,6 +204,13 @@ static const struct iio_info aspeed_adc_iio_info = { .debugfs_reg_access = aspeed_adc_reg_access, }; +static void aspeed_adc_unregister_fixed_divider(void *data) +{ + struct clk_hw *clk = data; + + clk_hw_unregister_fixed_factor(clk); +} + static void aspeed_adc_reset_assert(void *data) { struct reset_control *rst = data; @@ -320,6 +334,19 @@ static int aspeed_adc_probe(struct platform_device *pdev) spin_lock_init(&data->clk_lock); snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), "%s", of_clk_get_parent_name(pdev->dev.of_node, 0)); + snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-fixed-div", + data->model_data->model_name); + data->fixed_div_clk = clk_hw_register_fixed_factor( + &pdev->dev, clk_name, clk_parent_name, 0, 1, 2); + if (IS_ERR(data->fixed_div_clk)) + return PTR_ERR(data->fixed_div_clk); + + ret = devm_add_action_or_reset(data->dev, + aspeed_adc_unregister_fixed_divider, + data->fixed_div_clk); + if (ret) + return ret; + snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), clk_name); if (data->model_data->need_prescaler) { snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-prescaler", -- cgit v1.2.3-70-g09d2 From 13d4f9df333b8b665ab8b4429c192eb788bcd92f Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Wed, 22 Sep 2021 16:15:17 +0800 Subject: iio: adc: aspeed: Add func to set sampling rate. Add the function to set the sampling rate and keep the sampling period for a driver used to wait the fresh value. In addition, since the ADC clock is required when initializing the ADC device, move clk_prepare_enable ahead of the initialization phase. Signed-off-by: Billy Tsai Link: https://lore.kernel.org/r/20210922081520.30580-9-billy_tsai@aspeedtech.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/aspeed_adc.c | 58 +++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index da530c420211..0b216002e0ed 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -73,6 +73,12 @@ #define ASPEED_ADC_INIT_POLLING_TIME 500 #define ASPEED_ADC_INIT_TIMEOUT 500000 +/* + * When the sampling rate is too high, the ADC may not have enough charging + * time, resulting in a low voltage value. Thus, the default uses a slow + * sampling rate for most use cases. + */ +#define ASPEED_ADC_DEF_SAMPLING_RATE 65000 struct aspeed_adc_model_data { const char *model_name; @@ -96,6 +102,7 @@ struct aspeed_adc_data { struct clk_hw *clk_scaler; struct reset_control *rst; int vref_mv; + u32 sample_period_ns; }; #define ASPEED_CHAN(_idx, _data_reg_addr) { \ @@ -127,6 +134,24 @@ static const struct iio_chan_spec aspeed_adc_iio_channels[] = { ASPEED_CHAN(15, 0x2E), }; +static int aspeed_adc_set_sampling_rate(struct iio_dev *indio_dev, u32 rate) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + + if (rate < data->model_data->min_sampling_rate || + rate > data->model_data->max_sampling_rate) + return -EINVAL; + /* Each sampling needs 12 clocks to convert.*/ + clk_set_rate(data->clk_scaler->clk, rate * ASPEED_CLOCKS_PER_SAMPLE); + rate = clk_get_rate(data->clk_scaler->clk); + data->sample_period_ns = DIV_ROUND_UP_ULL( + (u64)NSEC_PER_SEC * ASPEED_CLOCKS_PER_SAMPLE, rate); + dev_dbg(data->dev, "Adc clock = %d sample period = %d ns", rate, + data->sample_period_ns); + + return 0; +} + static int aspeed_adc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -157,17 +182,9 @@ static int aspeed_adc_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { - struct aspeed_adc_data *data = iio_priv(indio_dev); - switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: - if (val < data->model_data->min_sampling_rate || - val > data->model_data->max_sampling_rate) - return -EINVAL; - - clk_set_rate(data->clk_scaler->clk, - val * ASPEED_CLOCKS_PER_SAMPLE); - return 0; + return aspeed_adc_set_sampling_rate(indio_dev, val); case IIO_CHAN_INFO_SCALE: case IIO_CHAN_INFO_RAW: @@ -391,6 +408,19 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (ret) return ret; + ret = clk_prepare_enable(data->clk_scaler->clk); + if (ret) + return ret; + ret = devm_add_action_or_reset(data->dev, + aspeed_adc_clk_disable_unprepare, + data->clk_scaler->clk); + if (ret) + return ret; + ret = aspeed_adc_set_sampling_rate(indio_dev, + ASPEED_ADC_DEF_SAMPLING_RATE); + if (ret) + return ret; + adc_engine_control_reg_val = readl(data->base + ASPEED_REG_ENGINE_CONTROL); adc_engine_control_reg_val |= @@ -417,16 +447,6 @@ static int aspeed_adc_probe(struct platform_device *pdev) return ret; } - ret = clk_prepare_enable(data->clk_scaler->clk); - if (ret) - return ret; - - ret = devm_add_action_or_reset(data->dev, - aspeed_adc_clk_disable_unprepare, - data->clk_scaler->clk); - if (ret) - return ret; - /* Start all channels in normal mode. */ adc_engine_control_reg_val = readl(data->base + ASPEED_REG_ENGINE_CONTROL); -- cgit v1.2.3-70-g09d2 From f2836e8c4c2e88c45de8f98b1653eb064c4ef333 Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Wed, 22 Sep 2021 16:15:18 +0800 Subject: iio: adc: aspeed: Add compensation phase. This patch adds a compensation phase to improve the accuracy of ADC measurement. This is the built-in function through input half of the reference voltage to get the ADC offset. Signed-off-by: Billy Tsai Link: https://lore.kernel.org/r/20210922081520.30580-10-billy_tsai@aspeedtech.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/aspeed_adc.c | 54 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 0b216002e0ed..e10f4455abcf 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -103,6 +103,7 @@ struct aspeed_adc_data { struct reset_control *rst; int vref_mv; u32 sample_period_ns; + int cv; }; #define ASPEED_CHAN(_idx, _data_reg_addr) { \ @@ -112,7 +113,8 @@ struct aspeed_adc_data { .address = (_data_reg_addr), \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ } static const struct iio_chan_spec aspeed_adc_iio_channels[] = { @@ -134,6 +136,51 @@ static const struct iio_chan_spec aspeed_adc_iio_channels[] = { ASPEED_CHAN(15, 0x2E), }; +static int aspeed_adc_compensation(struct iio_dev *indio_dev) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + u32 index, adc_raw = 0; + u32 adc_engine_control_reg_val; + + adc_engine_control_reg_val = + readl(data->base + ASPEED_REG_ENGINE_CONTROL); + adc_engine_control_reg_val &= ~ASPEED_ADC_OP_MODE; + adc_engine_control_reg_val |= + (FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | + ASPEED_ADC_ENGINE_ENABLE); + /* + * Enable compensating sensing: + * After that, the input voltage of ADC will force to half of the reference + * voltage. So the expected reading raw data will become half of the max + * value. We can get compensating value = 0x200 - ADC read raw value. + * It is recommended to average at least 10 samples to get a final CV. + */ + writel(adc_engine_control_reg_val | ASPEED_ADC_CTRL_COMPENSATION | + ASPEED_ADC_CTRL_CHANNEL_ENABLE(0), + data->base + ASPEED_REG_ENGINE_CONTROL); + /* + * After enable compensating sensing mode need to wait some time for ADC stable + * Experiment result is 1ms. + */ + mdelay(1); + + for (index = 0; index < 16; index++) { + /* + * Waiting for the sampling period ensures that the value acquired + * is fresh each time. + */ + ndelay(data->sample_period_ns); + adc_raw += readw(data->base + aspeed_adc_iio_channels[0].address); + } + adc_raw >>= 4; + data->cv = BIT(ASPEED_RESOLUTION_BITS - 1) - adc_raw; + writel(adc_engine_control_reg_val, + data->base + ASPEED_REG_ENGINE_CONTROL); + dev_dbg(data->dev, "Compensating value = %d\n", data->cv); + + return 0; +} + static int aspeed_adc_set_sampling_rate(struct iio_dev *indio_dev, u32 rate) { struct aspeed_adc_data *data = iio_priv(indio_dev); @@ -163,6 +210,10 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, *val = readw(data->base + chan->address); return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + *val = data->cv; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: *val = data->vref_mv; *val2 = ASPEED_RESOLUTION_BITS; @@ -447,6 +498,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) return ret; } + aspeed_adc_compensation(indio_dev); /* Start all channels in normal mode. */ adc_engine_control_reg_val = readl(data->base + ASPEED_REG_ENGINE_CONTROL); -- cgit v1.2.3-70-g09d2 From df05f384a7e387797c30c2fdf7e4073543ebb57d Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Wed, 22 Sep 2021 16:15:19 +0800 Subject: iio: adc: aspeed: Support battery sensing. In ast2600, ADC integrate dividing circuit at last input channel for battery sensing. This patch use the dts property "battery-sensing" to enable this feature makes the last channel of each adc can tolerance higher voltage than reference voltage. The offset interface of ch7 will be separated when enabling the battery sensing mode. Signed-off-by: Billy Tsai Link: https://lore.kernel.org/r/20210922081520.30580-11-billy_tsai@aspeedtech.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/aspeed_adc.c | 81 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index e10f4455abcf..72c7e0ea0769 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -87,10 +87,16 @@ struct aspeed_adc_model_data { unsigned int vref_fixed_mv; bool wait_init_sequence; bool need_prescaler; + bool bat_sense_sup; u8 scaler_bit_width; unsigned int num_channels; }; +struct adc_gain { + u8 mult; + u8 div; +}; + struct aspeed_adc_data { struct device *dev; const struct aspeed_adc_model_data *model_data; @@ -104,6 +110,8 @@ struct aspeed_adc_data { int vref_mv; u32 sample_period_ns; int cv; + bool battery_sensing; + struct adc_gain battery_mode_gain; }; #define ASPEED_CHAN(_idx, _data_reg_addr) { \ @@ -136,6 +144,27 @@ static const struct iio_chan_spec aspeed_adc_iio_channels[] = { ASPEED_CHAN(15, 0x2E), }; +#define ASPEED_BAT_CHAN(_idx, _data_reg_addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_idx), \ + .address = (_data_reg_addr), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ +} +static const struct iio_chan_spec aspeed_adc_iio_bat_channels[] = { + ASPEED_CHAN(0, 0x10), + ASPEED_CHAN(1, 0x12), + ASPEED_CHAN(2, 0x14), + ASPEED_CHAN(3, 0x16), + ASPEED_CHAN(4, 0x18), + ASPEED_CHAN(5, 0x1A), + ASPEED_CHAN(6, 0x1C), + ASPEED_BAT_CHAN(7, 0x1E), +}; + static int aspeed_adc_compensation(struct iio_dev *indio_dev) { struct aspeed_adc_data *data = iio_priv(indio_dev); @@ -204,14 +233,39 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct aspeed_adc_data *data = iio_priv(indio_dev); + u32 adc_engine_control_reg_val; switch (mask) { case IIO_CHAN_INFO_RAW: - *val = readw(data->base + chan->address); + if (data->battery_sensing && chan->channel == 7) { + adc_engine_control_reg_val = + readl(data->base + ASPEED_REG_ENGINE_CONTROL); + writel(adc_engine_control_reg_val | + FIELD_PREP(ASPEED_ADC_CH7_MODE, + ASPEED_ADC_CH7_BAT) | + ASPEED_ADC_BAT_SENSING_ENABLE, + data->base + ASPEED_REG_ENGINE_CONTROL); + /* + * After enable battery sensing mode need to wait some time for adc stable + * Experiment result is 1ms. + */ + mdelay(1); + *val = readw(data->base + chan->address); + *val = (*val * data->battery_mode_gain.mult) / + data->battery_mode_gain.div; + /* Restore control register value */ + writel(adc_engine_control_reg_val, + data->base + ASPEED_REG_ENGINE_CONTROL); + } else + *val = readw(data->base + chan->address); return IIO_VAL_INT; case IIO_CHAN_INFO_OFFSET: - *val = data->cv; + if (data->battery_sensing && chan->channel == 7) + *val = (data->cv * data->battery_mode_gain.mult) / + data->battery_mode_gain.div; + else + *val = data->cv; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: @@ -459,6 +513,23 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (ret) return ret; + if (of_find_property(data->dev->of_node, "aspeed,battery-sensing", + NULL)) { + if (data->model_data->bat_sense_sup) { + data->battery_sensing = 1; + if (readl(data->base + ASPEED_REG_ENGINE_CONTROL) & + ASPEED_ADC_BAT_SENSING_DIV) { + data->battery_mode_gain.mult = 3; + data->battery_mode_gain.div = 1; + } else { + data->battery_mode_gain.mult = 3; + data->battery_mode_gain.div = 2; + } + } else + dev_warn(&pdev->dev, + "Failed to enable battey-sensing mode\n"); + } + ret = clk_prepare_enable(data->clk_scaler->clk); if (ret) return ret; @@ -509,7 +580,9 @@ static int aspeed_adc_probe(struct platform_device *pdev) indio_dev->name = data->model_data->model_name; indio_dev->info = &aspeed_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = aspeed_adc_iio_channels; + indio_dev->channels = data->battery_sensing ? + aspeed_adc_iio_bat_channels : + aspeed_adc_iio_channels; indio_dev->num_channels = data->model_data->num_channels; ret = devm_iio_device_register(data->dev, indio_dev); @@ -542,6 +615,7 @@ static const struct aspeed_adc_model_data ast2600_adc0_model_data = { .min_sampling_rate = 10000, .max_sampling_rate = 500000, .wait_init_sequence = true, + .bat_sense_sup = true, .scaler_bit_width = 16, .num_channels = 8, }; @@ -551,6 +625,7 @@ static const struct aspeed_adc_model_data ast2600_adc1_model_data = { .min_sampling_rate = 10000, .max_sampling_rate = 500000, .wait_init_sequence = true, + .bat_sense_sup = true, .scaler_bit_width = 16, .num_channels = 8, }; -- cgit v1.2.3-70-g09d2 From d0a4c17b40736b368f1f26602ad162e24b4108e7 Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Wed, 22 Sep 2021 16:15:20 +0800 Subject: iio: adc: aspeed: Get and set trimming data. The ADC controller has a trimming register for fine-tune the reference voltage. The trimming value comes from the OTP register which will be written during chip production. This patch will read this OTP value and configure it to the ADC register when the ADC controller probes and using dts property "aspeed,trim-data-valid" to determine whether to execute this flow. Signed-off-by: Billy Tsai Link: https://lore.kernel.org/r/20210922081520.30580-12-billy_tsai@aspeedtech.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/aspeed_adc.c | 71 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 72c7e0ea0769..9e2c85c3cbe9 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include @@ -80,6 +82,11 @@ */ #define ASPEED_ADC_DEF_SAMPLING_RATE 65000 +struct aspeed_adc_trim_locate { + const unsigned int offset; + const unsigned int field; +}; + struct aspeed_adc_model_data { const char *model_name; unsigned int min_sampling_rate; // Hz @@ -90,6 +97,7 @@ struct aspeed_adc_model_data { bool bat_sense_sup; u8 scaler_bit_width; unsigned int num_channels; + const struct aspeed_adc_trim_locate *trim_locate; }; struct adc_gain { @@ -165,6 +173,44 @@ static const struct iio_chan_spec aspeed_adc_iio_bat_channels[] = { ASPEED_BAT_CHAN(7, 0x1E), }; +static int aspeed_adc_set_trim_data(struct iio_dev *indio_dev) +{ + struct device_node *syscon; + struct regmap *scu; + u32 scu_otp, trimming_val; + struct aspeed_adc_data *data = iio_priv(indio_dev); + + syscon = of_find_node_by_name(NULL, "syscon"); + if (syscon == NULL) { + dev_warn(data->dev, "Couldn't find syscon node\n"); + return -EOPNOTSUPP; + } + scu = syscon_node_to_regmap(syscon); + if (IS_ERR(scu)) { + dev_warn(data->dev, "Failed to get syscon regmap\n"); + return -EOPNOTSUPP; + } + if (data->model_data->trim_locate) { + if (regmap_read(scu, data->model_data->trim_locate->offset, + &scu_otp)) { + dev_warn(data->dev, + "Failed to get adc trimming data\n"); + trimming_val = 0x8; + } else { + trimming_val = + ((scu_otp) & + (data->model_data->trim_locate->field)) >> + __ffs(data->model_data->trim_locate->field); + } + dev_dbg(data->dev, + "trimming val = %d, offset = %08x, fields = %08x\n", + trimming_val, data->model_data->trim_locate->offset, + data->model_data->trim_locate->field); + writel(trimming_val, data->base + ASPEED_REG_COMPENSATION_TRIM); + } + return 0; +} + static int aspeed_adc_compensation(struct iio_dev *indio_dev) { struct aspeed_adc_data *data = iio_priv(indio_dev); @@ -513,6 +559,13 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (ret) return ret; + if (of_find_property(data->dev->of_node, "aspeed,trim-data-valid", + NULL)) { + ret = aspeed_adc_set_trim_data(indio_dev); + if (ret) + return ret; + } + if (of_find_property(data->dev->of_node, "aspeed,battery-sensing", NULL)) { if (data->model_data->bat_sense_sup) { @@ -589,6 +642,21 @@ static int aspeed_adc_probe(struct platform_device *pdev) return ret; } +static const struct aspeed_adc_trim_locate ast2500_adc_trim = { + .offset = 0x154, + .field = GENMASK(31, 28), +}; + +static const struct aspeed_adc_trim_locate ast2600_adc0_trim = { + .offset = 0x5d0, + .field = GENMASK(3, 0), +}; + +static const struct aspeed_adc_trim_locate ast2600_adc1_trim = { + .offset = 0x5d0, + .field = GENMASK(7, 4), +}; + static const struct aspeed_adc_model_data ast2400_model_data = { .model_name = "ast2400-adc", .vref_fixed_mv = 2500, @@ -608,6 +676,7 @@ static const struct aspeed_adc_model_data ast2500_model_data = { .need_prescaler = true, .scaler_bit_width = 10, .num_channels = 16, + .trim_locate = &ast2500_adc_trim, }; static const struct aspeed_adc_model_data ast2600_adc0_model_data = { @@ -618,6 +687,7 @@ static const struct aspeed_adc_model_data ast2600_adc0_model_data = { .bat_sense_sup = true, .scaler_bit_width = 16, .num_channels = 8, + .trim_locate = &ast2600_adc0_trim, }; static const struct aspeed_adc_model_data ast2600_adc1_model_data = { @@ -628,6 +698,7 @@ static const struct aspeed_adc_model_data ast2600_adc1_model_data = { .bat_sense_sup = true, .scaler_bit_width = 16, .num_channels = 8, + .trim_locate = &ast2600_adc1_trim, }; static const struct of_device_id aspeed_adc_matches[] = { -- cgit v1.2.3-70-g09d2 From 131fb9f2b96f671679e49264ac4ae2f002e97074 Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Mon, 20 Sep 2021 13:42:20 +0200 Subject: iio: accel: fxls8962af: add threshold event handling Add event channels that control the creation of motion events. Signed-off-by: Sean Nyekjaer Link: https://lore.kernel.org/r/20210920114221.1595543-1-sean@geanix.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/fxls8962af-core.c | 301 +++++++++++++++++++++++++++++++++++- 1 file changed, 298 insertions(+), 3 deletions(-) diff --git a/drivers/iio/accel/fxls8962af-core.c b/drivers/iio/accel/fxls8962af-core.c index 0019f1ea7df2..f3d7b3aa9d0e 100644 --- a/drivers/iio/accel/fxls8962af-core.c +++ b/drivers/iio/accel/fxls8962af-core.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -30,6 +31,7 @@ #define FXLS8962AF_INT_STATUS 0x00 #define FXLS8962AF_INT_STATUS_SRC_BOOT BIT(0) +#define FXLS8962AF_INT_STATUS_SRC_SDCD_OT BIT(4) #define FXLS8962AF_INT_STATUS_SRC_BUF BIT(5) #define FXLS8962AF_INT_STATUS_SRC_DRDY BIT(7) #define FXLS8962AF_TEMP_OUT 0x01 @@ -73,6 +75,7 @@ #define FXLS8962AF_ASLP_COUNT_LSB 0x1e #define FXLS8962AF_INT_EN 0x20 +#define FXLS8962AF_INT_EN_SDCD_OT_EN BIT(5) #define FXLS8962AF_INT_EN_BUF_EN BIT(6) #define FXLS8962AF_INT_PIN_SEL 0x21 #define FXLS8962AF_INT_PIN_SEL_MASK GENMASK(7, 0) @@ -96,9 +99,21 @@ #define FXLS8962AF_ORIENT_THS_REG 0x2c #define FXLS8962AF_SDCD_INT_SRC1 0x2d +#define FXLS8962AF_SDCD_INT_SRC1_X_OT BIT(5) +#define FXLS8962AF_SDCD_INT_SRC1_X_POL BIT(4) +#define FXLS8962AF_SDCD_INT_SRC1_Y_OT BIT(3) +#define FXLS8962AF_SDCD_INT_SRC1_Y_POL BIT(2) +#define FXLS8962AF_SDCD_INT_SRC1_Z_OT BIT(1) +#define FXLS8962AF_SDCD_INT_SRC1_Z_POL BIT(0) #define FXLS8962AF_SDCD_INT_SRC2 0x2e #define FXLS8962AF_SDCD_CONFIG1 0x2f +#define FXLS8962AF_SDCD_CONFIG1_Z_OT_EN BIT(3) +#define FXLS8962AF_SDCD_CONFIG1_Y_OT_EN BIT(4) +#define FXLS8962AF_SDCD_CONFIG1_X_OT_EN BIT(5) +#define FXLS8962AF_SDCD_CONFIG1_OT_ELE BIT(7) #define FXLS8962AF_SDCD_CONFIG2 0x30 +#define FXLS8962AF_SDCD_CONFIG2_SDCD_EN BIT(7) +#define FXLS8962AF_SC2_REF_UPDM_AC GENMASK(6, 5) #define FXLS8962AF_SDCD_OT_DBCNT 0x31 #define FXLS8962AF_SDCD_WT_DBCNT 0x32 #define FXLS8962AF_SDCD_LTHS_LSB 0x33 @@ -152,6 +167,9 @@ struct fxls8962af_data { int64_t timestamp, old_timestamp; /* Only used in hw fifo mode. */ struct iio_mount_matrix orientation; u8 watermark; + u8 enable_event; + u16 lower_thres; + u16 upper_thres; }; const struct regmap_config fxls8962af_regmap_conf = { @@ -238,7 +256,7 @@ static int fxls8962af_get_out(struct fxls8962af_data *data, } ret = regmap_bulk_read(data->regmap, chan->address, - &raw_val, (chan->scan_type.storagebits / 8)); + &raw_val, sizeof(data->lower_thres)); if (!is_active) fxls8962af_power_off(data); @@ -451,6 +469,15 @@ static int fxls8962af_write_raw(struct iio_dev *indio_dev, } } +static int fxls8962af_event_setup(struct fxls8962af_data *data, int state) +{ + /* Enable wakeup interrupt */ + int mask = FXLS8962AF_INT_EN_SDCD_OT_EN; + int value = state ? mask : 0; + + return regmap_update_bits(data->regmap, FXLS8962AF_INT_EN, mask, value); +} + static int fxls8962af_set_watermark(struct iio_dev *indio_dev, unsigned val) { struct fxls8962af_data *data = iio_priv(indio_dev); @@ -463,6 +490,217 @@ static int fxls8962af_set_watermark(struct iio_dev *indio_dev, unsigned val) return 0; } +static int __fxls8962af_set_thresholds(struct fxls8962af_data *data, + const struct iio_chan_spec *chan, + enum iio_event_direction dir, + int val) +{ + switch (dir) { + case IIO_EV_DIR_FALLING: + data->lower_thres = val; + return regmap_bulk_write(data->regmap, FXLS8962AF_SDCD_LTHS_LSB, + &data->lower_thres, sizeof(data->lower_thres)); + case IIO_EV_DIR_RISING: + data->upper_thres = val; + return regmap_bulk_write(data->regmap, FXLS8962AF_SDCD_UTHS_LSB, + &data->upper_thres, sizeof(data->upper_thres)); + default: + return -EINVAL; + } +} + +static int fxls8962af_read_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + int ret; + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + switch (dir) { + case IIO_EV_DIR_FALLING: + ret = regmap_bulk_read(data->regmap, FXLS8962AF_SDCD_LTHS_LSB, + &data->lower_thres, sizeof(data->lower_thres)); + if (ret) + return ret; + + *val = sign_extend32(data->lower_thres, chan->scan_type.realbits - 1); + return IIO_VAL_INT; + case IIO_EV_DIR_RISING: + ret = regmap_bulk_read(data->regmap, FXLS8962AF_SDCD_UTHS_LSB, + &data->upper_thres, sizeof(data->upper_thres)); + if (ret) + return ret; + + *val = sign_extend32(data->upper_thres, chan->scan_type.realbits - 1); + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int fxls8962af_write_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + int ret, val_masked; + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + if (val < -2048 || val > 2047) + return -EINVAL; + + if (data->enable_event) + return -EBUSY; + + val_masked = val & GENMASK(11, 0); + if (fxls8962af_is_active(data)) { + ret = fxls8962af_standby(data); + if (ret) + return ret; + + ret = __fxls8962af_set_thresholds(data, chan, dir, val_masked); + if (ret) + return ret; + + return fxls8962af_active(data); + } else { + return __fxls8962af_set_thresholds(data, chan, dir, val_masked); + } +} + +static int +fxls8962af_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + switch (chan->channel2) { + case IIO_MOD_X: + return !!(FXLS8962AF_SDCD_CONFIG1_X_OT_EN & data->enable_event); + case IIO_MOD_Y: + return !!(FXLS8962AF_SDCD_CONFIG1_Y_OT_EN & data->enable_event); + case IIO_MOD_Z: + return !!(FXLS8962AF_SDCD_CONFIG1_Z_OT_EN & data->enable_event); + default: + return -EINVAL; + } +} + +static int +fxls8962af_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, int state) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + u8 enable_event, enable_bits; + int ret, value; + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + switch (chan->channel2) { + case IIO_MOD_X: + enable_bits = FXLS8962AF_SDCD_CONFIG1_X_OT_EN; + break; + case IIO_MOD_Y: + enable_bits = FXLS8962AF_SDCD_CONFIG1_Y_OT_EN; + break; + case IIO_MOD_Z: + enable_bits = FXLS8962AF_SDCD_CONFIG1_Z_OT_EN; + break; + default: + return -EINVAL; + } + + if (state) + enable_event = data->enable_event | enable_bits; + else + enable_event = data->enable_event & ~enable_bits; + + if (data->enable_event == enable_event) + return 0; + + ret = fxls8962af_standby(data); + if (ret) + return ret; + + /* Enable events */ + value = enable_event | FXLS8962AF_SDCD_CONFIG1_OT_ELE; + ret = regmap_write(data->regmap, FXLS8962AF_SDCD_CONFIG1, value); + if (ret) + return ret; + + /* + * Enable update of SDCD_REF_X/Y/Z values with the current decimated and + * trimmed X/Y/Z acceleration input data. This allows for acceleration + * slope detection with Data(n) to Data(n–1) always used as the input + * to the window comparator. + */ + value = enable_event ? + FXLS8962AF_SDCD_CONFIG2_SDCD_EN | FXLS8962AF_SC2_REF_UPDM_AC : + 0x00; + ret = regmap_write(data->regmap, FXLS8962AF_SDCD_CONFIG2, value); + if (ret) + return ret; + + ret = fxls8962af_event_setup(data, state); + if (ret) + return ret; + + data->enable_event = enable_event; + + if (data->enable_event) { + fxls8962af_active(data); + ret = fxls8962af_power_on(data); + } else { + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + /* Not in buffered mode so disable power */ + ret = fxls8962af_power_off(data); + + iio_device_release_direct_mode(indio_dev); + } + + return ret; +} + +static const struct iio_event_spec fxls8962af_event[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, +}; + #define FXLS8962AF_CHANNEL(axis, reg, idx) { \ .type = IIO_ACCEL, \ .address = reg, \ @@ -481,6 +719,8 @@ static int fxls8962af_set_watermark(struct iio_dev *indio_dev, unsigned val) .shift = 4, \ .endianness = IIO_BE, \ }, \ + .event_spec = fxls8962af_event, \ + .num_event_specs = ARRAY_SIZE(fxls8962af_event), \ } #define FXLS8962AF_TEMP_CHANNEL { \ @@ -522,6 +762,10 @@ static const struct iio_info fxls8962af_info = { .read_raw = &fxls8962af_read_raw, .write_raw = &fxls8962af_write_raw, .write_raw_get_fmt = fxls8962af_write_raw_get_fmt, + .read_event_value = fxls8962af_read_event, + .write_event_value = fxls8962af_write_event, + .read_event_config = fxls8962af_read_event_config, + .write_event_config = fxls8962af_write_event_config, .read_avail = fxls8962af_read_avail, .hwfifo_set_watermark = fxls8962af_set_watermark, }; @@ -605,7 +849,8 @@ static int fxls8962af_buffer_predisable(struct iio_dev *indio_dev) ret = __fxls8962af_fifo_set_mode(data, false); - fxls8962af_active(data); + if (data->enable_event) + fxls8962af_active(data); return ret; } @@ -614,7 +859,10 @@ static int fxls8962af_buffer_postdisable(struct iio_dev *indio_dev) { struct fxls8962af_data *data = iio_priv(indio_dev); - return fxls8962af_power_off(data); + if (!data->enable_event) + fxls8962af_power_off(data); + + return 0; } static const struct iio_buffer_setup_ops fxls8962af_buffer_ops = { @@ -725,6 +973,45 @@ static int fxls8962af_fifo_flush(struct iio_dev *indio_dev) return count; } +static int fxls8962af_event_interrupt(struct iio_dev *indio_dev) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + s64 ts = iio_get_time_ns(indio_dev); + unsigned int reg; + u64 ev_code; + int ret; + + ret = regmap_read(data->regmap, FXLS8962AF_SDCD_INT_SRC1, ®); + if (ret) + return ret; + + if (reg & FXLS8962AF_SDCD_INT_SRC1_X_OT) { + ev_code = reg & FXLS8962AF_SDCD_INT_SRC1_X_POL ? + IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING; + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X, + IIO_EV_TYPE_THRESH, ev_code), ts); + } + + if (reg & FXLS8962AF_SDCD_INT_SRC1_Y_OT) { + ev_code = reg & FXLS8962AF_SDCD_INT_SRC1_Y_POL ? + IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING; + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X, + IIO_EV_TYPE_THRESH, ev_code), ts); + } + + if (reg & FXLS8962AF_SDCD_INT_SRC1_Z_OT) { + ev_code = reg & FXLS8962AF_SDCD_INT_SRC1_Z_POL ? + IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING; + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X, + IIO_EV_TYPE_THRESH, ev_code), ts); + } + + return 0; +} + static irqreturn_t fxls8962af_interrupt(int irq, void *p) { struct iio_dev *indio_dev = p; @@ -744,6 +1031,14 @@ static irqreturn_t fxls8962af_interrupt(int irq, void *p) return IRQ_HANDLED; } + if (reg & FXLS8962AF_INT_STATUS_SRC_SDCD_OT) { + ret = fxls8962af_event_interrupt(indio_dev); + if (ret < 0) + return IRQ_NONE; + + return IRQ_HANDLED; + } + return IRQ_NONE; } -- cgit v1.2.3-70-g09d2 From 269efcf0bbee582fa061fd7e4dfac05ee63e051b Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Mon, 20 Sep 2021 13:42:21 +0200 Subject: iio: accel: fxls8962af: add wake on event This adds ways for the SoC to wake from accelerometer wake events. In the suspend function we skip disabling the sensor if wakeup-source and events are activated. If buffered reads are enabled they will be deactivated before suspend. As the onboard buffer is only holding up to 32 12-bit X/Y/Z data triplets. Signed-off-by: Sean Nyekjaer Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210920114221.1595543-2-sean@geanix.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/fxls8962af-core.c | 46 +++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/drivers/iio/accel/fxls8962af-core.c b/drivers/iio/accel/fxls8962af-core.c index f3d7b3aa9d0e..06b490c2a8ce 100644 --- a/drivers/iio/accel/fxls8962af-core.c +++ b/drivers/iio/accel/fxls8962af-core.c @@ -166,6 +166,7 @@ struct fxls8962af_data { } scan; int64_t timestamp, old_timestamp; /* Only used in hw fifo mode. */ struct iio_mount_matrix orientation; + int irq; u8 watermark; u8 enable_event; u16 lower_thres; @@ -1156,6 +1157,7 @@ int fxls8962af_core_probe(struct device *dev, struct regmap *regmap, int irq) data = iio_priv(indio_dev); dev_set_drvdata(dev, indio_dev); data->regmap = regmap; + data->irq = irq; ret = iio_read_mount_matrix(dev, &data->orientation); if (ret) @@ -1225,6 +1227,9 @@ int fxls8962af_core_probe(struct device *dev, struct regmap *regmap, int irq) if (ret) return ret; + if (device_property_read_bool(dev, "wakeup-source")) + device_init_wakeup(dev, true); + return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL_GPL(fxls8962af_core_probe); @@ -1250,9 +1255,46 @@ static int __maybe_unused fxls8962af_runtime_resume(struct device *dev) return fxls8962af_active(data); } +static int __maybe_unused fxls8962af_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct fxls8962af_data *data = iio_priv(indio_dev); + + if (device_may_wakeup(dev) && data->enable_event) { + enable_irq_wake(data->irq); + + /* + * Disable buffer, as the buffer is so small the device will wake + * almost immediately. + */ + if (iio_buffer_enabled(indio_dev)) + fxls8962af_buffer_predisable(indio_dev); + } else { + fxls8962af_runtime_suspend(dev); + } + + return 0; +} + +static int __maybe_unused fxls8962af_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct fxls8962af_data *data = iio_priv(indio_dev); + + if (device_may_wakeup(dev) && data->enable_event) { + disable_irq_wake(data->irq); + + if (iio_buffer_enabled(indio_dev)) + fxls8962af_buffer_postenable(indio_dev); + } else { + fxls8962af_runtime_resume(dev); + } + + return 0; +} + const struct dev_pm_ops fxls8962af_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + SET_SYSTEM_SLEEP_PM_OPS(fxls8962af_suspend, fxls8962af_resume) SET_RUNTIME_PM_OPS(fxls8962af_runtime_suspend, fxls8962af_runtime_resume, NULL) }; -- cgit v1.2.3-70-g09d2 From 1e23dcaa1a9facf48ebfaf13cc6663e2c8983c0a Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Sat, 25 Sep 2021 10:05:45 +0800 Subject: iio: imx8qxp-adc: Add driver support for NXP IMX8QXP ADC The NXP i.MX 8QuadXPlus SOC has a new ADC IP, so add driver support for this ADC. Signed-off-by: Cai Huoqing Reviewed-by: Fabio Estevam Link: https://lore.kernel.org/r/20210925020555.129-2-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 10 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/imx8qxp-adc.c | 494 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 505 insertions(+) create mode 100644 drivers/iio/adc/imx8qxp-adc.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 6cc1268da184..0ceea8e69e3c 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -530,6 +530,16 @@ config IMX7D_ADC This driver can also be built as a module. If so, the module will be called imx7d_adc. +config IMX8QXP_ADC + tristate "NXP IMX8QXP ADC driver" + depends on ARCH_MXC_ARM64 || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to build support for IMX8QXP ADC. + + This driver can also be built as a module. If so, the module will be + called imx8qxp-adc. + config LP8788_ADC tristate "LP8788 ADC driver" depends on MFD_LP8788 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index d68550f493e3..d3f53549720c 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o obj-$(CONFIG_HI8435) += hi8435.o obj-$(CONFIG_HX711) += hx711.o obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o +obj-$(CONFIG_IMX8QXP_ADC) += imx8qxp-adc.o obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o obj-$(CONFIG_INGENIC_ADC) += ingenic-adc.o obj-$(CONFIG_INTEL_MRFLD_ADC) += intel_mrfld_adc.o diff --git a/drivers/iio/adc/imx8qxp-adc.c b/drivers/iio/adc/imx8qxp-adc.c new file mode 100644 index 000000000000..5030e0d8318d --- /dev/null +++ b/drivers/iio/adc/imx8qxp-adc.c @@ -0,0 +1,494 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * NXP i.MX8QXP ADC driver + * + * Based on the work of Haibo Chen + * The initial developer of the original code is Haibo Chen. + * Portions created by Haibo Chen are Copyright (C) 2018 NXP. + * All Rights Reserved. + * + * Copyright (C) 2018 NXP + * Copyright (C) 2021 Cai Huoqing + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ADC_DRIVER_NAME "imx8qxp-adc" + +/* Register map definition */ +#define IMX8QXP_ADR_ADC_CTRL 0x10 +#define IMX8QXP_ADR_ADC_STAT 0x14 +#define IMX8QXP_ADR_ADC_IE 0x18 +#define IMX8QXP_ADR_ADC_DE 0x1c +#define IMX8QXP_ADR_ADC_CFG 0x20 +#define IMX8QXP_ADR_ADC_FCTRL 0x30 +#define IMX8QXP_ADR_ADC_SWTRIG 0x34 +#define IMX8QXP_ADR_ADC_TCTRL(tid) (0xc0 + (tid) * 4) +#define IMX8QXP_ADR_ADC_CMDH(cid) (0x100 + (cid) * 8) +#define IMX8QXP_ADR_ADC_CMDL(cid) (0x104 + (cid) * 8) +#define IMX8QXP_ADR_ADC_RESFIFO 0x300 +#define IMX8QXP_ADR_ADC_TST 0xffc + +/* ADC bit shift */ +#define IMX8QXP_ADC_IE_FWMIE_MASK GENMASK(1, 0) +#define IMX8QXP_ADC_CTRL_FIFO_RESET_MASK BIT(8) +#define IMX8QXP_ADC_CTRL_SOFTWARE_RESET_MASK BIT(1) +#define IMX8QXP_ADC_CTRL_ADC_EN_MASK BIT(0) +#define IMX8QXP_ADC_TCTRL_TCMD_MASK GENMASK(31, 24) +#define IMX8QXP_ADC_TCTRL_TDLY_MASK GENMASK(23, 16) +#define IMX8QXP_ADC_TCTRL_TPRI_MASK GENMASK(15, 8) +#define IMX8QXP_ADC_TCTRL_HTEN_MASK GENMASK(7, 0) +#define IMX8QXP_ADC_CMDL_CSCALE_MASK GENMASK(13, 8) +#define IMX8QXP_ADC_CMDL_MODE_MASK BIT(7) +#define IMX8QXP_ADC_CMDL_DIFF_MASK BIT(6) +#define IMX8QXP_ADC_CMDL_ABSEL_MASK BIT(5) +#define IMX8QXP_ADC_CMDL_ADCH_MASK GENMASK(2, 0) +#define IMX8QXP_ADC_CMDH_NEXT_MASK GENMASK(31, 24) +#define IMX8QXP_ADC_CMDH_LOOP_MASK GENMASK(23, 16) +#define IMX8QXP_ADC_CMDH_AVGS_MASK GENMASK(15, 12) +#define IMX8QXP_ADC_CMDH_STS_MASK BIT(8) +#define IMX8QXP_ADC_CMDH_LWI_MASK GENMASK(7, 7) +#define IMX8QXP_ADC_CMDH_CMPEN_MASK GENMASK(0, 0) +#define IMX8QXP_ADC_CFG_PWREN_MASK BIT(28) +#define IMX8QXP_ADC_CFG_PUDLY_MASK GENMASK(23, 16) +#define IMX8QXP_ADC_CFG_REFSEL_MASK GENMASK(7, 6) +#define IMX8QXP_ADC_CFG_PWRSEL_MASK GENMASK(5, 4) +#define IMX8QXP_ADC_CFG_TPRICTRL_MASK GENMASK(3, 0) +#define IMX8QXP_ADC_FCTRL_FWMARK_MASK GENMASK(20, 16) +#define IMX8QXP_ADC_FCTRL_FCOUNT_MASK GENMASK(4, 0) +#define IMX8QXP_ADC_RESFIFO_VAL_MASK GENMASK(18, 3) + +/* ADC PARAMETER*/ +#define IMX8QXP_ADC_CMDL_CHANNEL_SCALE_FULL GENMASK(5, 0) +#define IMX8QXP_ADC_CMDL_SEL_A_A_B_CHANNEL 0 +#define IMX8QXP_ADC_CMDL_STANDARD_RESOLUTION 0 +#define IMX8QXP_ADC_CMDL_MODE_SINGLE 0 +#define IMX8QXP_ADC_CMDH_LWI_INCREMENT_DIS 0 +#define IMX8QXP_ADC_CMDH_CMPEN_DIS 0 +#define IMX8QXP_ADC_PAUSE_EN BIT(31) +#define IMX8QXP_ADC_TCTRL_TPRI_PRIORITY_HIGH 0 + +#define IMX8QXP_ADC_TCTRL_HTEN_HW_TIRG_DIS 0 + +#define IMX8QXP_ADC_TIMEOUT msecs_to_jiffies(100) + +struct imx8qxp_adc { + struct device *dev; + void __iomem *regs; + struct clk *clk; + struct clk *ipg_clk; + struct regulator *vref; + /* Serialise ADC channel reads */ + struct mutex lock; + struct completion completion; +}; + +#define IMX8QXP_ADC_CHAN(_idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ +} + +static const struct iio_chan_spec imx8qxp_adc_iio_channels[] = { + IMX8QXP_ADC_CHAN(0), + IMX8QXP_ADC_CHAN(1), + IMX8QXP_ADC_CHAN(2), + IMX8QXP_ADC_CHAN(3), + IMX8QXP_ADC_CHAN(4), + IMX8QXP_ADC_CHAN(5), + IMX8QXP_ADC_CHAN(6), + IMX8QXP_ADC_CHAN(7), +}; + +static void imx8qxp_adc_reset(struct imx8qxp_adc *adc) +{ + u32 ctrl; + + /*software reset, need to clear the set bit*/ + ctrl = readl(adc->regs + IMX8QXP_ADR_ADC_CTRL); + ctrl |= FIELD_PREP(IMX8QXP_ADC_CTRL_SOFTWARE_RESET_MASK, 1); + writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL); + udelay(10); + ctrl &= ~FIELD_PREP(IMX8QXP_ADC_CTRL_SOFTWARE_RESET_MASK, 1); + writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL); + + /* reset the fifo */ + ctrl |= FIELD_PREP(IMX8QXP_ADC_CTRL_FIFO_RESET_MASK, 1); + writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL); +} + +static void imx8qxp_adc_reg_config(struct imx8qxp_adc *adc, int channel) +{ + u32 adc_cfg, adc_tctrl, adc_cmdl, adc_cmdh; + + /* ADC configuration */ + adc_cfg = FIELD_PREP(IMX8QXP_ADC_CFG_PWREN_MASK, 1) | + FIELD_PREP(IMX8QXP_ADC_CFG_PUDLY_MASK, 0x80)| + FIELD_PREP(IMX8QXP_ADC_CFG_REFSEL_MASK, 0) | + FIELD_PREP(IMX8QXP_ADC_CFG_PWRSEL_MASK, 3) | + FIELD_PREP(IMX8QXP_ADC_CFG_TPRICTRL_MASK, 0); + writel(adc_cfg, adc->regs + IMX8QXP_ADR_ADC_CFG); + + /* config the trigger control */ + adc_tctrl = FIELD_PREP(IMX8QXP_ADC_TCTRL_TCMD_MASK, 1) | + FIELD_PREP(IMX8QXP_ADC_TCTRL_TDLY_MASK, 0) | + FIELD_PREP(IMX8QXP_ADC_TCTRL_TPRI_MASK, IMX8QXP_ADC_TCTRL_TPRI_PRIORITY_HIGH) | + FIELD_PREP(IMX8QXP_ADC_TCTRL_HTEN_MASK, IMX8QXP_ADC_TCTRL_HTEN_HW_TIRG_DIS); + writel(adc_tctrl, adc->regs + IMX8QXP_ADR_ADC_TCTRL(0)); + + /* config the cmd */ + adc_cmdl = FIELD_PREP(IMX8QXP_ADC_CMDL_CSCALE_MASK, IMX8QXP_ADC_CMDL_CHANNEL_SCALE_FULL) | + FIELD_PREP(IMX8QXP_ADC_CMDL_MODE_MASK, IMX8QXP_ADC_CMDL_STANDARD_RESOLUTION) | + FIELD_PREP(IMX8QXP_ADC_CMDL_DIFF_MASK, IMX8QXP_ADC_CMDL_MODE_SINGLE) | + FIELD_PREP(IMX8QXP_ADC_CMDL_ABSEL_MASK, IMX8QXP_ADC_CMDL_SEL_A_A_B_CHANNEL) | + FIELD_PREP(IMX8QXP_ADC_CMDL_ADCH_MASK, channel); + writel(adc_cmdl, adc->regs + IMX8QXP_ADR_ADC_CMDL(0)); + + adc_cmdh = FIELD_PREP(IMX8QXP_ADC_CMDH_NEXT_MASK, 0) | + FIELD_PREP(IMX8QXP_ADC_CMDH_LOOP_MASK, 0) | + FIELD_PREP(IMX8QXP_ADC_CMDH_AVGS_MASK, 7) | + FIELD_PREP(IMX8QXP_ADC_CMDH_STS_MASK, 0) | + FIELD_PREP(IMX8QXP_ADC_CMDH_LWI_MASK, IMX8QXP_ADC_CMDH_LWI_INCREMENT_DIS) | + FIELD_PREP(IMX8QXP_ADC_CMDH_CMPEN_MASK, IMX8QXP_ADC_CMDH_CMPEN_DIS); + writel(adc_cmdh, adc->regs + IMX8QXP_ADR_ADC_CMDH(0)); +} + +static void imx8qxp_adc_fifo_config(struct imx8qxp_adc *adc) +{ + u32 fifo_ctrl, interrupt_en; + + fifo_ctrl = readl(adc->regs + IMX8QXP_ADR_ADC_FCTRL); + fifo_ctrl &= ~IMX8QXP_ADC_FCTRL_FWMARK_MASK; + /* set the watermark level to 1 */ + fifo_ctrl |= FIELD_PREP(IMX8QXP_ADC_FCTRL_FWMARK_MASK, 0); + writel(fifo_ctrl, adc->regs + IMX8QXP_ADR_ADC_FCTRL); + + /* FIFO Watermark Interrupt Enable */ + interrupt_en = readl(adc->regs + IMX8QXP_ADR_ADC_IE); + interrupt_en |= FIELD_PREP(IMX8QXP_ADC_IE_FWMIE_MASK, 1); + writel(interrupt_en, adc->regs + IMX8QXP_ADR_ADC_IE); +} + +static void imx8qxp_adc_disable(struct imx8qxp_adc *adc) +{ + u32 ctrl; + + ctrl = readl(adc->regs + IMX8QXP_ADR_ADC_CTRL); + ctrl &= ~FIELD_PREP(IMX8QXP_ADC_CTRL_ADC_EN_MASK, 1); + writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL); +} + +static int imx8qxp_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct imx8qxp_adc *adc = iio_priv(indio_dev); + struct device *dev = adc->dev; + + u32 ctrl, vref_uv; + long ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + pm_runtime_get_sync(dev); + + mutex_lock(&adc->lock); + reinit_completion(&adc->completion); + + imx8qxp_adc_reg_config(adc, chan->channel); + + imx8qxp_adc_fifo_config(adc); + + /* adc enable */ + ctrl = readl(adc->regs + IMX8QXP_ADR_ADC_CTRL); + ctrl |= FIELD_PREP(IMX8QXP_ADC_CTRL_ADC_EN_MASK, 1); + writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL); + /* adc start */ + writel(1, adc->regs + IMX8QXP_ADR_ADC_SWTRIG); + + ret = wait_for_completion_interruptible_timeout(&adc->completion, + IMX8QXP_ADC_TIMEOUT); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_sync_autosuspend(dev); + + if (ret == 0) { + mutex_unlock(&adc->lock); + return -ETIMEDOUT; + } + if (ret < 0) { + mutex_unlock(&adc->lock); + return ret; + } + + *val = FIELD_GET(IMX8QXP_ADC_RESFIFO_VAL_MASK, + readl(adc->regs + IMX8QXP_ADR_ADC_RESFIFO)); + + mutex_unlock(&adc->lock); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + vref_uv = regulator_get_voltage(adc->vref); + *val = vref_uv / 1000; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = clk_get_rate(adc->clk) / 3; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static irqreturn_t imx8qxp_adc_isr(int irq, void *dev_id) +{ + struct imx8qxp_adc *adc = dev_id; + u32 fifo_count; + + fifo_count = FIELD_GET(IMX8QXP_ADC_FCTRL_FCOUNT_MASK, + readl(adc->regs + IMX8QXP_ADR_ADC_FCTRL)); + + if (fifo_count) + complete(&adc->completion); + + return IRQ_HANDLED; +} + +static int imx8qxp_adc_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct imx8qxp_adc *adc = iio_priv(indio_dev); + struct device *dev = adc->dev; + + if (!readval || reg % 4 || reg > IMX8QXP_ADR_ADC_TST) + return -EINVAL; + + pm_runtime_get_sync(dev); + + *readval = readl(adc->regs + reg); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_sync_autosuspend(dev); + + return 0; +} + +static const struct iio_info imx8qxp_adc_iio_info = { + .read_raw = &imx8qxp_adc_read_raw, + .debugfs_reg_access = &imx8qxp_adc_reg_access, +}; + +static int imx8qxp_adc_probe(struct platform_device *pdev) +{ + struct imx8qxp_adc *adc; + struct iio_dev *indio_dev; + struct device *dev = &pdev->dev; + int irq; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!indio_dev) { + dev_err(dev, "Failed allocating iio device\n"); + return -ENOMEM; + } + + adc = iio_priv(indio_dev); + adc->dev = dev; + + mutex_init(&adc->lock); + adc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(adc->regs)) + return PTR_ERR(adc->regs); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + adc->clk = devm_clk_get(dev, "per"); + if (IS_ERR(adc->clk)) + return dev_err_probe(dev, PTR_ERR(adc->clk), "Failed getting clock\n"); + + adc->ipg_clk = devm_clk_get(dev, "ipg"); + if (IS_ERR(adc->ipg_clk)) + return dev_err_probe(dev, PTR_ERR(adc->ipg_clk), "Failed getting clock\n"); + + adc->vref = devm_regulator_get(dev, "vref"); + if (IS_ERR(adc->vref)) + return dev_err_probe(dev, PTR_ERR(adc->vref), "Failed getting reference voltage\n"); + + ret = regulator_enable(adc->vref); + if (ret) { + dev_err(dev, "Can't enable adc reference top voltage\n"); + return ret; + } + + platform_set_drvdata(pdev, indio_dev); + + init_completion(&adc->completion); + + indio_dev->name = ADC_DRIVER_NAME; + indio_dev->info = &imx8qxp_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = imx8qxp_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(imx8qxp_adc_iio_channels); + + ret = clk_prepare_enable(adc->clk); + if (ret) { + dev_err(&pdev->dev, "Could not prepare or enable the clock.\n"); + goto error_regulator_disable; + } + + ret = clk_prepare_enable(adc->ipg_clk); + if (ret) { + dev_err(&pdev->dev, "Could not prepare or enable the clock.\n"); + goto error_adc_clk_disable; + } + + ret = devm_request_irq(dev, irq, imx8qxp_adc_isr, 0, ADC_DRIVER_NAME, adc); + if (ret < 0) { + dev_err(dev, "Failed requesting irq, irq = %d\n", irq); + goto error_ipg_clk_disable; + } + + imx8qxp_adc_reset(adc); + + ret = iio_device_register(indio_dev); + if (ret) { + imx8qxp_adc_disable(adc); + dev_err(dev, "Couldn't register the device.\n"); + goto error_ipg_clk_disable; + } + + pm_runtime_set_active(dev); + pm_runtime_set_autosuspend_delay(dev, 50); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + + return 0; + +error_ipg_clk_disable: + clk_disable_unprepare(adc->ipg_clk); +error_adc_clk_disable: + clk_disable_unprepare(adc->clk); +error_regulator_disable: + regulator_disable(adc->vref); + + return ret; +} + +static int imx8qxp_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct imx8qxp_adc *adc = iio_priv(indio_dev); + struct device *dev = adc->dev; + + pm_runtime_get_sync(dev); + + iio_device_unregister(indio_dev); + + imx8qxp_adc_disable(adc); + + clk_disable_unprepare(adc->clk); + clk_disable_unprepare(adc->ipg_clk); + regulator_disable(adc->vref); + + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + + return 0; +} + +static int imx8qxp_adc_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct imx8qxp_adc *adc = iio_priv(indio_dev); + + imx8qxp_adc_disable(adc); + + clk_disable_unprepare(adc->clk); + clk_disable_unprepare(adc->ipg_clk); + regulator_disable(adc->vref); + + return 0; +} + +static int imx8qxp_adc_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct imx8qxp_adc *adc = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(adc->vref); + if (ret) { + dev_err(dev, "Can't enable adc reference top voltage, err = %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(adc->clk); + if (ret) { + dev_err(dev, "Could not prepare or enable clock.\n"); + goto err_disable_reg; + } + + ret = clk_prepare_enable(adc->ipg_clk); + if (ret) { + dev_err(dev, "Could not prepare or enable clock.\n"); + goto err_unprepare_clk; + } + + imx8qxp_adc_reset(adc); + + return 0; + +err_unprepare_clk: + clk_disable_unprepare(adc->clk); + +err_disable_reg: + regulator_disable(adc->vref); + + return ret; +} + +static const struct dev_pm_ops imx8qxp_adc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(imx8qxp_adc_runtime_suspend, imx8qxp_adc_runtime_resume, NULL) +}; + +static const struct of_device_id imx8qxp_adc_match[] = { + { .compatible = "nxp,imx8qxp-adc", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx8qxp_adc_match); + +static struct platform_driver imx8qxp_adc_driver = { + .probe = imx8qxp_adc_probe, + .remove = imx8qxp_adc_remove, + .driver = { + .name = ADC_DRIVER_NAME, + .of_match_table = imx8qxp_adc_match, + .pm = &imx8qxp_adc_pm_ops, + }, +}; + +module_platform_driver(imx8qxp_adc_driver); + +MODULE_DESCRIPTION("i.MX8QuadXPlus ADC driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-70-g09d2 From db73419d8c06dd354f362a0c2fe06e9ec4aa65e7 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Sat, 25 Sep 2021 10:05:46 +0800 Subject: dt-bindings: iio: adc: Add binding documentation for NXP IMX8QXP ADC The NXP i.MX 8QuadXPlus SOC a new ADC IP, so add binding documentation for NXP IMX8QXP ADC. Reviewed-by: Rob Herring Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20210925020555.129-3-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/nxp,imx8qxp-adc.yaml | 78 ++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/nxp,imx8qxp-adc.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/nxp,imx8qxp-adc.yaml b/Documentation/devicetree/bindings/iio/adc/nxp,imx8qxp-adc.yaml new file mode 100644 index 000000000000..9c59a20a6032 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/nxp,imx8qxp-adc.yaml @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/nxp,imx8qxp-adc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP IMX8QXP ADC bindings + +maintainers: + - Cai Huoqing + +description: + Supports the ADC found on the IMX8QXP SoC. + +properties: + compatible: + const: nxp,imx8qxp-adc + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: per + - const: ipg + + assigned-clocks: + maxItems: 1 + + assigned-clock-rates: + maxItems: 1 + + power-domains: + maxItems: 1 + + "#io-channel-cells": + const: 1 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - assigned-clocks + - assigned-clock-rates + - power-domains + - "#io-channel-cells" + +additionalProperties: false + +examples: + - | + #include + #include + soc { + #address-cells = <2>; + #size-cells = <2>; + adc@5a880000 { + compatible = "nxp,imx8qxp-adc"; + reg = <0x0 0x5a880000 0x0 0x10000>; + interrupts = ; + clocks = <&clk IMX_SC_R_ADC_0>, + <&clk IMX_SC_R_ADC_0>; + clock-names = "per", "ipg"; + assigned-clocks = <&clk IMX_SC_R_ADC_0>; + assigned-clock-rates = <24000000>; + power-domains = <&pd IMX_SC_R_ADC_0>; + #io-channel-cells = <1>; + }; + }; +... -- cgit v1.2.3-70-g09d2 From a6914983b6f157dd395979a040872330b0efdc88 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Sat, 25 Sep 2021 10:05:47 +0800 Subject: MAINTAINERS: Add the driver info of the NXP IMX8QXP The NXP i.MX 8QuadXPlus SOC has a new ADC IP. After adding the driver support for it, I add the driver info of the NXP IMX8QXP ADC to MAINTAINERS file. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20210925020555.129-4-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 2429e27daece..07904ef5a039 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13481,6 +13481,13 @@ S: Maintained F: Documentation/devicetree/bindings/display/imx/nxp,imx8mq-dcss.yaml F: drivers/gpu/drm/imx/dcss/ +NXP i.MX 8QXP ADC DRIVER +M: Cai Huoqing +L: linux-iio@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/iio/adc/nxp,imx8qxp-adc.yaml +F: drivers/iio/adc/imx8qxp-adc.c + NXP PF8100/PF8121A/PF8200 PMIC REGULATOR DEVICE DRIVER M: Jagan Teki S: Maintained -- cgit v1.2.3-70-g09d2 From 7127822d192969255d26902661c979b99214f629 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 21 Sep 2021 13:53:53 +0200 Subject: iio: adc: max1027: Fix style Follow checkpatch.pl's main advices before hacking into the driver, mainly: WARNING: Prefer 'unsigned int' to bare use of 'unsigned' WARNING: Prefer 'unsigned int *' to bare use of 'unsigned *' CHECK: Comparison to NULL could be written "!foo" CHECK: Alignment should match open parenthesis Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20210921115408.66711-2-miquel.raynal@bootlin.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1027.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index 655ab02d03d8..372f2ca96eae 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -328,8 +328,8 @@ static int max1027_read_raw(struct iio_dev *indio_dev, } static int max1027_debugfs_reg_access(struct iio_dev *indio_dev, - unsigned reg, unsigned writeval, - unsigned *readval) + unsigned int reg, unsigned int writeval, + unsigned int *readval) { struct max1027_state *st = iio_priv(indio_dev); u8 *val = (u8 *)st->buffer; @@ -425,7 +425,7 @@ static int max1027_probe(struct spi_device *spi) pr_debug("%s: probe(spi = 0x%p)\n", __func__, spi); indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); - if (indio_dev == NULL) { + if (!indio_dev) { pr_err("Can't allocate iio device\n"); return -ENOMEM; } @@ -444,9 +444,9 @@ static int max1027_probe(struct spi_device *spi) indio_dev->available_scan_masks = st->info->available_scan_masks; st->buffer = devm_kmalloc_array(&indio_dev->dev, - indio_dev->num_channels, 2, - GFP_KERNEL); - if (st->buffer == NULL) { + indio_dev->num_channels, 2, + GFP_KERNEL); + if (!st->buffer) { dev_err(&indio_dev->dev, "Can't allocate buffer\n"); return -ENOMEM; } @@ -463,7 +463,7 @@ static int max1027_probe(struct spi_device *spi) st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-trigger", indio_dev->name); - if (st->trig == NULL) { + if (!st->trig) { ret = -ENOMEM; dev_err(&indio_dev->dev, "Failed to allocate iio trigger\n"); -- cgit v1.2.3-70-g09d2 From 064652c0a402fc1324671b08bc0214957f784611 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 21 Sep 2021 13:53:54 +0200 Subject: iio: adc: max1027: Drop extra warning message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Memory allocation errors automatically trigger the right logs, no need to have our own. Signed-off-by: Miquel Raynal Reviewed-by: Nuno Sá Link: https://lore.kernel.org/r/20210921115408.66711-3-miquel.raynal@bootlin.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1027.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index 372f2ca96eae..9eaeb1784c3b 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -446,10 +446,8 @@ static int max1027_probe(struct spi_device *spi) st->buffer = devm_kmalloc_array(&indio_dev->dev, indio_dev->num_channels, 2, GFP_KERNEL); - if (!st->buffer) { - dev_err(&indio_dev->dev, "Can't allocate buffer\n"); + if (!st->buffer) return -ENOMEM; - } if (spi->irq) { ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, -- cgit v1.2.3-70-g09d2 From 6f1bc6d8fb569eff3c4a5a54a31fc501adab4234 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 21 Sep 2021 13:53:55 +0200 Subject: iio: adc: max1027: Drop useless debug messages These two debug messages bring absolutely no value, let's drop them. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20210921115408.66711-4-miquel.raynal@bootlin.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1027.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index 9eaeb1784c3b..63652a64e5a8 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -393,8 +393,6 @@ static irqreturn_t max1027_trigger_handler(int irq, void *private) struct iio_dev *indio_dev = pf->indio_dev; struct max1027_state *st = iio_priv(indio_dev); - pr_debug("%s(irq=%d, private=0x%p)\n", __func__, irq, private); - /* fill buffer with all channel */ spi_read(st->spi, st->buffer, indio_dev->masklength * 2); @@ -422,8 +420,6 @@ static int max1027_probe(struct spi_device *spi) struct iio_dev *indio_dev; struct max1027_state *st; - pr_debug("%s: probe(spi = 0x%p)\n", __func__, spi); - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); if (!indio_dev) { pr_err("Can't allocate iio device\n"); -- cgit v1.2.3-70-g09d2 From e1c0ea8f6e9d65754cade52fdb6fd206d169c3c4 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 21 Sep 2021 13:53:56 +0200 Subject: iio: adc: max1027: Minimize the number of converted channels Provide a list of ->available_scan_masks which match the device's capabilities. Basically, these devices are able to scan from 0 to N, N being the highest voltage channel requested by the user. The temperature can be included or not, but cannot be retrieved alone. The consequence is, instead of reading and pushing to the IIO buffers all channels each time, the "minimum" number of channels will be scanned and pushed based on the ->active_scan_mask. For example, if the user wants channels 1, 4 and 5, all channels from 0 to 5 will be scanned and pushed to the IIO buffers. The core will then filter out the unneeded samples based on the ->active_scan_mask that has been selected and only channels 1, 4 and 5 will be available to the user in the shared buffer. Provide a comment in the code explaining this logic. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20210921115408.66711-5-miquel.raynal@bootlin.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1027.c | 60 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index 63652a64e5a8..4d996fb09d7d 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -173,18 +173,53 @@ static const struct iio_chan_spec max1231_channels[] = { MAX1X31_CHANNELS(12), }; +/* + * These devices are able to scan from 0 to N, N being the highest voltage + * channel requested by the user. The temperature can be included or not, + * but cannot be retrieved alone. Based on the below + * ->available_scan_masks, the core will select the most appropriate + * ->active_scan_mask and the "minimum" number of channels will be + * scanned and pushed to the buffers. + * + * For example, if the user wants channels 1, 4 and 5, all channels from + * 0 to 5 will be scanned and pushed to the IIO buffers. The core will then + * filter out the unneeded samples based on the ->active_scan_mask that has + * been selected and only channels 1, 4 and 5 will be available to the user + * in the shared buffer. + */ +#define MAX1X27_SCAN_MASK_TEMP BIT(0) + +#define MAX1X27_SCAN_MASKS(temp) \ + GENMASK(1, 1 - (temp)), GENMASK(2, 1 - (temp)), \ + GENMASK(3, 1 - (temp)), GENMASK(4, 1 - (temp)), \ + GENMASK(5, 1 - (temp)), GENMASK(6, 1 - (temp)), \ + GENMASK(7, 1 - (temp)), GENMASK(8, 1 - (temp)) + +#define MAX1X29_SCAN_MASKS(temp) \ + MAX1X27_SCAN_MASKS(temp), \ + GENMASK(9, 1 - (temp)), GENMASK(10, 1 - (temp)), \ + GENMASK(11, 1 - (temp)), GENMASK(12, 1 - (temp)) + +#define MAX1X31_SCAN_MASKS(temp) \ + MAX1X29_SCAN_MASKS(temp), \ + GENMASK(13, 1 - (temp)), GENMASK(14, 1 - (temp)), \ + GENMASK(15, 1 - (temp)), GENMASK(16, 1 - (temp)) + static const unsigned long max1027_available_scan_masks[] = { - 0x000001ff, + MAX1X27_SCAN_MASKS(0), + MAX1X27_SCAN_MASKS(1), 0x00000000, }; static const unsigned long max1029_available_scan_masks[] = { - 0x00001fff, + MAX1X29_SCAN_MASKS(0), + MAX1X29_SCAN_MASKS(1), 0x00000000, }; static const unsigned long max1031_available_scan_masks[] = { - 0x0001ffff, + MAX1X31_SCAN_MASKS(0), + MAX1X31_SCAN_MASKS(1), 0x00000000, }; @@ -369,9 +404,15 @@ static int max1027_set_trigger_state(struct iio_trigger *trig, bool state) if (ret < 0) return ret; - /* Scan from 0 to max */ - st->reg = MAX1027_CONV_REG | MAX1027_CHAN(0) | - MAX1027_SCAN_N_M | MAX1027_TEMP; + /* + * Scan from chan 0 to the highest requested channel. + * Include temperature on demand. + */ + st->reg = MAX1027_CONV_REG | MAX1027_SCAN_0_N; + st->reg |= MAX1027_CHAN(fls(*indio_dev->active_scan_mask) - 2); + if (*indio_dev->active_scan_mask & MAX1X27_SCAN_MASK_TEMP) + st->reg |= MAX1027_TEMP; + ret = spi_write(st->spi, &st->reg, 1); if (ret < 0) return ret; @@ -392,9 +433,14 @@ static irqreturn_t max1027_trigger_handler(int irq, void *private) struct iio_poll_func *pf = private; struct iio_dev *indio_dev = pf->indio_dev; struct max1027_state *st = iio_priv(indio_dev); + unsigned int scanned_chans; + + scanned_chans = fls(*indio_dev->active_scan_mask) - 1; + if (*indio_dev->active_scan_mask & MAX1X27_SCAN_MASK_TEMP) + scanned_chans++; /* fill buffer with all channel */ - spi_read(st->spi, st->buffer, indio_dev->masklength * 2); + spi_read(st->spi, st->buffer, scanned_chans * 2); iio_push_to_buffers(indio_dev, st->buffer); -- cgit v1.2.3-70-g09d2 From 4201519a1769ef5c05959b7b272ca5c239982bd5 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 21 Sep 2021 13:53:57 +0200 Subject: iio: adc: max1027: Rename a helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make it clear that the *_set_trigger_state() hook is responsible for cnvst based conversions by renaming the helper. This may avoid confusions with software trigger support that is going to be introduced. Signed-off-by: Miquel Raynal Reviewed-by: Nuno Sá Link: https://lore.kernel.org/r/20210921115408.66711-6-miquel.raynal@bootlin.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1027.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index 4d996fb09d7d..94e096d03b7e 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -390,7 +390,7 @@ static int max1027_validate_trigger(struct iio_dev *indio_dev, return 0; } -static int max1027_set_trigger_state(struct iio_trigger *trig, bool state) +static int max1027_set_cnvst_trigger_state(struct iio_trigger *trig, bool state) { struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); struct max1027_state *st = iio_priv(indio_dev); @@ -451,7 +451,7 @@ static irqreturn_t max1027_trigger_handler(int irq, void *private) static const struct iio_trigger_ops max1027_trigger_ops = { .validate_device = &iio_trigger_validate_own_device, - .set_trigger_state = &max1027_set_trigger_state, + .set_trigger_state = &max1027_set_cnvst_trigger_state, }; static const struct iio_info max1027_info = { -- cgit v1.2.3-70-g09d2 From eaf57d50c675eb52bae6531e336a31677fd028de Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 21 Sep 2021 13:53:58 +0200 Subject: iio: adc: max1027: Create a helper to enable/disable the cnvst trigger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are two ways to physically trigger a conversion: - A falling edge on the cnvst pin - A write operation on the conversion register Let's create a helper for this. Signed-off-by: Miquel Raynal Reviewed-by: Nuno Sá Link: https://lore.kernel.org/r/20210921115408.66711-7-miquel.raynal@bootlin.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1027.c | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index 94e096d03b7e..ad0ca63212cb 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -272,6 +272,25 @@ struct max1027_state { u8 reg ____cacheline_aligned; }; +static int max1027_enable_trigger(struct iio_dev *indio_dev, bool enable) +{ + struct max1027_state *st = iio_priv(indio_dev); + + st->reg = MAX1027_SETUP_REG | MAX1027_REF_MODE2; + + /* + * Start acquisition on: + * MODE0: external hardware trigger wired to the cnvst input pin + * MODE2: conversion register write + */ + if (enable) + st->reg |= MAX1027_CKS_MODE0; + else + st->reg |= MAX1027_CKS_MODE2; + + return spi_write(st->spi, &st->reg, 1); +} + static int max1027_read_single_value(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) @@ -284,14 +303,9 @@ static int max1027_read_single_value(struct iio_dev *indio_dev, return -EBUSY; } - /* Start acquisition on conversion register write */ - st->reg = MAX1027_SETUP_REG | MAX1027_REF_MODE2 | MAX1027_CKS_MODE2; - ret = spi_write(st->spi, &st->reg, 1); - if (ret < 0) { - dev_err(&indio_dev->dev, - "Failed to configure setup register\n"); + ret = max1027_enable_trigger(indio_dev, false); + if (ret) return ret; - } /* Configure conversion register with the requested chan */ st->reg = MAX1027_CONV_REG | MAX1027_CHAN(chan->channel) | @@ -397,11 +411,8 @@ static int max1027_set_cnvst_trigger_state(struct iio_trigger *trig, bool state) int ret; if (state) { - /* Start acquisition on cnvst */ - st->reg = MAX1027_SETUP_REG | MAX1027_CKS_MODE0 | - MAX1027_REF_MODE2; - ret = spi_write(st->spi, &st->reg, 1); - if (ret < 0) + ret = max1027_enable_trigger(indio_dev, state); + if (ret) return ret; /* @@ -418,10 +429,8 @@ static int max1027_set_cnvst_trigger_state(struct iio_trigger *trig, bool state) return ret; } else { /* Start acquisition on conversion register write */ - st->reg = MAX1027_SETUP_REG | MAX1027_CKS_MODE2 | - MAX1027_REF_MODE2; - ret = spi_write(st->spi, &st->reg, 1); - if (ret < 0) + ret = max1027_enable_trigger(indio_dev, state); + if (ret) return ret; } -- cgit v1.2.3-70-g09d2 From c5a396298248238e4530e9351719b93b94e5b009 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 21 Sep 2021 13:53:59 +0200 Subject: iio: adc: max1027: Simplify the _set_trigger_state() helper The call to max1027_enable_trigger() is the same in both cases thanks to the 'state' variable, so factorize a little bit to simplify the code and explain why we call this helper. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20210921115408.66711-8-miquel.raynal@bootlin.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1027.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index ad0ca63212cb..66a040cbee35 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -410,11 +410,17 @@ static int max1027_set_cnvst_trigger_state(struct iio_trigger *trig, bool state) struct max1027_state *st = iio_priv(indio_dev); int ret; - if (state) { - ret = max1027_enable_trigger(indio_dev, state); - if (ret) - return ret; + /* + * In order to disable the convst trigger, start acquisition on + * conversion register write, which basically disables triggering + * conversions upon cnvst changes and thus has the effect of disabling + * the external hardware trigger. + */ + ret = max1027_enable_trigger(indio_dev, state); + if (ret) + return ret; + if (state) { /* * Scan from chan 0 to the highest requested channel. * Include temperature on demand. @@ -427,11 +433,6 @@ static int max1027_set_cnvst_trigger_state(struct iio_trigger *trig, bool state) ret = spi_write(st->spi, &st->reg, 1); if (ret < 0) return ret; - } else { - /* Start acquisition on conversion register write */ - ret = max1027_enable_trigger(indio_dev, state); - if (ret) - return ret; } return 0; -- cgit v1.2.3-70-g09d2 From cba18232c4f80b64325e351a6d2264cbbfeacb81 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 21 Sep 2021 13:54:00 +0200 Subject: iio: adc: max1027: Ensure a default cnvst trigger configuration We don't expect the (hardware) cnvst trigger to be enabled at boot time, this is a user choice made in sysfs and there is a dedicated callback to enable/disable this trigger. Hence, we can just ensure it is disabled in the probe at initialization time and then assume that whenever a ->read_raw() call happens, the trigger has been disabled and conversions will start on register write. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20210921115408.66711-9-miquel.raynal@bootlin.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1027.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index 66a040cbee35..fe5e9f95174b 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -303,10 +303,6 @@ static int max1027_read_single_value(struct iio_dev *indio_dev, return -EBUSY; } - ret = max1027_enable_trigger(indio_dev, false); - if (ret) - return ret; - /* Configure conversion register with the requested chan */ st->reg = MAX1027_CONV_REG | MAX1027_CHAN(chan->channel) | MAX1027_NOSCAN; @@ -558,6 +554,11 @@ static int max1027_probe(struct spi_device *spi) return ret; } + /* Assume conversion on register write for now */ + ret = max1027_enable_trigger(indio_dev, false); + if (ret) + return ret; + return devm_iio_device_register(&spi->dev, indio_dev); } -- cgit v1.2.3-70-g09d2 From af8b93e27fb6cc74c0cf1e9b70ba01e85586d793 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 21 Sep 2021 13:54:01 +0200 Subject: iio: adc: max1027: Create a helper to configure the channels to scan These bits are meant to be reused for triggered buffers setup. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20210921115408.66711-10-miquel.raynal@bootlin.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1027.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index fe5e9f95174b..33f89f902b83 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -272,6 +272,19 @@ struct max1027_state { u8 reg ____cacheline_aligned; }; +/* Scan from chan 0 to the highest requested channel. Include temperature on demand. */ +static int max1027_configure_chans_and_start(struct iio_dev *indio_dev) +{ + struct max1027_state *st = iio_priv(indio_dev); + + st->reg = MAX1027_CONV_REG | MAX1027_SCAN_0_N; + st->reg |= MAX1027_CHAN(fls(*indio_dev->active_scan_mask) - 2); + if (*indio_dev->active_scan_mask & MAX1X27_SCAN_MASK_TEMP) + st->reg |= MAX1027_TEMP; + + return spi_write(st->spi, &st->reg, 1); +} + static int max1027_enable_trigger(struct iio_dev *indio_dev, bool enable) { struct max1027_state *st = iio_priv(indio_dev); @@ -403,7 +416,6 @@ static int max1027_validate_trigger(struct iio_dev *indio_dev, static int max1027_set_cnvst_trigger_state(struct iio_trigger *trig, bool state) { struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); - struct max1027_state *st = iio_priv(indio_dev); int ret; /* @@ -417,17 +429,8 @@ static int max1027_set_cnvst_trigger_state(struct iio_trigger *trig, bool state) return ret; if (state) { - /* - * Scan from chan 0 to the highest requested channel. - * Include temperature on demand. - */ - st->reg = MAX1027_CONV_REG | MAX1027_SCAN_0_N; - st->reg |= MAX1027_CHAN(fls(*indio_dev->active_scan_mask) - 2); - if (*indio_dev->active_scan_mask & MAX1X27_SCAN_MASK_TEMP) - st->reg |= MAX1027_TEMP; - - ret = spi_write(st->spi, &st->reg, 1); - if (ret < 0) + ret = max1027_configure_chans_and_start(indio_dev); + if (ret) return ret; } -- cgit v1.2.3-70-g09d2 From 59fcc6af89ff2069436830be02ebfde76d79d2d8 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 21 Sep 2021 13:54:02 +0200 Subject: iio: adc: max1027: Prevent single channel accesses during buffer reads When hardware buffers are enabled (the cnvst pin being the trigger), one should not mess with the device state by requesting a single channel read. There is already a iio_buffer_enabled() check in *_read_single_value() to merely prevent this situation but the check is inconsistent since buffers can be enabled after the if clause anyway. Instead, use the core mutex by calling iio_device_claim/release_direct_mode(). Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20210921115408.66711-11-miquel.raynal@bootlin.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1027.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index 33f89f902b83..a3ab88bbe5ae 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -311,10 +311,9 @@ static int max1027_read_single_value(struct iio_dev *indio_dev, int ret; struct max1027_state *st = iio_priv(indio_dev); - if (iio_buffer_enabled(indio_dev)) { - dev_warn(&indio_dev->dev, "trigger mode already enabled"); - return -EBUSY; - } + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; /* Configure conversion register with the requested chan */ st->reg = MAX1027_CONV_REG | MAX1027_CHAN(chan->channel) | @@ -325,6 +324,7 @@ static int max1027_read_single_value(struct iio_dev *indio_dev, if (ret < 0) { dev_err(&indio_dev->dev, "Failed to configure conversion register\n"); + iio_device_release_direct_mode(indio_dev); return ret; } @@ -337,6 +337,9 @@ static int max1027_read_single_value(struct iio_dev *indio_dev, /* Read result */ ret = spi_read(st->spi, st->buffer, (chan->type == IIO_TEMP) ? 4 : 2); + + iio_device_release_direct_mode(indio_dev); + if (ret < 0) return ret; -- cgit v1.2.3-70-g09d2 From c757fc070886a88ec2ad6bf016b26ec11bfcc5bb Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 21 Sep 2021 13:54:03 +0200 Subject: iio: adc: max1027: Separate the IRQ handler from the read logic Create a max1027_read_scan() helper which will make clearer the future IRQ handler updates (no functional change). Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20210921115408.66711-12-miquel.raynal@bootlin.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1027.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index a3ab88bbe5ae..1748b962581f 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -440,22 +440,37 @@ static int max1027_set_cnvst_trigger_state(struct iio_trigger *trig, bool state) return 0; } -static irqreturn_t max1027_trigger_handler(int irq, void *private) +static int max1027_read_scan(struct iio_dev *indio_dev) { - struct iio_poll_func *pf = private; - struct iio_dev *indio_dev = pf->indio_dev; struct max1027_state *st = iio_priv(indio_dev); unsigned int scanned_chans; + int ret; scanned_chans = fls(*indio_dev->active_scan_mask) - 1; if (*indio_dev->active_scan_mask & MAX1X27_SCAN_MASK_TEMP) scanned_chans++; /* fill buffer with all channel */ - spi_read(st->spi, st->buffer, scanned_chans * 2); + ret = spi_read(st->spi, st->buffer, scanned_chans * 2); + if (ret < 0) + return ret; iio_push_to_buffers(indio_dev, st->buffer); + return 0; +} + +static irqreturn_t max1027_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + int ret; + + ret = max1027_read_scan(indio_dev); + if (ret) + dev_err(&indio_dev->dev, + "Cannot read scanned values (%d)\n", ret); + iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; -- cgit v1.2.3-70-g09d2 From a0e831653ef93912c442a6d30dcb29fe94ecf050 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 21 Sep 2021 13:54:04 +0200 Subject: iio: adc: max1027: Introduce an end of conversion helper For now this helper only waits for the maximum duration of a single conversion. In practice, a "temperature measurement" will take twice this time because it will also carry another analog conversion but as here we will only care about the temperature conversion which happens first, we can still only wait for a single sample and get the right data. This helper will soon be improved to properly handle the end of conversion interrupt as well as a higher number of samples. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20210921115408.66711-13-miquel.raynal@bootlin.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1027.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index 1748b962581f..009203d8d8cf 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -60,6 +60,9 @@ #define MAX1027_NAVG_32 (0x03 << 2) #define MAX1027_AVG_EN BIT(4) +/* Device can achieve 300ksps so we assume a 3.33us conversion delay */ +#define MAX1027_CONVERSION_UDELAY 4 + enum max1027_id { max1027, max1029, @@ -272,6 +275,15 @@ struct max1027_state { u8 reg ____cacheline_aligned; }; +static int max1027_wait_eoc(struct iio_dev *indio_dev) +{ + unsigned int conversion_time = MAX1027_CONVERSION_UDELAY; + + usleep_range(conversion_time, conversion_time * 2); + + return 0; +} + /* Scan from chan 0 to the highest requested channel. Include temperature on demand. */ static int max1027_configure_chans_and_start(struct iio_dev *indio_dev) { @@ -331,9 +343,11 @@ static int max1027_read_single_value(struct iio_dev *indio_dev, /* * For an unknown reason, when we use the mode "10" (write * conversion register), the interrupt doesn't occur every time. - * So we just wait 1 ms. + * So we just wait the maximum conversion time and deliver the value. */ - mdelay(1); + ret = max1027_wait_eoc(indio_dev); + if (ret) + return ret; /* Read result */ ret = spi_read(st->spi, st->buffer, (chan->type == IIO_TEMP) ? 4 : 2); -- cgit v1.2.3-70-g09d2 From d7aeec136929ceed6b41eca8ae003fda5f57487f Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 21 Sep 2021 13:54:05 +0200 Subject: iio: adc: max1027: Stop requesting a threaded IRQ The threaded handler is not populated, this means there is nothing running in process context so let's switch to the regular devm_request_irq() call instead. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20210921115408.66711-14-miquel.raynal@bootlin.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1027.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index 009203d8d8cf..dbfff2ef2e65 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -561,12 +561,10 @@ static int max1027_probe(struct spi_device *spi) return ret; } - ret = devm_request_threaded_irq(&spi->dev, spi->irq, - iio_trigger_generic_data_rdy_poll, - NULL, - IRQF_TRIGGER_FALLING, - spi->dev.driver->name, - st->trig); + ret = devm_request_irq(&spi->dev, spi->irq, + iio_trigger_generic_data_rdy_poll, + IRQF_TRIGGER_FALLING, + spi->dev.driver->name, st->trig); if (ret < 0) { dev_err(&indio_dev->dev, "Failed to allocate IRQ.\n"); return ret; -- cgit v1.2.3-70-g09d2 From 1f7b4048b31b2b1aba9d297d34055dac17485823 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 21 Sep 2021 13:54:06 +0200 Subject: iio: adc: max1027: Use the EOC IRQ when populated for single reads So far the End-Of-Conversion interrupt was only used in conjunction with the internal trigger to process the data. Let's extend the use of this interrupt handler to support regular single-shot conversions as well. Doing so requires writing our own hard IRQ handler. This handler has to check if buffers are enabled or not: *** Buffers disabled condition *** This means the user requested a single conversion and the sample is ready to be retrieved. -> This implies adding the relevant completion boilerplate. *** Buffers enabled condition *** Triggers are used. So far there is only support for the internal trigger but this trigger might soon be attached to another device as well so it is the core duty to decide which handler to call in order to process the data. The core will decide to either: * Call the internal trigger handler which will extract the data that is already present in the ADC FIFOs or * Call the trigger handler of another driver when using this trigger with another device, even though this call will be slightly delayed by the fact that the max1027 IRQ is a data-ready interrupt rather than a real trigger: -> The new handler will manually inform the core about the trigger having transitioned by directly calling iio_trigger_poll() (which iio_trigger_generic_data_rdy_poll() initially did). In order for the handler to be "source" agnostic, we also need to change the private pointer and provide the IIO device instead of the trigger object. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20210921115408.66711-15-miquel.raynal@bootlin.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1027.c | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index dbfff2ef2e65..3f4be6859e2d 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -271,15 +271,26 @@ struct max1027_state { struct iio_trigger *trig; __be16 *buffer; struct mutex lock; + struct completion complete; u8 reg ____cacheline_aligned; }; static int max1027_wait_eoc(struct iio_dev *indio_dev) { + struct max1027_state *st = iio_priv(indio_dev); unsigned int conversion_time = MAX1027_CONVERSION_UDELAY; + int ret; - usleep_range(conversion_time, conversion_time * 2); + if (st->spi->irq) { + ret = wait_for_completion_timeout(&st->complete, + msecs_to_jiffies(1000)); + reinit_completion(&st->complete); + if (!ret) + return ret; + } else { + usleep_range(conversion_time, conversion_time * 2); + } return 0; } @@ -474,6 +485,30 @@ static int max1027_read_scan(struct iio_dev *indio_dev) return 0; } +static irqreturn_t max1027_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct max1027_state *st = iio_priv(indio_dev); + + /* + * If buffers are disabled (raw read), we just need to unlock the + * waiters which will then handle the data. + * + * When using the internal trigger, we must hand-off the choice of the + * handler to the core which will then lookup through the interrupt tree + * for the right handler registered with iio_triggered_buffer_setup() + * to execute, as this trigger might very well be used in conjunction + * with another device. The core will then call the relevant handler to + * perform the data processing step. + */ + if (!iio_buffer_enabled(indio_dev)) + complete(&st->complete); + else + iio_trigger_poll(indio_dev->trig); + + return IRQ_HANDLED; +} + static irqreturn_t max1027_trigger_handler(int irq, void *private) { struct iio_poll_func *pf = private; @@ -518,6 +553,7 @@ static int max1027_probe(struct spi_device *spi) st->info = &max1027_chip_info_tbl[spi_get_device_id(spi)->driver_data]; mutex_init(&st->lock); + init_completion(&st->complete); indio_dev->name = spi_get_device_id(spi)->name; indio_dev->info = &max1027_info; @@ -561,10 +597,9 @@ static int max1027_probe(struct spi_device *spi) return ret; } - ret = devm_request_irq(&spi->dev, spi->irq, - iio_trigger_generic_data_rdy_poll, + ret = devm_request_irq(&spi->dev, spi->irq, max1027_handler, IRQF_TRIGGER_FALLING, - spi->dev.driver->name, st->trig); + spi->dev.driver->name, indio_dev); if (ret < 0) { dev_err(&indio_dev->dev, "Failed to allocate IRQ.\n"); return ret; -- cgit v1.2.3-70-g09d2 From 075d3280b4a135b51efd259a6ef792156b4f71a9 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 21 Sep 2021 13:54:07 +0200 Subject: iio: adc: max1027: Allow all kind of triggers to be used There is no reason to limit this driver to its internal trigger. The only difference being, when using an external trigger, the sample conversion must be manually started. Drop the ->validate_trigger() hook in order to allow other triggers to be bound. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20210921115408.66711-16-miquel.raynal@bootlin.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1027.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index 3f4be6859e2d..ba391d4438a5 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -430,17 +430,6 @@ static int max1027_debugfs_reg_access(struct iio_dev *indio_dev, return spi_write(st->spi, val, 1); } -static int max1027_validate_trigger(struct iio_dev *indio_dev, - struct iio_trigger *trig) -{ - struct max1027_state *st = iio_priv(indio_dev); - - if (st->trig != trig) - return -EINVAL; - - return 0; -} - static int max1027_set_cnvst_trigger_state(struct iio_trigger *trig, bool state) { struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); @@ -491,8 +480,8 @@ static irqreturn_t max1027_handler(int irq, void *private) struct max1027_state *st = iio_priv(indio_dev); /* - * If buffers are disabled (raw read), we just need to unlock the - * waiters which will then handle the data. + * If buffers are disabled (raw read) or when using external triggers, + * we just need to unlock the waiters which will then handle the data. * * When using the internal trigger, we must hand-off the choice of the * handler to the core which will then lookup through the interrupt tree @@ -515,7 +504,19 @@ static irqreturn_t max1027_trigger_handler(int irq, void *private) struct iio_dev *indio_dev = pf->indio_dev; int ret; + if (!iio_trigger_using_own(indio_dev)) { + ret = max1027_configure_chans_and_start(indio_dev); + if (ret) + goto out; + + /* This is a threaded handler, it is fine to wait for an IRQ */ + ret = max1027_wait_eoc(indio_dev); + if (ret) + goto out; + } + ret = max1027_read_scan(indio_dev); +out: if (ret) dev_err(&indio_dev->dev, "Cannot read scanned values (%d)\n", ret); @@ -532,7 +533,6 @@ static const struct iio_trigger_ops max1027_trigger_ops = { static const struct iio_info max1027_info = { .read_raw = &max1027_read_raw, - .validate_trigger = &max1027_validate_trigger, .debugfs_reg_access = &max1027_debugfs_reg_access, }; -- cgit v1.2.3-70-g09d2 From 089ec5e934133ca33b332d631325cdb4669a4780 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 21 Sep 2021 13:54:08 +0200 Subject: iio: adc: max1027: Don't reject external triggers when there is no IRQ External triggers do not necessarily need the EOC interrupt to be populated to work properly. The end of conversion status may either come from an interrupt or from a sufficient enough extra delay. IRQs are not mandatory so move the triggered buffer setup out of the IRQ condition and add the logic to wait enough time for all the requested conversions to be in the device's FIFO. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20210921115408.66711-17-miquel.raynal@bootlin.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1027.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index ba391d4438a5..fa4a2f48a36a 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -289,6 +289,9 @@ static int max1027_wait_eoc(struct iio_dev *indio_dev) if (!ret) return ret; } else { + if (indio_dev->active_scan_mask) + conversion_time *= hweight32(*indio_dev->active_scan_mask); + usleep_range(conversion_time, conversion_time * 2); } @@ -568,16 +571,18 @@ static int max1027_probe(struct spi_device *spi) if (!st->buffer) return -ENOMEM; - if (spi->irq) { - ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, - &iio_pollfunc_store_time, - &max1027_trigger_handler, - NULL); - if (ret < 0) { - dev_err(&indio_dev->dev, "Failed to setup buffer\n"); - return ret; - } + /* Enable triggered buffers */ + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, + &iio_pollfunc_store_time, + &max1027_trigger_handler, + NULL); + if (ret < 0) { + dev_err(&indio_dev->dev, "Failed to setup buffer\n"); + return ret; + } + /* If there is an EOC interrupt, register the cnvst hardware trigger */ + if (spi->irq) { st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-trigger", indio_dev->name); if (!st->trig) { -- cgit v1.2.3-70-g09d2 From 1b7da2fa18f723a821c49aa67d4ba9603a386c0a Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 26 Sep 2021 16:02:30 +0200 Subject: iio: imu: st_lsm6dsx: move max_fifo_size in st_lsm6dsx_fifo_ops Move max_fifo_size in st_lsm6dsx_fifo_ops in order to have all FIFO configuration parameters in st_lsm6dsx_fifo_ops structure. This patch does not introduce any logic change, just small code rearrangement. Signed-off-by: Lorenzo Bianconi Link: https://lore.kernel.org/r/3262ad9d9d1497e19ea1bab208c495c2b9a98994.1632664866.git.lorenzo@kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 4 ++-- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 16 +++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index 5ef55763a6cc..6ac4eac36458 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -143,6 +143,7 @@ struct st_lsm6dsx_fs_table_entry { * @read_fifo: Read FIFO callback. * @fifo_th: FIFO threshold register info (addr + mask). * @fifo_diff: FIFO diff status register info (addr + mask). + * @max_size: Sensor max fifo length in FIFO words. * @th_wl: FIFO threshold word length. */ struct st_lsm6dsx_fifo_ops { @@ -156,6 +157,7 @@ struct st_lsm6dsx_fifo_ops { u8 addr; u16 mask; } fifo_diff; + u16 max_size; u8 th_wl; }; @@ -271,7 +273,6 @@ struct st_lsm6dsx_ext_dev_settings { * @reset: register address for reset. * @boot: register address for boot. * @bdu: register address for Block Data Update. - * @max_fifo_size: Sensor max fifo length in FIFO words. * @id: List of hw id/device name supported by the driver configuration. * @channels: IIO channels supported by the device. * @irq_config: interrupts related registers. @@ -288,7 +289,6 @@ struct st_lsm6dsx_settings { struct st_lsm6dsx_reg reset; struct st_lsm6dsx_reg boot; struct st_lsm6dsx_reg bdu; - u16 max_fifo_size; struct { enum st_lsm6dsx_hw_id hw_id; const char *name; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index db45f1fc0b81..0f54df85134a 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -102,7 +102,6 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x22, .mask = BIT(6), }, - .max_fifo_size = 32, .id = { { .hw_id = ST_LSM9DS1_ID, @@ -194,6 +193,9 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .mask = BIT(4), }, }, + .fifo_ops = { + .max_size = 32, + }, }, { .reset = { @@ -208,7 +210,6 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x12, .mask = BIT(6), }, - .max_fifo_size = 1365, .id = { { .hw_id = ST_LSM6DS3_ID, @@ -329,6 +330,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x3a, .mask = GENMASK(11, 0), }, + .max_size = 1365, .th_wl = 3, /* 1LSB = 2B */ }, .ts_settings = { @@ -374,7 +376,6 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x12, .mask = BIT(6), }, - .max_fifo_size = 682, .id = { { .hw_id = ST_LSM6DS3H_ID, @@ -495,6 +496,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x3a, .mask = GENMASK(11, 0), }, + .max_size = 682, .th_wl = 3, /* 1LSB = 2B */ }, .ts_settings = { @@ -540,7 +542,6 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x12, .mask = BIT(6), }, - .max_fifo_size = 682, .id = { { .hw_id = ST_LSM6DSL_ID, @@ -677,6 +678,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x3a, .mask = GENMASK(10, 0), }, + .max_size = 682, .th_wl = 3, /* 1LSB = 2B */ }, .ts_settings = { @@ -759,7 +761,6 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x12, .mask = BIT(6), }, - .max_fifo_size = 512, .id = { { .hw_id = ST_LSM6DSR_ID, @@ -910,6 +911,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x3a, .mask = GENMASK(9, 0), }, + .max_size = 512, .th_wl = 1, }, .ts_settings = { @@ -984,7 +986,6 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x12, .mask = BIT(6), }, - .max_fifo_size = 512, .id = { { .hw_id = ST_ASM330LHH_ID, @@ -1119,6 +1120,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x3a, .mask = GENMASK(9, 0), }, + .max_size = 512, .th_wl = 1, }, .ts_settings = { @@ -1603,7 +1605,7 @@ int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val) struct st_lsm6dsx_hw *hw = sensor->hw; int err; - if (val < 1 || val > hw->settings->max_fifo_size) + if (val < 1 || val > hw->settings->fifo_ops.max_size) return -EINVAL; mutex_lock(&hw->conf_lock); -- cgit v1.2.3-70-g09d2 From c5fd034a2ac99117f67f9ab7653912af33f9106e Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Fri, 25 Jun 2021 10:43:25 +0300 Subject: iio: adc: fsl-imx25-gcq: initialize regulators as needed The driver tries to initialize all possible regulators from the DT, then match the external regulators with each channel and then release all unused regulators. We can change the logic a bit to initialize regulators only when at least one channel needs them. This change creates a mx25_gcq_ext_regulator_setup() function that is called only for the external regulators. If there's already a reference to an external regulator, the function will just exit early with no error. This way, the driver doesn't need to keep any track of these regulators during init. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210625074325.9237-1-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/fsl-imx25-gcq.c | 55 +++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/drivers/iio/adc/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c index 329c555b55cc..551e83ae573c 100644 --- a/drivers/iio/adc/fsl-imx25-gcq.c +++ b/drivers/iio/adc/fsl-imx25-gcq.c @@ -172,13 +172,35 @@ static const struct regmap_config mx25_gcq_regconfig = { .reg_stride = 4, }; +static int mx25_gcq_ext_regulator_setup(struct device *dev, + struct mx25_gcq_priv *priv, u32 refp) +{ + char reg_name[12]; + int ret; + + if (priv->vref[refp]) + return 0; + + ret = snprintf(reg_name, sizeof(reg_name), "vref-%s", + mx25_gcq_refp_names[refp]); + if (ret < 0) + return ret; + + priv->vref[refp] = devm_regulator_get_optional(dev, reg_name); + if (IS_ERR(priv->vref[refp])) + return dev_err_probe(dev, PTR_ERR(priv->vref[refp]), + "Error, trying to use external voltage reference without a %s regulator.", + reg_name); + + return 0; +} + static int mx25_gcq_setup_cfgs(struct platform_device *pdev, struct mx25_gcq_priv *priv) { struct device_node *np = pdev->dev.of_node; struct device_node *child; struct device *dev = &pdev->dev; - unsigned int refp_used[4] = {}; int ret, i; /* @@ -194,19 +216,6 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev, MX25_ADCQ_CFG_IN(i) | MX25_ADCQ_CFG_REFN_NGND2); - /* - * First get all regulators to store them in channel_vref_mv if - * necessary. Later we use that information for proper IIO scale - * information. - */ - priv->vref[MX25_ADC_REFP_INT] = NULL; - priv->vref[MX25_ADC_REFP_EXT] = - devm_regulator_get_optional(dev, "vref-ext"); - priv->vref[MX25_ADC_REFP_XP] = - devm_regulator_get_optional(dev, "vref-xp"); - priv->vref[MX25_ADC_REFP_YP] = - devm_regulator_get_optional(dev, "vref-yp"); - for_each_child_of_node(np, child) { u32 reg; u32 refp = MX25_ADCQ_CFG_REFP_INT; @@ -233,11 +242,10 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev, case MX25_ADC_REFP_EXT: case MX25_ADC_REFP_XP: case MX25_ADC_REFP_YP: - if (IS_ERR(priv->vref[refp])) { - dev_err(dev, "Error, trying to use external voltage reference without a vref-%s regulator.", - mx25_gcq_refp_names[refp]); + ret = mx25_gcq_ext_regulator_setup(&pdev->dev, priv, refp); + if (ret) { of_node_put(child); - return PTR_ERR(priv->vref[refp]); + return ret; } priv->channel_vref_mv[reg] = regulator_get_voltage(priv->vref[refp]); @@ -253,8 +261,6 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev, return -EINVAL; } - ++refp_used[refp]; - /* * Shift the read values to the correct positions within the * register. @@ -285,15 +291,6 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev, regmap_write(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK | MX25_ADCQ_CR_QSM_FQS); - /* Remove unused regulators */ - for (i = 0; i != 4; ++i) { - if (!refp_used[i]) { - if (!IS_ERR_OR_NULL(priv->vref[i])) - devm_regulator_put(priv->vref[i]); - priv->vref[i] = NULL; - } - } - return 0; } -- cgit v1.2.3-70-g09d2 From 25c02edfd41f0dd7aad9115149625d7e7f441b7d Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Fri, 3 Sep 2021 10:29:13 +0300 Subject: iio: inkern: introduce devm_iio_map_array_register() short-hand function This change introduces a device-managed variant to the iio_map_array_register() function. It's a simple implementation of calling iio_map_array_register() and registering a callback to iio_map_array_unregister() with the devm_add_action_or_reset(). The function uses an explicit 'dev' parameter to bind the unwinding to. It could have been implemented to implicitly use the parent of the IIO device, however it shouldn't be too expensive to callers to just specify to which device object to bind this unwind call. It would make the API a bit more flexible. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210903072917.45769-2-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- Documentation/driver-api/driver-model/devres.rst | 1 + drivers/iio/inkern.c | 17 +++++++++++++++++ include/linux/iio/driver.h | 14 ++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index 650096523f4f..148e19381b79 100644 --- a/Documentation/driver-api/driver-model/devres.rst +++ b/Documentation/driver-api/driver-model/devres.rst @@ -287,6 +287,7 @@ IIO devm_iio_device_register() devm_iio_dmaengine_buffer_setup() devm_iio_kfifo_buffer_setup() + devm_iio_map_array_register() devm_iio_triggered_buffer_setup() devm_iio_trigger_alloc() devm_iio_trigger_register() diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 391a3380a1d1..0222885b334c 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -85,6 +85,23 @@ int iio_map_array_unregister(struct iio_dev *indio_dev) } EXPORT_SYMBOL_GPL(iio_map_array_unregister); +static void iio_map_array_unregister_cb(void *indio_dev) +{ + iio_map_array_unregister(indio_dev); +} + +int devm_iio_map_array_register(struct device *dev, struct iio_dev *indio_dev, struct iio_map *maps) +{ + int ret; + + ret = iio_map_array_register(indio_dev, maps); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, iio_map_array_unregister_cb, indio_dev); +} +EXPORT_SYMBOL_GPL(devm_iio_map_array_register); + static const struct iio_chan_spec *iio_chan_spec_from_name(const struct iio_dev *indio_dev, const char *name) { diff --git a/include/linux/iio/driver.h b/include/linux/iio/driver.h index 36de60a5da7a..7a157ed218f6 100644 --- a/include/linux/iio/driver.h +++ b/include/linux/iio/driver.h @@ -8,6 +8,7 @@ #ifndef _IIO_INKERN_H_ #define _IIO_INKERN_H_ +struct device; struct iio_dev; struct iio_map; @@ -26,4 +27,17 @@ int iio_map_array_register(struct iio_dev *indio_dev, */ int iio_map_array_unregister(struct iio_dev *indio_dev); +/** + * devm_iio_map_array_register - device-managed version of iio_map_array_register + * @dev: Device object to which to bind the unwinding of this registration + * @indio_dev: Pointer to the iio_dev structure + * @maps: Pointer to an IIO map object which is to be registered to this IIO device + * + * This function will call iio_map_array_register() to register an IIO map object + * and will also hook a callback to the iio_map_array_unregister() function to + * handle de-registration of the IIO map object when the device's refcount goes to + * zero. + */ +int devm_iio_map_array_register(struct device *dev, struct iio_dev *indio_dev, struct iio_map *maps); + #endif -- cgit v1.2.3-70-g09d2 From 7a29120c6e31e2f2ce3a0bacdcbc5efaf6603589 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Fri, 3 Sep 2021 10:29:14 +0300 Subject: iio: adc: intel_mrfld_adc: convert probe to full device-managed The only call in the remove hook is the iio_map_array_unregister() call. Since we have a devm_iio_map_array_register() function now, we can use that and remove the remove hook entirely. The IIO device was registered with the devm_iio_device_register() prior to this change. Also, the platform_set_drvdata() can be removed now, since it was used only in the remove hook. Signed-off-by: Alexandru Ardelean Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210903072917.45769-3-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/intel_mrfld_adc.c | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/drivers/iio/adc/intel_mrfld_adc.c b/drivers/iio/adc/intel_mrfld_adc.c index 75394350eb4c..616de0c3a049 100644 --- a/drivers/iio/adc/intel_mrfld_adc.c +++ b/drivers/iio/adc/intel_mrfld_adc.c @@ -205,8 +205,6 @@ static int mrfld_adc_probe(struct platform_device *pdev) if (ret) return ret; - platform_set_drvdata(pdev, indio_dev); - indio_dev->name = pdev->name; indio_dev->channels = mrfld_adc_channels; @@ -214,28 +212,11 @@ static int mrfld_adc_probe(struct platform_device *pdev) indio_dev->info = &mrfld_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; - ret = iio_map_array_register(indio_dev, iio_maps); + ret = devm_iio_map_array_register(dev, indio_dev, iio_maps); if (ret) return ret; - ret = devm_iio_device_register(dev, indio_dev); - if (ret < 0) - goto err_array_unregister; - - return 0; - -err_array_unregister: - iio_map_array_unregister(indio_dev); - return ret; -} - -static int mrfld_adc_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - - iio_map_array_unregister(indio_dev); - - return 0; + return devm_iio_device_register(dev, indio_dev); } static const struct platform_device_id mrfld_adc_id_table[] = { @@ -249,7 +230,6 @@ static struct platform_driver mrfld_adc_driver = { .name = "mrfld_bcove_adc", }, .probe = mrfld_adc_probe, - .remove = mrfld_adc_remove, .id_table = mrfld_adc_id_table, }; module_platform_driver(mrfld_adc_driver); -- cgit v1.2.3-70-g09d2 From 298fdedc4aff5f6bb5386170a718e7043166d3cd Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Fri, 3 Sep 2021 10:29:15 +0300 Subject: iio: adc: axp288_adc: convert probe to full device-managed This change converts the probe of this driver to use device-managed functions only, which means that the remove hook can be removed. The remove hook has only 2 calls to iio_device_unregister() and iio_map_array_unregister(). Both these can now be done via devm register functions, now that there's also a devm_iio_map_array_register() function. The platform_set_drvdata() can also be removed now. This change also removes the error print for when the iio_device_register() call fails. This isn't required now. Signed-off-by: Alexandru Ardelean Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20210903072917.45769-4-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/axp288_adc.c | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c index 5f5e8b39e4d2..a4b8be5b8f88 100644 --- a/drivers/iio/adc/axp288_adc.c +++ b/drivers/iio/adc/axp288_adc.c @@ -259,7 +259,7 @@ static int axp288_adc_probe(struct platform_device *pdev) info->irq = platform_get_irq(pdev, 0); if (info->irq < 0) return info->irq; - platform_set_drvdata(pdev, indio_dev); + info->regmap = axp20x->regmap; /* * Set ADC to enabled state at all time, including system suspend. @@ -276,31 +276,12 @@ static int axp288_adc_probe(struct platform_device *pdev) indio_dev->num_channels = ARRAY_SIZE(axp288_adc_channels); indio_dev->info = &axp288_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; - ret = iio_map_array_register(indio_dev, axp288_adc_default_maps); + + ret = devm_iio_map_array_register(&pdev->dev, indio_dev, axp288_adc_default_maps); if (ret < 0) return ret; - ret = iio_device_register(indio_dev); - if (ret < 0) { - dev_err(&pdev->dev, "unable to register iio device\n"); - goto err_array_unregister; - } - return 0; - -err_array_unregister: - iio_map_array_unregister(indio_dev); - - return ret; -} - -static int axp288_adc_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - - iio_device_unregister(indio_dev); - iio_map_array_unregister(indio_dev); - - return 0; + return devm_iio_device_register(&pdev->dev, indio_dev); } static const struct platform_device_id axp288_adc_id_table[] = { @@ -310,7 +291,6 @@ static const struct platform_device_id axp288_adc_id_table[] = { static struct platform_driver axp288_adc_driver = { .probe = axp288_adc_probe, - .remove = axp288_adc_remove, .id_table = axp288_adc_id_table, .driver = { .name = "axp288_adc", -- cgit v1.2.3-70-g09d2 From 9c22f459cc413db4b7164f35eb99010d9284b4bb Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Fri, 3 Sep 2021 10:29:16 +0300 Subject: iio: adc: lp8788_adc: convert probe to full-device managed This change converts the probe of this driver to use device-managed functions only, which means that the remove hook can be removed. The remove hook has only 2 calls to iio_device_unregister() and iio_map_array_unregister(). Both these can now be done via devm register functions, now that there's also a devm_iio_map_array_register() function. The platform_set_drvdata() can also be removed now. This change also removes the error print for when the iio_device_register() call fails. This isn't required now. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210903072917.45769-5-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/lp8788_adc.c | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/drivers/iio/adc/lp8788_adc.c b/drivers/iio/adc/lp8788_adc.c index 8fb57e375529..6d9b354bc705 100644 --- a/drivers/iio/adc/lp8788_adc.c +++ b/drivers/iio/adc/lp8788_adc.c @@ -163,7 +163,8 @@ static struct iio_map lp8788_default_iio_maps[] = { { } }; -static int lp8788_iio_map_register(struct iio_dev *indio_dev, +static int lp8788_iio_map_register(struct device *dev, + struct iio_dev *indio_dev, struct lp8788_platform_data *pdata, struct lp8788_adc *adc) { @@ -173,7 +174,7 @@ static int lp8788_iio_map_register(struct iio_dev *indio_dev, map = (!pdata || !pdata->adc_pdata) ? lp8788_default_iio_maps : pdata->adc_pdata; - ret = iio_map_array_register(indio_dev, map); + ret = devm_iio_map_array_register(dev, indio_dev, map); if (ret) { dev_err(&indio_dev->dev, "iio map err: %d\n", ret); return ret; @@ -196,9 +197,8 @@ static int lp8788_adc_probe(struct platform_device *pdev) adc = iio_priv(indio_dev); adc->lp = lp; - platform_set_drvdata(pdev, indio_dev); - ret = lp8788_iio_map_register(indio_dev, lp->pdata, adc); + ret = lp8788_iio_map_register(&pdev->dev, indio_dev, lp->pdata, adc); if (ret) return ret; @@ -210,32 +210,11 @@ static int lp8788_adc_probe(struct platform_device *pdev) indio_dev->channels = lp8788_adc_channels; indio_dev->num_channels = ARRAY_SIZE(lp8788_adc_channels); - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "iio dev register err: %d\n", ret); - goto err_iio_device; - } - - return 0; - -err_iio_device: - iio_map_array_unregister(indio_dev); - return ret; -} - -static int lp8788_adc_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - - iio_device_unregister(indio_dev); - iio_map_array_unregister(indio_dev); - - return 0; + return devm_iio_device_register(&pdev->dev, indio_dev); } static struct platform_driver lp8788_adc_driver = { .probe = lp8788_adc_probe, - .remove = lp8788_adc_remove, .driver = { .name = LP8788_DEV_ADC, }, -- cgit v1.2.3-70-g09d2 From fb6349effb7e09757e7fa3d115711009f9330275 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Fri, 3 Sep 2021 10:29:17 +0300 Subject: iio: adc: da9150-gpadc: convert probe to full-device managed This change converts the probe of this driver to use device-managed functions only, which means that the remove hook can be removed. The remove hook has only 2 calls to iio_device_unregister() and iio_map_array_unregister(). Both these can now be done via devm register functions, now that there's also a devm_iio_map_array_register() function. The platform_set_drvdata() can also be removed now. This change also removes the error print for when the iio_device_register() call fails. This isn't required now. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210903072917.45769-6-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/da9150-gpadc.c | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/drivers/iio/adc/da9150-gpadc.c b/drivers/iio/adc/da9150-gpadc.c index 7a7a54a7ed76..8f0d3fb63b67 100644 --- a/drivers/iio/adc/da9150-gpadc.c +++ b/drivers/iio/adc/da9150-gpadc.c @@ -330,7 +330,6 @@ static int da9150_gpadc_probe(struct platform_device *pdev) } gpadc = iio_priv(indio_dev); - platform_set_drvdata(pdev, indio_dev); gpadc->da9150 = da9150; gpadc->dev = dev; mutex_init(&gpadc->lock); @@ -347,7 +346,7 @@ static int da9150_gpadc_probe(struct platform_device *pdev) return ret; } - ret = iio_map_array_register(indio_dev, da9150_gpadc_default_maps); + ret = devm_iio_map_array_register(&pdev->dev, indio_dev, da9150_gpadc_default_maps); if (ret) { dev_err(dev, "Failed to register IIO maps: %d\n", ret); return ret; @@ -359,28 +358,7 @@ static int da9150_gpadc_probe(struct platform_device *pdev) indio_dev->channels = da9150_gpadc_channels; indio_dev->num_channels = ARRAY_SIZE(da9150_gpadc_channels); - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(dev, "Failed to register IIO device: %d\n", ret); - goto iio_map_unreg; - } - - return 0; - -iio_map_unreg: - iio_map_array_unregister(indio_dev); - - return ret; -} - -static int da9150_gpadc_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - - iio_device_unregister(indio_dev); - iio_map_array_unregister(indio_dev); - - return 0; + return devm_iio_device_register(&pdev->dev, indio_dev); } static struct platform_driver da9150_gpadc_driver = { @@ -388,7 +366,6 @@ static struct platform_driver da9150_gpadc_driver = { .name = "da9150-gpadc", }, .probe = da9150_gpadc_probe, - .remove = da9150_gpadc_remove, }; module_platform_driver(da9150_gpadc_driver); -- cgit v1.2.3-70-g09d2 From b600bd7eb333554518b4dd36b882b2ae58a5149e Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Fri, 3 Sep 2021 16:14:19 +0200 Subject: iio: adis: do not disabe IRQs in 'adis_init()' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With commit ecb010d441088 ("iio: imu: adis: Refactor adis_initial_startup") we are doing a HW or SW reset to the device which means that we'll get the default state of the data ready pin (which is enabled). Hence there's no point in disabling the IRQ in the init function. Moreover, this function is intended to initialize internal data structures and not really do anything on the device. As a result of this, some devices were left with the data ready pin enabled after probe which was not the desired behavior. Thus, we move the call to 'adis_enable_irq()' to the initial startup function where it makes more sense for it to be. Note that for devices that cannot mask/unmask the pin, it makes no sense to call the function at this point since the IRQ should not have been yet requested. This will be improved in a follow up change. Fixes: ecb010d441088 ("iio: imu: adis: Refactor adis_initial_startup") Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20210903141423.517028-2-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index b9a06ca29bee..d4e692b187cd 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -430,6 +430,8 @@ int __adis_initial_startup(struct adis *adis) if (ret) return ret; + adis_enable_irq(adis, false); + if (!adis->data->prod_id_reg) return 0; @@ -526,7 +528,7 @@ int adis_init(struct adis *adis, struct iio_dev *indio_dev, adis->current_page = 0; } - return adis_enable_irq(adis, false); + return 0; } EXPORT_SYMBOL_GPL(adis_init); -- cgit v1.2.3-70-g09d2 From 31fa357ac809affd9f9a7d0b5d1991951e16beec Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Fri, 3 Sep 2021 16:14:20 +0200 Subject: iio: adis: handle devices that cannot unmask the drdy pin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some devices can't mask/unmask the data ready pin and in those cases each driver was just calling '{dis}enable_irq()' to control the trigger state. This change, moves that handling into the library by introducing a new boolean in the data structure that tells the library that the device cannot unmask the pin. On top of controlling the trigger state, we can also use this flag to automatically request the IRQ with 'IRQF_NO_AUTOEN' in case it is set. So far, all users of the library want to start operation with IRQs/DRDY pin disabled so it should be fairly safe to do this inside the library. Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20210903141423.517028-3-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis.c | 15 ++++++++++++++- drivers/iio/imu/adis_trigger.c | 4 ++++ include/linux/iio/imu/adis.h | 2 ++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index d4e692b187cd..cb0d66bf6561 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -286,6 +286,13 @@ int adis_enable_irq(struct adis *adis, bool enable) if (adis->data->enable_irq) { ret = adis->data->enable_irq(adis, enable); goto out_unlock; + } else if (adis->data->unmasked_drdy) { + if (enable) + enable_irq(adis->spi->irq); + else + disable_irq(adis->spi->irq); + + goto out_unlock; } ret = __adis_read_reg_16(adis, adis->data->msc_ctrl_reg, &msc); @@ -430,7 +437,13 @@ int __adis_initial_startup(struct adis *adis) if (ret) return ret; - adis_enable_irq(adis, false); + /* + * don't bother calling this if we can't unmask the IRQ as in this case + * the IRQ is most likely not yet requested and we will request it + * with 'IRQF_NO_AUTOEN' anyways. + */ + if (!adis->data->unmasked_drdy) + adis_enable_irq(adis, false); if (!adis->data->prod_id_reg) return 0; diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu/adis_trigger.c index 48eedc29b28a..c461bd1e8e69 100644 --- a/drivers/iio/imu/adis_trigger.c +++ b/drivers/iio/imu/adis_trigger.c @@ -30,6 +30,10 @@ static const struct iio_trigger_ops adis_trigger_ops = { static int adis_validate_irq_flag(struct adis *adis) { unsigned long direction = adis->irq_flag & IRQF_TRIGGER_MASK; + + /* We cannot mask the interrupt so ensure it's not enabled at request */ + if (adis->data->unmasked_drdy) + adis->irq_flag |= IRQF_NO_AUTOEN; /* * Typically this devices have data ready either on the rising edge or * on the falling edge of the data ready pin. This checks enforces that diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index cf49997d5903..7c02f5292eea 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -49,6 +49,7 @@ struct adis_timeout { * @status_error_mask: Bitmask of errors supported by the device * @timeouts: Chip specific delays * @enable_irq: Hook for ADIS devices that have a special IRQ enable/disable + * @unmasked_drdy: True for devices that cannot mask/unmask the data ready pin * @has_paging: True if ADIS device has paged registers * @burst_reg_cmd: Register command that triggers burst * @burst_len: Burst size in the SPI RX buffer. If @burst_max_len is defined, @@ -78,6 +79,7 @@ struct adis_data { unsigned int status_error_mask; int (*enable_irq)(struct adis *adis, bool enable); + bool unmasked_drdy; bool has_paging; -- cgit v1.2.3-70-g09d2 From cab85eadd78517a798bf818ce85a8e7ddf444b88 Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Fri, 3 Sep 2021 16:14:21 +0200 Subject: iio: adis16475: make use of the new unmasked_drdy flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The library can now handle enabling/disabling IRQs for devices that cannot unmask the data ready pin. Hence there's no need to provide an 'enable_irq' callback anymore. The library will also automatically request the IRQ with 'IRQF_NO_AUTOEN' so that we can also remove that from the driver. Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20210903141423.517028-4-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16475.c | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c index eb48102f9424..b76d8482bbd5 100644 --- a/drivers/iio/imu/adis16475.c +++ b/drivers/iio/imu/adis16475.c @@ -606,20 +606,6 @@ static const char * const adis16475_status_error_msgs[] = { [ADIS16475_DIAG_STAT_CLK] = "Clock error", }; -static int adis16475_enable_irq(struct adis *adis, bool enable) -{ - /* - * There is no way to gate the data-ready signal internally inside the - * ADIS16475. We can only control it's polarity... - */ - if (enable) - enable_irq(adis->spi->irq); - else - disable_irq(adis->spi->irq); - - return 0; -} - #define ADIS16475_DATA(_prod_id, _timeouts) \ { \ .msc_ctrl_reg = ADIS16475_REG_MSG_CTRL, \ @@ -640,7 +626,7 @@ static int adis16475_enable_irq(struct adis *adis, bool enable) BIT(ADIS16475_DIAG_STAT_SENSOR) | \ BIT(ADIS16475_DIAG_STAT_MEMORY) | \ BIT(ADIS16475_DIAG_STAT_CLK), \ - .enable_irq = adis16475_enable_irq, \ + .unmasked_drdy = true, \ .timeouts = (_timeouts), \ .burst_reg_cmd = ADIS16475_REG_GLOB_CMD, \ .burst_len = ADIS16475_BURST_MAX_DATA, \ @@ -1254,9 +1240,6 @@ static int adis16475_config_irq_pin(struct adis16475 *st) return -EINVAL; } - /* We cannot mask the interrupt so ensure it's not enabled at request */ - st->adis.irq_flag |= IRQF_NO_AUTOEN; - val = ADIS16475_MSG_CTRL_DR_POL(polarity); ret = __adis_update_bits(&st->adis, ADIS16475_REG_MSG_CTRL, ADIS16475_MSG_CTRL_DR_POL_MASK, val); -- cgit v1.2.3-70-g09d2 From 23a3b67c52d01b9f16d27694d5d871d445ca2f7b Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Fri, 3 Sep 2021 16:14:22 +0200 Subject: iio: adis16460: make use of the new unmasked_drdy flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The library can now handle enabling/disabling IRQs for devices that cannot unmask the data ready pin. Hence there's no need to provide an 'enable_irq' callback anymore. The library will also automatically request the IRQ with 'IRQF_NO_AUTOEN' so that we can also remove that from the driver. Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20210903141423.517028-5-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16460.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/iio/imu/adis16460.c b/drivers/iio/imu/adis16460.c index a6f9fba3e03f..b01988170118 100644 --- a/drivers/iio/imu/adis16460.c +++ b/drivers/iio/imu/adis16460.c @@ -319,20 +319,6 @@ static const struct iio_info adis16460_info = { .debugfs_reg_access = adis_debugfs_reg_access, }; -static int adis16460_enable_irq(struct adis *adis, bool enable) -{ - /* - * There is no way to gate the data-ready signal internally inside the - * ADIS16460 :( - */ - if (enable) - enable_irq(adis->spi->irq); - else - disable_irq(adis->spi->irq); - - return 0; -} - #define ADIS16460_DIAG_STAT_IN_CLK_OOS 7 #define ADIS16460_DIAG_STAT_FLASH_MEM 6 #define ADIS16460_DIAG_STAT_SELF_TEST 5 @@ -373,7 +359,7 @@ static const struct adis_data adis16460_data = { BIT(ADIS16460_DIAG_STAT_OVERRANGE) | BIT(ADIS16460_DIAG_STAT_SPI_COMM) | BIT(ADIS16460_DIAG_STAT_FLASH_UPT), - .enable_irq = adis16460_enable_irq, + .unmasked_drdy = true, .timeouts = &adis16460_timeouts, }; @@ -400,8 +386,6 @@ static int adis16460_probe(struct spi_device *spi) if (ret) return ret; - /* We cannot mask the interrupt, so ensure it isn't auto enabled */ - st->adis.irq_flag |= IRQF_NO_AUTOEN; ret = devm_adis_setup_buffer_and_trigger(&st->adis, indio_dev, NULL); if (ret) return ret; -- cgit v1.2.3-70-g09d2 From 4415381093fc54548f1a0dd3919abd7f5a72a4f2 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Sun, 26 Sep 2021 18:49:32 +0300 Subject: iio: adc: nau7802: convert probe to full device-managed This is a trivial conversion to device-managed functions. The mutex_destroy() calls are redundant, as the data will be free'd anyway. And the IRQ and IIO register functions both have device-managed equivalents. Signed-off-by: Alexandru Ardelean Reviewed-by: Alexandre Belloni Link: https://lore.kernel.org/r/20210926154932.3287590-1-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/nau7802.c | 50 +++++++++-------------------------------------- 1 file changed, 9 insertions(+), 41 deletions(-) diff --git a/drivers/iio/adc/nau7802.c b/drivers/iio/adc/nau7802.c index bb70b51d25b1..976c235f3079 100644 --- a/drivers/iio/adc/nau7802.c +++ b/drivers/iio/adc/nau7802.c @@ -428,8 +428,6 @@ static int nau7802_probe(struct i2c_client *client, st = iio_priv(indio_dev); - i2c_set_clientdata(client, indio_dev); - indio_dev->name = dev_name(&client->dev); indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &nau7802_info; @@ -495,13 +493,13 @@ static int nau7802_probe(struct i2c_client *client, * will enable them back when we will need them.. */ if (client->irq) { - ret = request_threaded_irq(client->irq, - NULL, - nau7802_eoc_trigger, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT | - IRQF_NO_AUTOEN, - client->dev.driver->name, - indio_dev); + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, + nau7802_eoc_trigger, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT | + IRQF_NO_AUTOEN, + client->dev.driver->name, + indio_dev); if (ret) { /* * What may happen here is that our IRQ controller is @@ -526,7 +524,7 @@ static int nau7802_probe(struct i2c_client *client, ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_CTRL2, NAU7802_CTRL2_CRS(st->sample_rate)); if (ret) - goto error_free_irq; + return ret; } /* Setup the ADC channels available on the board */ @@ -536,36 +534,7 @@ static int nau7802_probe(struct i2c_client *client, mutex_init(&st->lock); mutex_init(&st->data_lock); - ret = iio_device_register(indio_dev); - if (ret < 0) { - dev_err(&client->dev, "Couldn't register the device.\n"); - goto error_device_register; - } - - return 0; - -error_device_register: - mutex_destroy(&st->lock); - mutex_destroy(&st->data_lock); -error_free_irq: - if (client->irq) - free_irq(client->irq, indio_dev); - - return ret; -} - -static int nau7802_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct nau7802_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - mutex_destroy(&st->lock); - mutex_destroy(&st->data_lock); - if (client->irq) - free_irq(client->irq, indio_dev); - - return 0; + return devm_iio_device_register(&client->dev, indio_dev); } static const struct i2c_device_id nau7802_i2c_id[] = { @@ -582,7 +551,6 @@ MODULE_DEVICE_TABLE(of, nau7802_dt_ids); static struct i2c_driver nau7802_driver = { .probe = nau7802_probe, - .remove = nau7802_remove, .id_table = nau7802_i2c_id, .driver = { .name = "nau7802", -- cgit v1.2.3-70-g09d2 From a1ff6d25261315ac622f5d4f2288d385affc6a8f Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Sun, 26 Sep 2021 19:21:10 +0300 Subject: iio: adc: max1363: convert probe to full device-managed For this conversion, the 2 regulators (being enabled) require each a devm_add_action_or_reset() hook registration. For the other functions, there are device-managed variants. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210926162110.3536436-1-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1363.c | 82 +++++++++++++++++------------------------------ 1 file changed, 30 insertions(+), 52 deletions(-) diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index f2b576c69949..eef55ed4814a 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -1577,6 +1577,11 @@ static const struct of_device_id max1363_of_match[] = { }; MODULE_DEVICE_TABLE(of, max1363_of_match); +static void max1363_reg_disable(void *reg) +{ + regulator_disable(reg); +} + static int max1363_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1590,7 +1595,8 @@ static int max1363_probe(struct i2c_client *client, if (!indio_dev) return -ENOMEM; - ret = iio_map_array_register(indio_dev, client->dev.platform_data); + ret = devm_iio_map_array_register(&client->dev, indio_dev, + client->dev.platform_data); if (ret < 0) return ret; @@ -1598,17 +1604,16 @@ static int max1363_probe(struct i2c_client *client, mutex_init(&st->lock); st->reg = devm_regulator_get(&client->dev, "vcc"); - if (IS_ERR(st->reg)) { - ret = PTR_ERR(st->reg); - goto error_unregister_map; - } + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); ret = regulator_enable(st->reg); if (ret) - goto error_unregister_map; + return ret; - /* this is only used for device removal purposes */ - i2c_set_clientdata(client, indio_dev); + ret = devm_add_action_or_reset(&client->dev, max1363_reg_disable, st->reg); + if (ret) + return ret; st->chip_info = device_get_match_data(&client->dev); if (!st->chip_info) @@ -1622,13 +1627,17 @@ static int max1363_probe(struct i2c_client *client, ret = regulator_enable(vref); if (ret) - goto error_disable_reg; + return ret; + + ret = devm_add_action_or_reset(&client->dev, max1363_reg_disable, vref); + if (ret) + return ret; + st->vref = vref; vref_uv = regulator_get_voltage(vref); - if (vref_uv <= 0) { - ret = -EINVAL; - goto error_disable_reg; - } + if (vref_uv <= 0) + return -EINVAL; + st->vref_uv = vref_uv; } @@ -1640,13 +1649,12 @@ static int max1363_probe(struct i2c_client *client, st->send = max1363_smbus_send; st->recv = max1363_smbus_recv; } else { - ret = -EOPNOTSUPP; - goto error_disable_reg; + return -EOPNOTSUPP; } ret = max1363_alloc_scan_masks(indio_dev); if (ret) - goto error_disable_reg; + return ret; indio_dev->name = id->name; indio_dev->channels = st->chip_info->channels; @@ -1655,12 +1663,12 @@ static int max1363_probe(struct i2c_client *client, indio_dev->modes = INDIO_DIRECT_MODE; ret = max1363_initial_setup(st); if (ret < 0) - goto error_disable_reg; + return ret; - ret = iio_triggered_buffer_setup(indio_dev, NULL, - &max1363_trigger_handler, NULL); + ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL, + &max1363_trigger_handler, NULL); if (ret) - goto error_disable_reg; + return ret; if (client->irq) { ret = devm_request_threaded_irq(&client->dev, st->client->irq, @@ -1671,39 +1679,10 @@ static int max1363_probe(struct i2c_client *client, indio_dev); if (ret) - goto error_uninit_buffer; + return ret; } - ret = iio_device_register(indio_dev); - if (ret < 0) - goto error_uninit_buffer; - - return 0; - -error_uninit_buffer: - iio_triggered_buffer_cleanup(indio_dev); -error_disable_reg: - if (st->vref) - regulator_disable(st->vref); - regulator_disable(st->reg); -error_unregister_map: - iio_map_array_unregister(indio_dev); - return ret; -} - -static int max1363_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct max1363_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - iio_triggered_buffer_cleanup(indio_dev); - if (st->vref) - regulator_disable(st->vref); - regulator_disable(st->reg); - iio_map_array_unregister(indio_dev); - - return 0; + return devm_iio_device_register(&client->dev, indio_dev); } static const struct i2c_device_id max1363_id[] = { @@ -1756,7 +1735,6 @@ static struct i2c_driver max1363_driver = { .of_match_table = max1363_of_match, }, .probe = max1363_probe, - .remove = max1363_remove, .id_table = max1363_id, }; module_i2c_driver(max1363_driver); -- cgit v1.2.3-70-g09d2 From bdf48481d01df39c4c95cf4a49b877b4a05e8d50 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Sun, 26 Sep 2021 19:28:59 +0300 Subject: iio: adc: rn5t618-adc: use devm_iio_map_array_register() function This driver already hooks a similar unwind callback via devm_add_action_or_reset(). They pretty much do the same thing, so this change converts it to the devm_iio_map_array_register(). Signed-off-by: Alexandru Ardelean Reviewed-by: Andreas Kemnade Link: https://lore.kernel.org/r/20210926162859.3567685-1-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/rn5t618-adc.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/iio/adc/rn5t618-adc.c b/drivers/iio/adc/rn5t618-adc.c index c56fccb2c8e1..7d891b4ea461 100644 --- a/drivers/iio/adc/rn5t618-adc.c +++ b/drivers/iio/adc/rn5t618-adc.c @@ -197,13 +197,6 @@ static struct iio_map rn5t618_maps[] = { { /* sentinel */ } }; -static void unregister_map(void *data) -{ - struct iio_dev *iio_dev = (struct iio_dev *) data; - - iio_map_array_unregister(iio_dev); -} - static int rn5t618_adc_probe(struct platform_device *pdev) { int ret; @@ -254,11 +247,7 @@ static int rn5t618_adc_probe(struct platform_device *pdev) return ret; } - ret = iio_map_array_register(iio_dev, rn5t618_maps); - if (ret < 0) - return ret; - - ret = devm_add_action_or_reset(adc->dev, unregister_map, iio_dev); + ret = devm_iio_map_array_register(adc->dev, iio_dev, rn5t618_maps); if (ret < 0) return ret; -- cgit v1.2.3-70-g09d2 From 461a1c79e7146916a62a7be106f59ed929c73492 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Sun, 26 Sep 2021 22:26:41 +0300 Subject: iio: adc: berlin2-adc: convert probe to device-managed only This driver requires only a devm_add_action_or_reset() hook for the power-down of the device, and then devm_iio_device_register() can be used directly. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210926192642.4051329-1-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/berlin2-adc.c | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/drivers/iio/adc/berlin2-adc.c b/drivers/iio/adc/berlin2-adc.c index 8b04b95b7b7a..03987d7e6b3d 100644 --- a/drivers/iio/adc/berlin2-adc.c +++ b/drivers/iio/adc/berlin2-adc.c @@ -280,6 +280,13 @@ static const struct iio_info berlin2_adc_info = { .read_raw = berlin2_adc_read_raw, }; +static void berlin2_adc_powerdown(void *regmap) +{ + regmap_update_bits(regmap, BERLIN2_SM_CTRL, + BERLIN2_SM_CTRL_ADC_POWER, 0); + +} + static int berlin2_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; @@ -293,7 +300,6 @@ static int berlin2_adc_probe(struct platform_device *pdev) return -ENOMEM; priv = iio_priv(indio_dev); - platform_set_drvdata(pdev, indio_dev); priv->regmap = syscon_node_to_regmap(parent_np); of_node_put(parent_np); @@ -333,29 +339,12 @@ static int berlin2_adc_probe(struct platform_device *pdev) BERLIN2_SM_CTRL_ADC_POWER, BERLIN2_SM_CTRL_ADC_POWER); - ret = iio_device_register(indio_dev); - if (ret) { - /* Power down the ADC */ - regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL, - BERLIN2_SM_CTRL_ADC_POWER, 0); + ret = devm_add_action_or_reset(&pdev->dev, berlin2_adc_powerdown, + priv->regmap); + if (ret) return ret; - } - - return 0; -} - -static int berlin2_adc_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - struct berlin2_adc_priv *priv = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - /* Power down the ADC */ - regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL, - BERLIN2_SM_CTRL_ADC_POWER, 0); - return 0; + return devm_iio_device_register(&pdev->dev, indio_dev); } static const struct of_device_id berlin2_adc_match[] = { @@ -370,7 +359,6 @@ static struct platform_driver berlin2_adc_driver = { .of_match_table = berlin2_adc_match, }, .probe = berlin2_adc_probe, - .remove = berlin2_adc_remove, }; module_platform_driver(berlin2_adc_driver); -- cgit v1.2.3-70-g09d2 From 8ee724ee4ebcb53f109c5d144f1b9eac966cc6f9 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Sun, 26 Sep 2021 22:26:42 +0300 Subject: iio: adc: Kconfig: add COMPILE_TEST dep for berlin2-adc Otherwise most build checks will omit this driver from a compile-test due to it's dependency only on the BERLIN_ARCH symbol. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210926192642.4051329-2-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 0ceea8e69e3c..8bf5b62a73f4 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -354,7 +354,7 @@ config BCM_IPROC_ADC config BERLIN2_ADC tristate "Marvell Berlin2 ADC driver" - depends on ARCH_BERLIN + depends on ARCH_BERLIN || COMPILE_TEST help Marvell Berlin2 ADC driver. This ADC has 8 channels, with one used for temperature measurement. -- cgit v1.2.3-70-g09d2 From 3cc2fd275d94db40a380cf9b2c664eedaa275e9c Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Sun, 26 Sep 2021 22:43:15 +0300 Subject: iio: adc: ad7291: convert probe to device-managed only This is a simple conversion for to device-managed with using devm_request_threaded_irq(), disabling the regulator via a devm_add_action_or_reset() hook and finally using devm_iio_device_register(). The i2c_set_clientdata() call is removed as it becomes redundant after this change. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210926194315.7742-1-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7291.c | 70 +++++++++++++++--------------------------------- 1 file changed, 22 insertions(+), 48 deletions(-) diff --git a/drivers/iio/adc/ad7291.c b/drivers/iio/adc/ad7291.c index 2301a0e27f23..e9129dac762f 100644 --- a/drivers/iio/adc/ad7291.c +++ b/drivers/iio/adc/ad7291.c @@ -460,6 +460,11 @@ static const struct iio_info ad7291_info = { .write_event_value = &ad7291_write_event_value, }; +static void ad7291_reg_disable(void *reg) +{ + regulator_disable(reg); +} + static int ad7291_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -473,8 +478,6 @@ static int ad7291_probe(struct i2c_client *client, chip = iio_priv(indio_dev); mutex_init(&chip->state_lock); - /* this is only used for device removal purposes */ - i2c_set_clientdata(client, indio_dev); chip->client = client; @@ -495,6 +498,11 @@ static int ad7291_probe(struct i2c_client *client, if (ret) return ret; + ret = devm_add_action_or_reset(&client->dev, ad7291_reg_disable, + chip->reg); + if (ret) + return ret; + chip->command |= AD7291_EXT_REF; } @@ -506,58 +514,25 @@ static int ad7291_probe(struct i2c_client *client, indio_dev->modes = INDIO_DIRECT_MODE; ret = ad7291_i2c_write(chip, AD7291_COMMAND, AD7291_RESET); - if (ret) { - ret = -EIO; - goto error_disable_reg; - } + if (ret) + return -EIO; ret = ad7291_i2c_write(chip, AD7291_COMMAND, chip->command); - if (ret) { - ret = -EIO; - goto error_disable_reg; - } + if (ret) + return -EIO; if (client->irq > 0) { - ret = request_threaded_irq(client->irq, - NULL, - &ad7291_event_handler, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - id->name, - indio_dev); + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, + &ad7291_event_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + id->name, + indio_dev); if (ret) - goto error_disable_reg; + return ret; } - ret = iio_device_register(indio_dev); - if (ret) - goto error_unreg_irq; - - return 0; - -error_unreg_irq: - if (client->irq) - free_irq(client->irq, indio_dev); -error_disable_reg: - if (chip->reg) - regulator_disable(chip->reg); - - return ret; -} - -static int ad7291_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct ad7291_chip_info *chip = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - if (client->irq) - free_irq(client->irq, indio_dev); - - if (chip->reg) - regulator_disable(chip->reg); - - return 0; + return devm_iio_device_register(&client->dev, indio_dev); } static const struct i2c_device_id ad7291_id[] = { @@ -579,7 +554,6 @@ static struct i2c_driver ad7291_driver = { .of_match_table = ad7291_of_match, }, .probe = ad7291_probe, - .remove = ad7291_remove, .id_table = ad7291_id, }; module_i2c_driver(ad7291_driver); -- cgit v1.2.3-70-g09d2 From 0fc3c82690fc727ea6c03d9abed7676fe20254a4 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 1 Oct 2021 13:00:18 +0100 Subject: iio: adc: aspeed: Fix spelling mistake "battey" -> "battery" There is a spelling mistake in a dev_warn message. Fix it. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20211001120018.17570-1-colin.king@canonical.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/aspeed_adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 9e2c85c3cbe9..c2705f612495 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -580,7 +580,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) } } else dev_warn(&pdev->dev, - "Failed to enable battey-sensing mode\n"); + "Failed to enable battery-sensing mode\n"); } ret = clk_prepare_enable(data->clk_scaler->clk); -- cgit v1.2.3-70-g09d2 From b18831cc99426a9f6f085a29a53a423aa9b7c62e Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 2 Oct 2021 16:28:03 -0700 Subject: iio: chemical: SENSEAIR_SUNRISE_CO2 depends on I2C Fix kconfig symbol dependency warning: WARNING: unmet direct dependencies detected for REGMAP_I2C Depends on [n]: I2C [=n] Selected by [y]: - SENSEAIR_SUNRISE_CO2 [=y] && IIO [=y] Fixes: c397894e24f1 ("iio: chemical: Add Senseair Sunrise 006-0-007 drive") Signed-off-by: Randy Dunlap Cc: Jacopo Mondi Cc: Jonathan Cameron Cc: linux-iio@vger.kernel.org Link: https://lore.kernel.org/r/20211002232803.5108-1-rdunlap@infradead.org Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig index dc169f9ad4e9..b3d6efe06809 100644 --- a/drivers/iio/chemical/Kconfig +++ b/drivers/iio/chemical/Kconfig @@ -172,6 +172,7 @@ config SPS30_SERIAL config SENSEAIR_SUNRISE_CO2 tristate "Senseair Sunrise 006-0-0007 CO2 sensor" + depends on I2C select REGMAP_I2C help Say yes here to build support for Senseair Sunrise 006-0-0007 CO2 -- cgit v1.2.3-70-g09d2 From 95ec3fdf2b79eaff79e78688bbc2f7dbb98d68b6 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 13 Jun 2021 16:10:36 +0100 Subject: iio: core: Introduce iio_push_to_buffers_with_ts_unaligned() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Whilst it is almost always possible to arrange for scan data to be read directly into a buffer that is suitable for passing to iio_push_to_buffers_with_timestamp(), there are a few places where leading data needs to be skipped over. For these cases introduce a function that will allocate an appropriate sized and aligned bounce buffer (if not already allocated) and copy the unaligned data into that before calling iio_push_to_buffers_with_timestamp() on the bounce buffer. We tie the lifespace of this buffer to that of the iio_dev.dev which should ensure no memory leaks occur. Signed-off-by: Jonathan Cameron Reviewed-by: Nuno Sá Link: https://lore.kernel.org/r/20210613151039.569883-2-jic23@kernel.org --- drivers/iio/industrialio-buffer.c | 46 +++++++++++++++++++++++++++++++++++++++ include/linux/iio/buffer.h | 4 ++++ include/linux/iio/iio-opaque.h | 4 ++++ 3 files changed, 54 insertions(+) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index a95cc2da56be..4209e933ab80 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -1731,6 +1731,52 @@ int iio_push_to_buffers(struct iio_dev *indio_dev, const void *data) } EXPORT_SYMBOL_GPL(iio_push_to_buffers); +/** + * iio_push_to_buffers_with_ts_unaligned() - push to registered buffer, + * no alignment or space requirements. + * @indio_dev: iio_dev structure for device. + * @data: channel data excluding the timestamp. + * @data_sz: size of data. + * @timestamp: timestamp for the sample data. + * + * This special variant of iio_push_to_buffers_with_timestamp() does + * not require space for the timestamp, or 8 byte alignment of data. + * It does however require an allocation on first call and additional + * copies on all calls, so should be avoided if possible. + */ +int iio_push_to_buffers_with_ts_unaligned(struct iio_dev *indio_dev, + const void *data, + size_t data_sz, + int64_t timestamp) +{ + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + + /* + * Conservative estimate - we can always safely copy the minimum + * of either the data provided or the length of the destination buffer. + * This relaxed limit allows the calling drivers to be lax about + * tracking the size of the data they are pushing, at the cost of + * unnecessary copying of padding. + */ + data_sz = min_t(size_t, indio_dev->scan_bytes, data_sz); + if (iio_dev_opaque->bounce_buffer_size != indio_dev->scan_bytes) { + void *bb; + + bb = devm_krealloc(&indio_dev->dev, + iio_dev_opaque->bounce_buffer, + indio_dev->scan_bytes, GFP_KERNEL); + if (!bb) + return -ENOMEM; + iio_dev_opaque->bounce_buffer = bb; + iio_dev_opaque->bounce_buffer_size = indio_dev->scan_bytes; + } + memcpy(iio_dev_opaque->bounce_buffer, data, data_sz); + return iio_push_to_buffers_with_timestamp(indio_dev, + iio_dev_opaque->bounce_buffer, + timestamp); +} +EXPORT_SYMBOL_GPL(iio_push_to_buffers_with_ts_unaligned); + /** * iio_buffer_release() - Free a buffer's resources * @ref: Pointer to the kref embedded in the iio_buffer struct diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h index b6928ac5c63d..451379a3984a 100644 --- a/include/linux/iio/buffer.h +++ b/include/linux/iio/buffer.h @@ -38,6 +38,10 @@ static inline int iio_push_to_buffers_with_timestamp(struct iio_dev *indio_dev, return iio_push_to_buffers(indio_dev, data); } +int iio_push_to_buffers_with_ts_unaligned(struct iio_dev *indio_dev, + const void *data, size_t data_sz, + int64_t timestamp); + bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev, const unsigned long *mask); diff --git a/include/linux/iio/iio-opaque.h b/include/linux/iio/iio-opaque.h index c9504e9da571..2be12b7b5dc5 100644 --- a/include/linux/iio/iio-opaque.h +++ b/include/linux/iio/iio-opaque.h @@ -23,6 +23,8 @@ * @groupcounter: index of next attribute group * @legacy_scan_el_group: attribute group for legacy scan elements attribute group * @legacy_buffer_group: attribute group for legacy buffer attributes group + * @bounce_buffer: for devices that call iio_push_to_buffers_with_timestamp_unaligned() + * @bounce_buffer_size: size of currently allocate bounce buffer * @scan_index_timestamp: cache of the index to the timestamp * @clock_id: timestamping clock posix identifier * @chrdev: associated character device @@ -50,6 +52,8 @@ struct iio_dev_opaque { int groupcounter; struct attribute_group legacy_scan_el_group; struct attribute_group legacy_buffer_group; + void *bounce_buffer; + size_t bounce_buffer_size; unsigned int scan_index_timestamp; clockid_t clock_id; -- cgit v1.2.3-70-g09d2 From cbe5c6977604116db11ae6d60fd1f62beefa2a57 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 13 Jun 2021 16:10:37 +0100 Subject: iio: adc: ti-adc108s102: Fix alignment of buffer pushed to iio buffers. Use the newly introduce iio_push_to_buffers_with_ts_unaligned() function to ensure a bounce buffer is used to provide the required alignment and space padding needed by the IIO core which requires the timestamp is naturally aligned. There will be a performance cost to this change but it will ensure the driver works on platforms that do not support unaligned 8 byte assignments, and with consumer drivers that may assume natural alignment of the timestamp. Issue found as part of an audit of all calls to iio_push_to_buffers_with_timestamp() Fixes: 7e87d11c9bda ("iio: adc: Add support for TI ADC108S102 and ADC128S102") Signed-off-by: Jonathan Cameron Cc: Jan Kiszka Link: https://lore.kernel.org/r/20210613151039.569883-3-jic23@kernel.org --- drivers/iio/adc/ti-adc108s102.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/ti-adc108s102.c b/drivers/iio/adc/ti-adc108s102.c index db902aef2abe..c8e48881c37f 100644 --- a/drivers/iio/adc/ti-adc108s102.c +++ b/drivers/iio/adc/ti-adc108s102.c @@ -75,9 +75,9 @@ struct adc108s102_state { * rx_buf: |XX|R0|R1|R2|R3|R4|R5|R6|R7|tt|tt|tt|tt| * * tx_buf: 8 channel read commands, plus 1 dummy command - * rx_buf: 1 dummy response, 8 channel responses, plus 64-bit timestamp + * rx_buf: 1 dummy response, 8 channel responses */ - __be16 rx_buf[13] ____cacheline_aligned; + __be16 rx_buf[9] ____cacheline_aligned; __be16 tx_buf[9] ____cacheline_aligned; }; @@ -149,9 +149,10 @@ static irqreturn_t adc108s102_trigger_handler(int irq, void *p) goto out_notify; /* Skip the dummy response in the first slot */ - iio_push_to_buffers_with_timestamp(indio_dev, - (u8 *)&st->rx_buf[1], - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts_unaligned(indio_dev, + &st->rx_buf[1], + st->ring_xfer.len - sizeof(st->rx_buf[1]), + iio_get_time_ns(indio_dev)); out_notify: iio_trigger_notify_done(indio_dev->trig); -- cgit v1.2.3-70-g09d2 From b5ca2046c6d481128f1fc5202ffffea0bbf70a45 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 13 Jun 2021 16:10:38 +0100 Subject: iio: gyro: mpu3050: Fix alignment and size issues with buffers. Fix a set of closely related issues. 1. When using fifo_values() there was not enough space for the timestamp to be inserted by iio_push_to_buffers_with_timestamp() 2. fifo_values() did not meet the alignment requirement of iio_push_to_buffers_with_timestamp() 3. hw_values did not meet the alignment requirement either. 1 and 2 fixed by using new iio_push_to_buffers_with_ts_unaligned() which has no alignment or space padding requirements. 3 fixed by introducing a structure that makes the space and alignment requirements explicit. Fixes: 3904b28efb2c ("iio: gyro: Add driver for the MPU-3050 gyroscope") Signed-off-by: Jonathan Cameron Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20210613151039.569883-4-jic23@kernel.org --- drivers/iio/gyro/mpu3050-core.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c index 3225de1f023b..ea387efab62d 100644 --- a/drivers/iio/gyro/mpu3050-core.c +++ b/drivers/iio/gyro/mpu3050-core.c @@ -471,13 +471,10 @@ static irqreturn_t mpu3050_trigger_handler(int irq, void *p) struct iio_dev *indio_dev = pf->indio_dev; struct mpu3050 *mpu3050 = iio_priv(indio_dev); int ret; - /* - * Temperature 1*16 bits - * Three axes 3*16 bits - * Timestamp 64 bits (4*16 bits) - * Sum total 8*16 bits - */ - __be16 hw_values[8]; + struct { + __be16 chans[4]; + s64 timestamp __aligned(8); + } scan; s64 timestamp; unsigned int datums_from_fifo = 0; @@ -572,9 +569,10 @@ static irqreturn_t mpu3050_trigger_handler(int irq, void *p) fifo_values[4]); /* Index past the footer (fifo_values[0]) and push */ - iio_push_to_buffers_with_timestamp(indio_dev, - &fifo_values[1], - timestamp); + iio_push_to_buffers_with_ts_unaligned(indio_dev, + &fifo_values[1], + sizeof(__be16) * 4, + timestamp); fifocnt -= toread; datums_from_fifo++; @@ -632,15 +630,15 @@ static irqreturn_t mpu3050_trigger_handler(int irq, void *p) goto out_trigger_unlock; } - ret = regmap_bulk_read(mpu3050->map, MPU3050_TEMP_H, &hw_values, - sizeof(hw_values)); + ret = regmap_bulk_read(mpu3050->map, MPU3050_TEMP_H, scan.chans, + sizeof(scan.chans)); if (ret) { dev_err(mpu3050->dev, "error reading axis data\n"); goto out_trigger_unlock; } - iio_push_to_buffers_with_timestamp(indio_dev, hw_values, timestamp); + iio_push_to_buffers_with_timestamp(indio_dev, &scan, timestamp); out_trigger_unlock: mutex_unlock(&mpu3050->lock); -- cgit v1.2.3-70-g09d2 From d7a83bc38d8dd11b3df7e0db3739be6ced1667af Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 13 Jun 2021 16:10:39 +0100 Subject: iio: imu: adis16400: Fix buffer alignment requirements. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iio_push_to_buffers_with_timestamp() requires that the buffer is 8 byte alignment to ensure an inserted timestamp is naturally aligned. This requirement was not met here when burst mode is in use beause of a leading u16. Use the new iio_push_to_buffers_with_ts_unaligned() function that has more relaxed requirements. It is somewhat complex to access that actual data length, but a safe bound can be found by using scan_bytes - sizeof(timestamp) so that is used in this path. More efficient approaches exist, but this ensure correctness at the cost of using a bounce buffer. Fixes: 5075e0720d93 ("iio: imu: adis: generalize burst mode support") Signed-off-by: Jonathan Cameron Reviewed-by: Nuno Sá Link: https://lore.kernel.org/r/20210613151039.569883-5-jic23@kernel.org --- drivers/iio/imu/adis16400.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/iio/imu/adis16400.c b/drivers/iio/imu/adis16400.c index b12917a7cb60..9fd30e62d6e8 100644 --- a/drivers/iio/imu/adis16400.c +++ b/drivers/iio/imu/adis16400.c @@ -641,13 +641,23 @@ static irqreturn_t adis16400_trigger_handler(int irq, void *p) if (ret) dev_err(&adis->spi->dev, "Failed to read data: %d\n", ret); - if (st->variant->flags & ADIS16400_BURST_DIAG_STAT) + if (st->variant->flags & ADIS16400_BURST_DIAG_STAT) { buffer = adis->buffer + sizeof(u16); - else - buffer = adis->buffer; + /* + * The size here is always larger than, or equal to the true + * size of the channel data. This may result in a larger copy + * than necessary, but as the target buffer will be + * buffer->scan_bytes this will be safe. + */ + iio_push_to_buffers_with_ts_unaligned(indio_dev, buffer, + indio_dev->scan_bytes - sizeof(pf->timestamp), + pf->timestamp); + } else { + iio_push_to_buffers_with_timestamp(indio_dev, + adis->buffer, + pf->timestamp); + } - iio_push_to_buffers_with_timestamp(indio_dev, buffer, - pf->timestamp); iio_trigger_notify_done(indio_dev->trig); -- cgit v1.2.3-70-g09d2 From 2021ef0609009806829aa831f90c815bce1fe756 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 4 Oct 2021 16:44:54 +0300 Subject: iio: adc: max1027: fix error code in max1027_wait_eoc() Return -ETIMEDOUT on timeout instead of success. Fixes: 1f7b4048b31b ("iio: adc: max1027: Use the EOC IRQ when populated for single reads") Signed-off-by: Dan Carpenter Reviewed-by: Miquel Raynal Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1027.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index fa4a2f48a36a..cb60b76a4d3a 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -287,7 +287,7 @@ static int max1027_wait_eoc(struct iio_dev *indio_dev) msecs_to_jiffies(1000)); reinit_completion(&st->complete); if (!ret) - return ret; + return -ETIMEDOUT; } else { if (indio_dev->active_scan_mask) conversion_time *= hweight32(*indio_dev->active_scan_mask); -- cgit v1.2.3-70-g09d2 From 6b104e7895ab16b9b7f466c5f2ca282b87f661e8 Mon Sep 17 00:00:00 2001 From: Florian Boor Date: Thu, 30 Sep 2021 12:42:48 +0200 Subject: iio: adc: ad799x: Implement selecting external reference voltage input on AD7991, AD7995 and AD7999. Make use of the AD7991_REF_SEL bit and support using the external reference voltage if 'vref-supply' is present. Use VCC voltage supply as reference if no extra reference is supplied. Signed-off-by: Florian Boor Link: https://lore.kernel.org/r/20210930104249.2924336-1-florian.boor@kernelconcepts.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad799x.c | 68 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c index 18bf8386d50a..220228c375d3 100644 --- a/drivers/iio/adc/ad799x.c +++ b/drivers/iio/adc/ad799x.c @@ -299,7 +299,11 @@ static int ad799x_read_raw(struct iio_dev *indio_dev, GENMASK(chan->scan_type.realbits - 1, 0); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - ret = regulator_get_voltage(st->vref); + if (st->vref) + ret = regulator_get_voltage(st->vref); + else + ret = regulator_get_voltage(st->reg); + if (ret < 0) return ret; *val = ret / 1000; @@ -770,6 +774,7 @@ static int ad799x_probe(struct i2c_client *client, const struct i2c_device_id *id) { int ret; + int extra_config = 0; struct ad799x_state *st; struct iio_dev *indio_dev; const struct ad799x_chip_info *chip_info = @@ -797,14 +802,36 @@ static int ad799x_probe(struct i2c_client *client, ret = regulator_enable(st->reg); if (ret) return ret; - st->vref = devm_regulator_get(&client->dev, "vref"); + + /* check if an external reference is supplied */ + st->vref = devm_regulator_get_optional(&client->dev, "vref"); + if (IS_ERR(st->vref)) { - ret = PTR_ERR(st->vref); - goto error_disable_reg; + if (PTR_ERR(st->vref) == -ENODEV) { + st->vref = NULL; + dev_info(&client->dev, "Using VCC reference voltage\n"); + } else { + ret = PTR_ERR(st->vref); + goto error_disable_reg; + } + } + + if (st->vref) { + /* + * Use external reference voltage if supported by hardware. + * This is optional if voltage / regulator present, use VCC otherwise. + */ + if ((st->id == ad7991) || (st->id == ad7995) || (st->id == ad7999)) { + dev_info(&client->dev, "Using external reference voltage\n"); + extra_config |= AD7991_REF_SEL; + ret = regulator_enable(st->vref); + if (ret) + goto error_disable_reg; + } else { + st->vref = NULL; + dev_warn(&client->dev, "Supplied reference not supported\n"); + } } - ret = regulator_enable(st->vref); - if (ret) - goto error_disable_reg; st->client = client; @@ -815,7 +842,7 @@ static int ad799x_probe(struct i2c_client *client, indio_dev->channels = st->chip_config->channel; indio_dev->num_channels = chip_info->num_channels; - ret = ad799x_update_config(st, st->chip_config->default_config); + ret = ad799x_update_config(st, st->chip_config->default_config | extra_config); if (ret) goto error_disable_vref; @@ -845,7 +872,8 @@ static int ad799x_probe(struct i2c_client *client, error_cleanup_ring: iio_triggered_buffer_cleanup(indio_dev); error_disable_vref: - regulator_disable(st->vref); + if (st->vref) + regulator_disable(st->vref); error_disable_reg: regulator_disable(st->reg); @@ -860,7 +888,8 @@ static int ad799x_remove(struct i2c_client *client) iio_device_unregister(indio_dev); iio_triggered_buffer_cleanup(indio_dev); - regulator_disable(st->vref); + if (st->vref) + regulator_disable(st->vref); regulator_disable(st->reg); kfree(st->rx_buf); @@ -872,7 +901,8 @@ static int __maybe_unused ad799x_suspend(struct device *dev) struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); struct ad799x_state *st = iio_priv(indio_dev); - regulator_disable(st->vref); + if (st->vref) + regulator_disable(st->vref); regulator_disable(st->reg); return 0; @@ -889,17 +919,21 @@ static int __maybe_unused ad799x_resume(struct device *dev) dev_err(dev, "Unable to enable vcc regulator\n"); return ret; } - ret = regulator_enable(st->vref); - if (ret) { - regulator_disable(st->reg); - dev_err(dev, "Unable to enable vref regulator\n"); - return ret; + + if (st->vref) { + ret = regulator_enable(st->vref); + if (ret) { + regulator_disable(st->reg); + dev_err(dev, "Unable to enable vref regulator\n"); + return ret; + } } /* resync config */ ret = ad799x_update_config(st, st->config); if (ret) { - regulator_disable(st->vref); + if (st->vref) + regulator_disable(st->vref); regulator_disable(st->reg); return ret; } -- cgit v1.2.3-70-g09d2 From 04892d253374ff10a808a5e2b36dd398300ca241 Mon Sep 17 00:00:00 2001 From: Florian Boor Date: Thu, 30 Sep 2021 12:42:49 +0200 Subject: dt-bindings: iio: ad779x: Add binding document MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New binding documentation for AD799x series of I²C ADC ICs. Signed-off-by: Florian Boor Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20210930104249.2924336-2-florian.boor@kernelconcepts.de Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/adi,ad799x.yaml | 73 ++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ad799x.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad799x.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad799x.yaml new file mode 100644 index 000000000000..29641ce7175b --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad799x.yaml @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/adi,ad799x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD799x analog to digital converters + +maintainers: + - Michael Hennerich + +description: | + Support for Analog Devices AD7991, AD7992, AD7993, AD7994, AD7995, AD7997, AD7998, + AD7999 and similar analog to digital converters. + Specifications on the converters can be found at: + AD7991, AD7995, AD7999: + https://www.analog.com/media/en/technical-documentation/data-sheets/AD7991_7995_7999.pdf + AD7992: + https://www.analog.com/media/en/technical-documentation/data-sheets/AD7992.pdf + AD7993, AD7994: + https://www.analog.com/media/en/technical-documentation/data-sheets/AD7993_7994.pdf + AD7997, AD7998: + https://www.analog.com/media/en/technical-documentation/data-sheets/AD7997_7998.pdf + +properties: + compatible: + enum: + - adi,ad7991 + - adi,ad7992 + - adi,ad7993 + - adi,ad7994 + - adi,ad7995 + - adi,ad7997 + - adi,ad7998 + - adi,ad7999 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + vcc-supply: + description: + ADC power supply + + vref-supply: + description: + ADC reference voltage supply, optional for AD7991, AD7995 and AD7999 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + adc1: adc@28 { + reg = <0x28>; + compatible = "adi,ad7991"; + interrupts = <13 2>; + interrupt-parent = <&gpio6>; + + vcc-supply = <&vcc_3v3>; + vref-supply = <&adc_vref>; + }; + }; +... -- cgit v1.2.3-70-g09d2 From f80d6061dab190e37286f873b0fc0af3dd1c3999 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Tue, 28 Sep 2021 09:38:54 +0800 Subject: iio: dac: ad8801: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20210928013902.1341-1-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad8801.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/iio/dac/ad8801.c b/drivers/iio/dac/ad8801.c index 6354b7c8f052..5ecfdad54dec 100644 --- a/drivers/iio/dac/ad8801.c +++ b/drivers/iio/dac/ad8801.c @@ -123,10 +123,9 @@ static int ad8801_probe(struct spi_device *spi) id = spi_get_device_id(spi); state->vrefh_reg = devm_regulator_get(&spi->dev, "vrefh"); - if (IS_ERR(state->vrefh_reg)) { - dev_err(&spi->dev, "Vrefh regulator not specified\n"); - return PTR_ERR(state->vrefh_reg); - } + if (IS_ERR(state->vrefh_reg)) + return dev_err_probe(&spi->dev, PTR_ERR(state->vrefh_reg), + "Vrefh regulator not specified\n"); ret = regulator_enable(state->vrefh_reg); if (ret) { @@ -146,8 +145,8 @@ static int ad8801_probe(struct spi_device *spi) if (id->driver_data == ID_AD8803) { state->vrefl_reg = devm_regulator_get(&spi->dev, "vrefl"); if (IS_ERR(state->vrefl_reg)) { - dev_err(&spi->dev, "Vrefl regulator not specified\n"); - ret = PTR_ERR(state->vrefl_reg); + ret = dev_err_probe(&spi->dev, PTR_ERR(state->vrefl_reg), + "Vrefl regulator not specified\n"); goto error_disable_vrefh_reg; } -- cgit v1.2.3-70-g09d2 From 7cf5307c004075627ff38c2671c8c9b186b81a4b Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Tue, 28 Sep 2021 09:38:55 +0800 Subject: iio: dac: lpc18xx_dac: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20210928013902.1341-2-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/lpc18xx_dac.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/iio/dac/lpc18xx_dac.c b/drivers/iio/dac/lpc18xx_dac.c index 9e38607a189e..5502e4f62f0d 100644 --- a/drivers/iio/dac/lpc18xx_dac.c +++ b/drivers/iio/dac/lpc18xx_dac.c @@ -121,16 +121,14 @@ static int lpc18xx_dac_probe(struct platform_device *pdev) return PTR_ERR(dac->base); dac->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(dac->clk)) { - dev_err(&pdev->dev, "error getting clock\n"); - return PTR_ERR(dac->clk); - } + if (IS_ERR(dac->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(dac->clk), + "error getting clock\n"); dac->vref = devm_regulator_get(&pdev->dev, "vref"); - if (IS_ERR(dac->vref)) { - dev_err(&pdev->dev, "error getting regulator\n"); - return PTR_ERR(dac->vref); - } + if (IS_ERR(dac->vref)) + return dev_err_probe(&pdev->dev, PTR_ERR(dac->vref), + "error getting regulator\n"); indio_dev->name = dev_name(&pdev->dev); indio_dev->info = &lpc18xx_dac_info; -- cgit v1.2.3-70-g09d2 From 7bb9df2d58121b83bc5b2e42e26f6b338444e3d7 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Tue, 28 Sep 2021 09:38:56 +0800 Subject: iio: dac: ltc1660: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Reviewed-by: Marcus Folkesson Link: https://lore.kernel.org/r/20210928013902.1341-3-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ltc1660.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/iio/dac/ltc1660.c b/drivers/iio/dac/ltc1660.c index dc10188540ca..f6ec9bf5815e 100644 --- a/drivers/iio/dac/ltc1660.c +++ b/drivers/iio/dac/ltc1660.c @@ -172,10 +172,9 @@ static int ltc1660_probe(struct spi_device *spi) } priv->vref_reg = devm_regulator_get(&spi->dev, "vref"); - if (IS_ERR(priv->vref_reg)) { - dev_err(&spi->dev, "vref regulator not specified\n"); - return PTR_ERR(priv->vref_reg); - } + if (IS_ERR(priv->vref_reg)) + return dev_err_probe(&spi->dev, PTR_ERR(priv->vref_reg), + "vref regulator not specified\n"); ret = regulator_enable(priv->vref_reg); if (ret) { -- cgit v1.2.3-70-g09d2 From 2b87c267d84ffba458efafb0b342fb082ddb24eb Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Tue, 28 Sep 2021 09:38:57 +0800 Subject: iio: dac: ds4424: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20210928013902.1341-4-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ds4424.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/iio/dac/ds4424.c b/drivers/iio/dac/ds4424.c index 79527fbc250a..5a5e967b0be4 100644 --- a/drivers/iio/dac/ds4424.c +++ b/drivers/iio/dac/ds4424.c @@ -232,12 +232,9 @@ static int ds4424_probe(struct i2c_client *client, indio_dev->name = id->name; data->vcc_reg = devm_regulator_get(&client->dev, "vcc"); - if (IS_ERR(data->vcc_reg)) { - dev_err(&client->dev, - "Failed to get vcc-supply regulator. err: %ld\n", - PTR_ERR(data->vcc_reg)); - return PTR_ERR(data->vcc_reg); - } + if (IS_ERR(data->vcc_reg)) + return dev_err_probe(&client->dev, PTR_ERR(data->vcc_reg), + "Failed to get vcc-supply regulator.\n"); mutex_init(&data->lock); ret = regulator_enable(data->vcc_reg); -- cgit v1.2.3-70-g09d2 From d5c1118f6faf41704484b86e160769a2ccec3c39 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Tue, 28 Sep 2021 09:38:58 +0800 Subject: iio: dac: max5821: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20210928013902.1341-5-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/max5821.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/iio/dac/max5821.c b/drivers/iio/dac/max5821.c index bd0b7f361154..7da4710a6408 100644 --- a/drivers/iio/dac/max5821.c +++ b/drivers/iio/dac/max5821.c @@ -321,12 +321,9 @@ static int max5821_probe(struct i2c_client *client, } data->vref_reg = devm_regulator_get(&client->dev, "vref"); - if (IS_ERR(data->vref_reg)) { - ret = PTR_ERR(data->vref_reg); - dev_err(&client->dev, - "Failed to get vref regulator: %d\n", ret); - return ret; - } + if (IS_ERR(data->vref_reg)) + return dev_err_probe(&client->dev, PTR_ERR(data->vref_reg), + "Failed to get vref regulator\n"); ret = regulator_enable(data->vref_reg); if (ret) { -- cgit v1.2.3-70-g09d2 From c0e9ef04a9788d7278dfb103be9b2932cf5a5086 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Tue, 28 Sep 2021 09:38:59 +0800 Subject: iio: dac: mcp4922: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20210928013902.1341-6-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/mcp4922.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/iio/dac/mcp4922.c b/drivers/iio/dac/mcp4922.c index c4e430b4050e..0ae414ee1716 100644 --- a/drivers/iio/dac/mcp4922.c +++ b/drivers/iio/dac/mcp4922.c @@ -130,10 +130,9 @@ static int mcp4922_probe(struct spi_device *spi) state = iio_priv(indio_dev); state->spi = spi; state->vref_reg = devm_regulator_get(&spi->dev, "vref"); - if (IS_ERR(state->vref_reg)) { - dev_err(&spi->dev, "Vref regulator not specified\n"); - return PTR_ERR(state->vref_reg); - } + if (IS_ERR(state->vref_reg)) + return dev_err_probe(&spi->dev, PTR_ERR(state->vref_reg), + "Vref regulator not specified\n"); ret = regulator_enable(state->vref_reg); if (ret) { -- cgit v1.2.3-70-g09d2 From 7051c1215c4bae95f861c641ea0a164ec25f18bf Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Tue, 28 Sep 2021 09:39:00 +0800 Subject: iio: dac: stm32-dac: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20210928013902.1341-7-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/stm32-dac-core.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/iio/dac/stm32-dac-core.c b/drivers/iio/dac/stm32-dac-core.c index 9a6a68b11b2a..bd7a3b20e645 100644 --- a/drivers/iio/dac/stm32-dac-core.c +++ b/drivers/iio/dac/stm32-dac-core.c @@ -116,18 +116,12 @@ static int stm32_dac_probe(struct platform_device *pdev) priv->common.regmap = regmap; priv->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(priv->pclk)) { - ret = PTR_ERR(priv->pclk); - dev_err(dev, "pclk get failed\n"); - return ret; - } + if (IS_ERR(priv->pclk)) + return dev_err_probe(dev, PTR_ERR(priv->pclk), "pclk get failed\n"); priv->vref = devm_regulator_get(dev, "vref"); - if (IS_ERR(priv->vref)) { - ret = PTR_ERR(priv->vref); - dev_err(dev, "vref get failed, %d\n", ret); - return ret; - } + if (IS_ERR(priv->vref)) + return dev_err_probe(dev, PTR_ERR(priv->vref), "vref get failed\n"); pm_runtime_get_noresume(dev); pm_runtime_set_active(dev); -- cgit v1.2.3-70-g09d2 From d1249ba70dbfe73c074d8b02f827526bb04a74df Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Tue, 28 Sep 2021 09:39:01 +0800 Subject: iio: dac: ti-dac7311: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20210928013902.1341-8-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ti-dac7311.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/iio/dac/ti-dac7311.c b/drivers/iio/dac/ti-dac7311.c index 9d0b253be841..09218c3029f0 100644 --- a/drivers/iio/dac/ti-dac7311.c +++ b/drivers/iio/dac/ti-dac7311.c @@ -266,10 +266,9 @@ static int ti_dac_probe(struct spi_device *spi) ti_dac->resolution = spec->resolution; ti_dac->vref = devm_regulator_get(dev, "vref"); - if (IS_ERR(ti_dac->vref)) { - dev_err(dev, "error to get regulator\n"); - return PTR_ERR(ti_dac->vref); - } + if (IS_ERR(ti_dac->vref)) + return dev_err_probe(dev, PTR_ERR(ti_dac->vref), + "error to get regulator\n"); ret = regulator_enable(ti_dac->vref); if (ret < 0) { -- cgit v1.2.3-70-g09d2 From 4dff754876959b3f3b354800089bc8aaa3ec1d95 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Tue, 28 Sep 2021 09:40:53 +0800 Subject: iio: st_sensors: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20210928014055.1431-1-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/common/st_sensors/st_sensors_core.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index a5a140de9a23..1de395bda03e 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -228,10 +228,10 @@ int st_sensors_power_enable(struct iio_dev *indio_dev) /* Regulators not mandatory, but if requested we should enable them. */ pdata->vdd = devm_regulator_get(parent, "vdd"); - if (IS_ERR(pdata->vdd)) { - dev_err(&indio_dev->dev, "unable to get Vdd supply\n"); - return PTR_ERR(pdata->vdd); - } + if (IS_ERR(pdata->vdd)) + return dev_err_probe(&indio_dev->dev, PTR_ERR(pdata->vdd), + "unable to get Vdd supply\n"); + err = regulator_enable(pdata->vdd); if (err != 0) { dev_warn(&indio_dev->dev, @@ -244,10 +244,10 @@ int st_sensors_power_enable(struct iio_dev *indio_dev) return err; pdata->vdd_io = devm_regulator_get(parent, "vddio"); - if (IS_ERR(pdata->vdd_io)) { - dev_err(&indio_dev->dev, "unable to get Vdd_IO supply\n"); - return PTR_ERR(pdata->vdd_io); - } + if (IS_ERR(pdata->vdd_io)) + return dev_err_probe(&indio_dev->dev, PTR_ERR(pdata->vdd_io), + "unable to get Vdd_IO supply\n"); + err = regulator_enable(pdata->vdd_io); if (err != 0) { dev_warn(&indio_dev->dev, -- cgit v1.2.3-70-g09d2 From b42baaa3e27739cd5c3a521a2b8352d2c9aeb966 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Tue, 28 Sep 2021 09:40:54 +0800 Subject: iio: st_lsm9ds0: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20210928014055.1431-2-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c index b3a43a3b04ff..9fb06b7cde3c 100644 --- a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c @@ -24,10 +24,10 @@ static int st_lsm9ds0_power_enable(struct device *dev, struct st_lsm9ds0 *lsm9ds /* Regulators not mandatory, but if requested we should enable them. */ lsm9ds0->vdd = devm_regulator_get(dev, "vdd"); - if (IS_ERR(lsm9ds0->vdd)) { - dev_err(dev, "unable to get Vdd supply\n"); - return PTR_ERR(lsm9ds0->vdd); - } + if (IS_ERR(lsm9ds0->vdd)) + return dev_err_probe(dev, PTR_ERR(lsm9ds0->vdd), + "unable to get Vdd supply\n"); + ret = regulator_enable(lsm9ds0->vdd); if (ret) { dev_warn(dev, "Failed to enable specified Vdd supply\n"); @@ -36,9 +36,9 @@ static int st_lsm9ds0_power_enable(struct device *dev, struct st_lsm9ds0 *lsm9ds lsm9ds0->vdd_io = devm_regulator_get(dev, "vddio"); if (IS_ERR(lsm9ds0->vdd_io)) { - dev_err(dev, "unable to get Vdd_IO supply\n"); regulator_disable(lsm9ds0->vdd); - return PTR_ERR(lsm9ds0->vdd_io); + return dev_err_probe(dev, PTR_ERR(lsm9ds0->vdd_io), + "unable to get Vdd_IO supply\n"); } ret = regulator_enable(lsm9ds0->vdd_io); if (ret) { -- cgit v1.2.3-70-g09d2 From 8025ea5095338bd68936de1dded91ea62092b299 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Tue, 28 Sep 2021 09:44:01 +0800 Subject: iio: health: afe4403: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20210928014403.1563-1-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/health/afe4403.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index d4921385aaf7..e89185544d0f 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -487,10 +487,10 @@ static int afe4403_probe(struct spi_device *spi) } afe->regulator = devm_regulator_get(afe->dev, "tx_sup"); - if (IS_ERR(afe->regulator)) { - dev_err(afe->dev, "Unable to get regulator\n"); - return PTR_ERR(afe->regulator); - } + if (IS_ERR(afe->regulator)) + return dev_err_probe(afe->dev, PTR_ERR(afe->regulator), + "Unable to get regulator\n"); + ret = regulator_enable(afe->regulator); if (ret) { dev_err(afe->dev, "Unable to enable regulator\n"); -- cgit v1.2.3-70-g09d2 From 842f221d8ca94862ca23639c5eb70854917b2ece Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Tue, 28 Sep 2021 09:44:02 +0800 Subject: iio: health: afe4404: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20210928014403.1563-2-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/health/afe4404.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index d8a27dfe074a..7ef3f5e34de5 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -494,10 +494,10 @@ static int afe4404_probe(struct i2c_client *client, } afe->regulator = devm_regulator_get(afe->dev, "tx_sup"); - if (IS_ERR(afe->regulator)) { - dev_err(afe->dev, "Unable to get regulator\n"); - return PTR_ERR(afe->regulator); - } + if (IS_ERR(afe->regulator)) + return dev_err_probe(afe->dev, PTR_ERR(afe->regulator), + "Unable to get regulator\n"); + ret = regulator_enable(afe->regulator); if (ret) { dev_err(afe->dev, "Unable to enable regulator\n"); -- cgit v1.2.3-70-g09d2 From 8283b95455ca962751d6851bf085dbe0b1e88a95 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Tue, 28 Sep 2021 09:41:53 +0800 Subject: iio: light: cm36651: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20210928014156.1491-2-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/cm36651.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/iio/light/cm36651.c b/drivers/iio/light/cm36651.c index fd83a19929bc..89f5e48a6642 100644 --- a/drivers/iio/light/cm36651.c +++ b/drivers/iio/light/cm36651.c @@ -632,10 +632,9 @@ static int cm36651_probe(struct i2c_client *client, cm36651 = iio_priv(indio_dev); cm36651->vled_reg = devm_regulator_get(&client->dev, "vled"); - if (IS_ERR(cm36651->vled_reg)) { - dev_err(&client->dev, "get regulator vled failed\n"); - return PTR_ERR(cm36651->vled_reg); - } + if (IS_ERR(cm36651->vled_reg)) + return dev_err_probe(&client->dev, PTR_ERR(cm36651->vled_reg), + "get regulator vled failed\n"); ret = regulator_enable(cm36651->vled_reg); if (ret) { -- cgit v1.2.3-70-g09d2 From c1b4de6a03e69ae08d4fe82e76915f70ee4e1f42 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Tue, 28 Sep 2021 09:41:55 +0800 Subject: iio: light: noa1305: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20210928014156.1491-4-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/noa1305.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/iio/light/noa1305.c b/drivers/iio/light/noa1305.c index a308fbc2fc7b..ee81fe083e4c 100644 --- a/drivers/iio/light/noa1305.c +++ b/drivers/iio/light/noa1305.c @@ -217,10 +217,9 @@ static int noa1305_probe(struct i2c_client *client, priv = iio_priv(indio_dev); priv->vin_reg = devm_regulator_get(&client->dev, "vin"); - if (IS_ERR(priv->vin_reg)) { - dev_err(&client->dev, "get regulator vin failed\n"); - return PTR_ERR(priv->vin_reg); - } + if (IS_ERR(priv->vin_reg)) + return dev_err_probe(&client->dev, PTR_ERR(priv->vin_reg), + "get regulator vin failed\n"); ret = regulator_enable(priv->vin_reg); if (ret) { -- cgit v1.2.3-70-g09d2 From a467ab2200980de03e0cea9c1594a85805c66cef Mon Sep 17 00:00:00 2001 From: Roan van Dijk Date: Fri, 8 Oct 2021 12:17:03 +0200 Subject: dt-bindings: iio: chemical: sensirion,scd4x: Add yaml description Add documentation for the SCD4x carbon dioxide sensor from Sensirion. Reviewed-by: Rob Herring Signed-off-by: Roan van Dijk Link: https://lore.kernel.org/r/20211008101706.755942-2-roan@protonic.nl Signed-off-by: Jonathan Cameron --- .../bindings/iio/chemical/sensirion,scd4x.yaml | 46 ++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/chemical/sensirion,scd4x.yaml diff --git a/Documentation/devicetree/bindings/iio/chemical/sensirion,scd4x.yaml b/Documentation/devicetree/bindings/iio/chemical/sensirion,scd4x.yaml new file mode 100644 index 000000000000..798f48d05279 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/chemical/sensirion,scd4x.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/chemical/sensirion,scd4x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sensirion SCD4X carbon dioxide sensor + +maintainers: + - Roan van Dijk + +description: | + Air quality sensor capable of measuring co2 concentration, temperature + and relative humidity. + +properties: + compatible: + enum: + - sensirion,scd40 + - sensirion,scd41 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + vdd-supply: true + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + co2-sensor@62 { + compatible = "sensirion,scd41"; + reg = <0x62>; + }; + }; -- cgit v1.2.3-70-g09d2 From 2be47f8d622b53ba340fe4bf62be8038d4646158 Mon Sep 17 00:00:00 2001 From: Roan van Dijk Date: Fri, 8 Oct 2021 12:17:04 +0200 Subject: MAINTAINERS: Add myself as maintainer of the scd4x driver Signed-off-by: Roan van Dijk Link: https://lore.kernel.org/r/20211008101706.755942-3-roan@protonic.nl Signed-off-by: Jonathan Cameron --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 07904ef5a039..f925f304d33d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16910,6 +16910,12 @@ F: drivers/iio/chemical/scd30_core.c F: drivers/iio/chemical/scd30_i2c.c F: drivers/iio/chemical/scd30_serial.c +SENSIRION SCD4X CARBON DIOXIDE SENSOR DRIVER +M: Roan van Dijk +S: Maintained +F: Documentation/devicetree/bindings/iio/chemical/sensirion,scd4x.yaml +F: drivers/iio/chemical/scd4x.c + SENSIRION SGP40 GAS SENSOR DRIVER M: Andreas Klinger S: Maintained -- cgit v1.2.3-70-g09d2 From 49d22b695cbb690b1d1a9d21b61eaf8f61a525d4 Mon Sep 17 00:00:00 2001 From: Roan van Dijk Date: Fri, 8 Oct 2021 12:17:05 +0200 Subject: drivers: iio: chemical: Add support for Sensirion SCD4x CO2 sensor This is a driver for the SCD4x CO2 sensor from Sensirion. The sensor is able to measure CO2 concentration, temperature and relative humdity. The sensor uses a photoacoustic principle for measuring CO2 concentration. An I2C interface is supported by this driver in order to communicate with the sensor. Signed-off-by: Roan van Dijk Link: https://lore.kernel.org/r/20211008101706.755942-4-roan@protonic.nl Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/Kconfig | 13 + drivers/iio/chemical/Makefile | 1 + drivers/iio/chemical/scd4x.c | 691 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 705 insertions(+) create mode 100644 drivers/iio/chemical/scd4x.c diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig index b3d6efe06809..c30657e10ee1 100644 --- a/drivers/iio/chemical/Kconfig +++ b/drivers/iio/chemical/Kconfig @@ -118,6 +118,19 @@ config SCD30_SERIAL To compile this driver as a module, choose M here: the module will be called scd30_serial. +config SCD4X + tristate "SCD4X carbon dioxide sensor driver" + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + depends on I2C + select CRC8 + help + Say Y here to build support for the Sensirion SCD4X sensor with carbon + dioxide, relative humidity and temperature sensing capabilities. + + To compile this driver as a module, choose M here: the module will + be called scd4x. + config SENSIRION_SGP30 tristate "Sensirion SGPxx gas sensors" depends on I2C diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile index 2569f52432f0..a11e777a7a00 100644 --- a/drivers/iio/chemical/Makefile +++ b/drivers/iio/chemical/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_PMS7003) += pms7003.o obj-$(CONFIG_SCD30_CORE) += scd30_core.o obj-$(CONFIG_SCD30_I2C) += scd30_i2c.o obj-$(CONFIG_SCD30_SERIAL) += scd30_serial.o +obj-$(CONFIG_SCD4X) += scd4x.o obj-$(CONFIG_SENSEAIR_SUNRISE_CO2) += sunrise_co2.o obj-$(CONFIG_SENSIRION_SGP30) += sgp30.o obj-$(CONFIG_SENSIRION_SGP40) += sgp40.o diff --git a/drivers/iio/chemical/scd4x.c b/drivers/iio/chemical/scd4x.c new file mode 100644 index 000000000000..ebebcb117ba2 --- /dev/null +++ b/drivers/iio/chemical/scd4x.c @@ -0,0 +1,691 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Sensirion SCD4X carbon dioxide sensor i2c driver + * + * Copyright (C) 2021 Protonic Holland + * Author: Roan van Dijk + * + * I2C slave address: 0x62 + * + * Datasheets: + * https://www.sensirion.com/file/datasheet_scd4x + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SCD4X_CRC8_POLYNOMIAL 0x31 +#define SCD4X_TIMEOUT_ERR 1000 +#define SCD4X_READ_BUF_SIZE 9 +#define SCD4X_COMMAND_BUF_SIZE 2 +#define SCD4X_WRITE_BUF_SIZE 5 +#define SCD4X_FRC_MIN_PPM 0 +#define SCD4X_FRC_MAX_PPM 2000 +#define SCD4X_READY_MASK 0x01 + +/*Commands SCD4X*/ +enum scd4x_cmd { + CMD_START_MEAS = 0x21b1, + CMD_READ_MEAS = 0xec05, + CMD_STOP_MEAS = 0x3f86, + CMD_SET_TEMP_OFFSET = 0x241d, + CMD_GET_TEMP_OFFSET = 0x2318, + CMD_FRC = 0x362f, + CMD_SET_ASC = 0x2416, + CMD_GET_ASC = 0x2313, + CMD_GET_DATA_READY = 0xe4b8, +}; + +enum scd4x_channel_idx { + SCD4X_CO2, + SCD4X_TEMP, + SCD4X_HR, +}; + +struct scd4x_state { + struct i2c_client *client; + /* maintain access to device, to prevent concurrent reads/writes */ + struct mutex lock; + struct regulator *vdd; +}; + +DECLARE_CRC8_TABLE(scd4x_crc8_table); + +static int scd4x_i2c_xfer(struct scd4x_state *state, char *txbuf, int txsize, + char *rxbuf, int rxsize) +{ + struct i2c_client *client = state->client; + int ret; + + ret = i2c_master_send(client, txbuf, txsize); + + if (ret < 0) + return ret; + if (ret != txsize) + return -EIO; + + if (rxsize == 0) + return 0; + + ret = i2c_master_recv(client, rxbuf, rxsize); + if (ret < 0) + return ret; + if (ret != rxsize) + return -EIO; + + return 0; +} + +static int scd4x_send_command(struct scd4x_state *state, enum scd4x_cmd cmd) +{ + char buf[SCD4X_COMMAND_BUF_SIZE]; + int ret; + + /* + * Measurement needs to be stopped before sending commands. + * Except stop and start command. + */ + if ((cmd != CMD_STOP_MEAS) && (cmd != CMD_START_MEAS)) { + + ret = scd4x_send_command(state, CMD_STOP_MEAS); + if (ret) + return ret; + + /* execution time for stopping measurement */ + msleep_interruptible(500); + } + + put_unaligned_be16(cmd, buf); + ret = scd4x_i2c_xfer(state, buf, 2, buf, 0); + if (ret) + return ret; + + if ((cmd != CMD_STOP_MEAS) && (cmd != CMD_START_MEAS)) { + ret = scd4x_send_command(state, CMD_START_MEAS); + if (ret) + return ret; + } + + return 0; +} + +static int scd4x_read(struct scd4x_state *state, enum scd4x_cmd cmd, + void *response, int response_sz) +{ + struct i2c_client *client = state->client; + char buf[SCD4X_READ_BUF_SIZE]; + char *rsp = response; + int i, ret; + char crc; + + /* + * Measurement needs to be stopped before sending commands. + * Except for reading measurement and data ready command. + */ + if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS)) { + ret = scd4x_send_command(state, CMD_STOP_MEAS); + if (ret) + return ret; + + /* execution time for stopping measurement */ + msleep_interruptible(500); + } + + /* CRC byte for every 2 bytes of data */ + response_sz += response_sz / 2; + + put_unaligned_be16(cmd, buf); + ret = scd4x_i2c_xfer(state, buf, 2, buf, response_sz); + if (ret) + return ret; + + for (i = 0; i < response_sz; i += 3) { + crc = crc8(scd4x_crc8_table, buf + i, 2, CRC8_INIT_VALUE); + if (crc != buf[i + 2]) { + dev_err(&client->dev, "CRC error\n"); + return -EIO; + } + + *rsp++ = buf[i]; + *rsp++ = buf[i + 1]; + } + + /* start measurement */ + if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS)) { + ret = scd4x_send_command(state, CMD_START_MEAS); + if (ret) + return ret; + } + + return 0; +} + +static int scd4x_write(struct scd4x_state *state, enum scd4x_cmd cmd, uint16_t arg) +{ + char buf[SCD4X_WRITE_BUF_SIZE]; + int ret; + char crc; + + put_unaligned_be16(cmd, buf); + put_unaligned_be16(arg, buf + 2); + + crc = crc8(scd4x_crc8_table, buf + 2, 2, CRC8_INIT_VALUE); + buf[4] = crc; + + /* measurement needs to be stopped before sending commands */ + ret = scd4x_send_command(state, CMD_STOP_MEAS); + if (ret) + return ret; + + /* execution time */ + msleep_interruptible(500); + + ret = scd4x_i2c_xfer(state, buf, SCD4X_WRITE_BUF_SIZE, buf, 0); + if (ret) + return ret; + + /* start measurement, except for forced calibration command */ + if (cmd != CMD_FRC) { + ret = scd4x_send_command(state, CMD_START_MEAS); + if (ret) + return ret; + } + + return 0; +} + +static int scd4x_write_and_fetch(struct scd4x_state *state, enum scd4x_cmd cmd, + uint16_t arg, void *response, int response_sz) +{ + struct i2c_client *client = state->client; + char buf[SCD4X_READ_BUF_SIZE]; + char *rsp = response; + int i, ret; + char crc; + + ret = scd4x_write(state, CMD_FRC, arg); + if (ret) + goto err; + + /* execution time */ + msleep_interruptible(400); + + /* CRC byte for every 2 bytes of data */ + response_sz += response_sz / 2; + + ret = i2c_master_recv(client, buf, response_sz); + if (ret < 0) + goto err; + if (ret != response_sz) { + ret = -EIO; + goto err; + } + + for (i = 0; i < response_sz; i += 3) { + crc = crc8(scd4x_crc8_table, buf + i, 2, CRC8_INIT_VALUE); + if (crc != buf[i + 2]) { + dev_err(&client->dev, "CRC error\n"); + ret = -EIO; + goto err; + } + + *rsp++ = buf[i]; + *rsp++ = buf[i + 1]; + } + + return scd4x_send_command(state, CMD_START_MEAS); + +err: + /* + * on error try to start the measurement, + * puts sensor back into continuous measurement + */ + scd4x_send_command(state, CMD_START_MEAS); + + return ret; +} + +static int scd4x_read_meas(struct scd4x_state *state, uint16_t *meas) +{ + int i, ret; + __be16 buf[3]; + + ret = scd4x_read(state, CMD_READ_MEAS, buf, sizeof(buf)); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(buf); i++) + meas[i] = be16_to_cpu(buf[i]); + + return 0; +} + +static int scd4x_wait_meas_poll(struct scd4x_state *state) +{ + struct i2c_client *client = state->client; + int tries = 6; + int ret; + + do { + __be16 bval; + uint16_t val; + + ret = scd4x_read(state, CMD_GET_DATA_READY, &bval, sizeof(bval)); + if (ret) + return -EIO; + val = be16_to_cpu(bval); + + /* new measurement available */ + if (val & 0x7FF) + return 0; + + msleep_interruptible(1000); + } while (--tries); + + /* try to start sensor on timeout */ + ret = scd4x_send_command(state, CMD_START_MEAS); + if (ret) + dev_err(&client->dev, "failed to start measurement: %d\n", ret); + + return -ETIMEDOUT; +} + +static int scd4x_read_poll(struct scd4x_state *state, uint16_t *buf) +{ + int ret; + + ret = scd4x_wait_meas_poll(state); + if (ret) + return ret; + + return scd4x_read_meas(state, buf); +} + +static int scd4x_read_channel(struct scd4x_state *state, int chan) +{ + int ret; + uint16_t buf[3]; + + ret = scd4x_read_poll(state, buf); + if (ret) + return ret; + + return buf[chan]; +} + +static int scd4x_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct scd4x_state *state = iio_priv(indio_dev); + int ret; + __be16 tmp; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + mutex_lock(&state->lock); + ret = scd4x_read_channel(state, chan->address); + mutex_unlock(&state->lock); + + iio_device_release_direct_mode(indio_dev); + if (ret < 0) + return ret; + + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_TEMP) { + *val = 175000; + *val2 = 65536; + return IIO_VAL_FRACTIONAL; + } else if (chan->type == IIO_HUMIDITYRELATIVE) { + *val = 100000; + *val2 = 65536; + return IIO_VAL_FRACTIONAL; + } + return -EINVAL; + case IIO_CHAN_INFO_OFFSET: + *val = -16852; + *val2 = 114286; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_CALIBBIAS: + mutex_lock(&state->lock); + ret = scd4x_read(state, CMD_GET_TEMP_OFFSET, &tmp, sizeof(tmp)); + mutex_unlock(&state->lock); + if (ret) + return ret; + + *val = be16_to_cpu(tmp); + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int scd4x_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct scd4x_state *state = iio_priv(indio_dev); + int ret = 0; + + switch (mask) { + case IIO_CHAN_INFO_CALIBBIAS: + mutex_lock(&state->lock); + ret = scd4x_write(state, CMD_SET_TEMP_OFFSET, val); + mutex_unlock(&state->lock); + + return ret; + default: + return -EINVAL; + } +} + +static ssize_t calibration_auto_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct scd4x_state *state = iio_priv(indio_dev); + int ret; + __be16 bval; + u16 val; + + mutex_lock(&state->lock); + ret = scd4x_read(state, CMD_GET_ASC, &bval, sizeof(bval)); + mutex_unlock(&state->lock); + if (ret) { + dev_err(dev, "failed to read automatic calibration"); + return ret; + } + + val = (be16_to_cpu(bval) & SCD4X_READY_MASK) ? 1 : 0; + + return sprintf(buf, "%d\n", val); +} + +static ssize_t calibration_auto_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct scd4x_state *state = iio_priv(indio_dev); + bool val; + int ret; + uint16_t value; + + ret = kstrtobool(buf, &val); + if (ret) + return ret; + + value = val; + + mutex_lock(&state->lock); + ret = scd4x_write(state, CMD_SET_ASC, value); + mutex_unlock(&state->lock); + if (ret) + dev_err(dev, "failed to set automatic calibration"); + + return ret ?: len; +} + +static ssize_t calibration_forced_value_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct scd4x_state *state = iio_priv(indio_dev); + uint16_t val, arg; + int ret; + + ret = kstrtou16(buf, 0, &arg); + if (ret) + return ret; + + if (arg < SCD4X_FRC_MIN_PPM || arg > SCD4X_FRC_MAX_PPM) + return -EINVAL; + + mutex_lock(&state->lock); + ret = scd4x_write_and_fetch(state, CMD_FRC, arg, &val, sizeof(val)); + mutex_unlock(&state->lock); + + if (val == 0xff) { + dev_err(dev, "forced calibration has failed"); + return -EINVAL; + } + + return ret ?: len; +} + +static IIO_DEVICE_ATTR_RW(calibration_auto_enable, 0); +static IIO_DEVICE_ATTR_WO(calibration_forced_value, 0); + +static IIO_CONST_ATTR(calibration_forced_value_available, + __stringify([SCD4X_FRC_MIN_PPM 1 SCD4X_FRC_MAX_PPM])); + +static struct attribute *scd4x_attrs[] = { + &iio_dev_attr_calibration_auto_enable.dev_attr.attr, + &iio_dev_attr_calibration_forced_value.dev_attr.attr, + &iio_const_attr_calibration_forced_value_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group scd4x_attr_group = { + .attrs = scd4x_attrs, +}; + +static const struct iio_info scd4x_info = { + .attrs = &scd4x_attr_group, + .read_raw = scd4x_read_raw, + .write_raw = scd4x_write_raw, +}; + +static const struct iio_chan_spec scd4x_channels[] = { + { + .type = IIO_CONCENTRATION, + .channel2 = IIO_MOD_CO2, + .modified = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .address = SCD4X_CO2, + .scan_index = SCD4X_CO2, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_BE, + }, + }, + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .address = SCD4X_TEMP, + .scan_index = SCD4X_TEMP, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_BE, + }, + }, + { + .type = IIO_HUMIDITYRELATIVE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .address = SCD4X_HR, + .scan_index = SCD4X_HR, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_BE, + }, + }, +}; + +static int __maybe_unused scd4x_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct scd4x_state *state = iio_priv(indio_dev); + int ret; + + ret = scd4x_send_command(state, CMD_STOP_MEAS); + if (ret) + return ret; + + return regulator_disable(state->vdd); +} + +static int __maybe_unused scd4x_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct scd4x_state *state = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(state->vdd); + if (ret) + return ret; + + return scd4x_send_command(state, CMD_START_MEAS); +} + +static __maybe_unused SIMPLE_DEV_PM_OPS(scd4x_pm_ops, scd4x_suspend, scd4x_resume); + +static void scd4x_stop_meas(void *state) +{ + scd4x_send_command(state, CMD_STOP_MEAS); +} + +static void scd4x_disable_regulator(void *data) +{ + struct scd4x_state *state = data; + + regulator_disable(state->vdd); +} + +static irqreturn_t scd4x_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct scd4x_state *state = iio_priv(indio_dev); + struct { + uint16_t data[3]; + int64_t ts __aligned(8); + } scan; + int ret; + + memset(&scan, 0, sizeof(scan)); + mutex_lock(&state->lock); + ret = scd4x_read_poll(state, scan.data); + mutex_unlock(&state->lock); + if (ret) + goto out; + + iio_push_to_buffers_with_timestamp(indio_dev, &scan, iio_get_time_ns(indio_dev)); +out: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static int scd4x_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + static const unsigned long scd4x_scan_masks[] = { 0x07, 0x00 }; + struct device *dev = &client->dev; + struct iio_dev *indio_dev; + struct scd4x_state *state; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*state)); + if (!indio_dev) + return -ENOMEM; + + state = iio_priv(indio_dev); + mutex_init(&state->lock); + state->client = client; + crc8_populate_msb(scd4x_crc8_table, SCD4X_CRC8_POLYNOMIAL); + + indio_dev->info = &scd4x_info; + indio_dev->name = client->name; + indio_dev->channels = scd4x_channels; + indio_dev->num_channels = ARRAY_SIZE(scd4x_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->available_scan_masks = scd4x_scan_masks; + + state->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(state->vdd)) + return dev_err_probe(dev, PTR_ERR(state->vdd), "failed to get regulator\n"); + + ret = regulator_enable(state->vdd); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, scd4x_disable_regulator, state); + if (ret) + return ret; + + ret = scd4x_send_command(state, CMD_STOP_MEAS); + if (ret) { + dev_err(dev, "failed to stop measurement: %d\n", ret); + return ret; + } + + /* execution time */ + msleep_interruptible(500); + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, scd4x_trigger_handler, NULL); + if (ret) + return ret; + + ret = scd4x_send_command(state, CMD_START_MEAS); + if (ret) { + dev_err(dev, "failed to start measurement: %d\n", ret); + return ret; + } + + ret = devm_add_action_or_reset(dev, scd4x_stop_meas, state); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id scd4x_dt_ids[] = { + { .compatible = "sensirion,scd40" }, + { .compatible = "sensirion,scd41" }, + { } +}; +MODULE_DEVICE_TABLE(of, scd4x_dt_ids); + +static struct i2c_driver scd4x_i2c_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = scd4x_dt_ids, + .pm = &scd4x_pm_ops + }, + .probe = scd4x_probe, +}; +module_i2c_driver(scd4x_i2c_driver); + +MODULE_AUTHOR("Roan van Dijk "); +MODULE_DESCRIPTION("Sensirion SCD4X carbon dioxide sensor core driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-70-g09d2 From d6fa1406306d6608d50e974551c61ebbfa5e26d0 Mon Sep 17 00:00:00 2001 From: Roan van Dijk Date: Fri, 8 Oct 2021 12:17:06 +0200 Subject: iio: documentation: Document scd4x calibration use Add entries from Documentation/ABI/testing/sysfs-bus-iio-scd30 to Documentation/ABI/testing/sysfs-bus-iio. The attributes of the scd4x and scd30 are common. Remove Documentation/ABI/testing/sysfs-bus-iio-scd30. Signed-off-by: Roan van Dijk Link: https://lore.kernel.org/r/20211008101706.755942-5-roan@protonic.nl Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 41 +++++++++++++++++++++++++++ Documentation/ABI/testing/sysfs-bus-iio-scd30 | 34 ---------------------- 2 files changed, 41 insertions(+), 34 deletions(-) delete mode 100644 Documentation/ABI/testing/sysfs-bus-iio-scd30 diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index f19ff76a3b54..c551301b33f1 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -1958,3 +1958,44 @@ Description: Specify the percent for light sensor relative to the channel absolute value that a data field should change before an event is generated. Units are a percentage of the prior reading. + +What: /sys/bus/iio/devices/iio:deviceX/calibration_auto_enable +Date: June 2020 +KernelVersion: 5.8 +Contact: linux-iio@vger.kernel.org +Description: + Some sensors have the ability to apply auto calibration at + runtime. For example, it may be necessary to compensate for + contaminant build-up in a measurement chamber or optical + element deterioration that would otherwise lead to sensor drift. + + Writing 1 or 0 to this attribute will respectively activate or + deactivate this auto calibration function. + + Upon reading, the current status is returned. + +What: /sys/bus/iio/devices/iio:deviceX/calibration_forced_value +Date: June 2020 +KernelVersion: 5.8 +Contact: linux-iio@vger.kernel.org +Description: + Some sensors have the ability to apply a manual calibration using + a known measurement value, perhaps obtained from an external + reference device. + + Writing a value to this function will force such a calibration + change. For the scd30 the value should be from the range + [400 1 2000]. + + Note for the scd30 that a valid value may only be obtained once + it is has been written. Until then any read back of this value + should be ignored. As for the scd4x an error will be returned + immediately if the manual calibration has failed. + +What: /sys/bus/iio/devices/iio:deviceX/calibration_forced_value_available +KernelVersion: 5.15 +Contact: linux-iio@vger.kernel.org +Description: + Available range for the forced calibration value, expressed as: + + - a range specified as "[min step max]" diff --git a/Documentation/ABI/testing/sysfs-bus-iio-scd30 b/Documentation/ABI/testing/sysfs-bus-iio-scd30 deleted file mode 100644 index b9712f390bec..000000000000 --- a/Documentation/ABI/testing/sysfs-bus-iio-scd30 +++ /dev/null @@ -1,34 +0,0 @@ -What: /sys/bus/iio/devices/iio:deviceX/calibration_auto_enable -Date: June 2020 -KernelVersion: 5.8 -Contact: linux-iio@vger.kernel.org -Description: - Contaminants build-up in the measurement chamber or optical - elements deterioration leads to sensor drift. - - One can compensate for sensor drift by using automatic self - calibration procedure (asc). - - Writing 1 or 0 to this attribute will respectively activate or - deactivate asc. - - Upon reading current asc status is returned. - -What: /sys/bus/iio/devices/iio:deviceX/calibration_forced_value -Date: June 2020 -KernelVersion: 5.8 -Contact: linux-iio@vger.kernel.org -Description: - Contaminants build-up in the measurement chamber or optical - elements deterioration leads to sensor drift. - - One can compensate for sensor drift by using forced - recalibration (frc). This is useful in case there's known - co2 reference available nearby the sensor. - - Picking value from the range [400 1 2000] and writing it to the - sensor will set frc. - - Upon reading current frc value is returned. Note that after - power cycling default value (i.e 400) is returned even though - internally sensor had recalibrated itself. -- cgit v1.2.3-70-g09d2 From 9eeee3b0bf190b4f677af27e24ba0cd1c030e49b Mon Sep 17 00:00:00 2001 From: Mihail Chindris Date: Thu, 7 Oct 2021 08:00:30 +0000 Subject: iio: Add output buffer support Currently IIO only supports buffer mode for capture devices like ADCs. Add support for buffered mode for output devices like DACs. The output buffer implementation is analogous to the input buffer implementation. Instead of using read() to get data from the buffer write() is used to copy data into the buffer. poll() with POLLOUT will wakeup if there is space available. Drivers can remove data from a buffer using iio_pop_from_buffer(), the function can e.g. called from a trigger handler to write the data to hardware. A buffer can only be either a output buffer or an input, but not both. So, for a device that has an ADC and DAC path, this will mean 2 IIO buffers (one for each direction). The direction of the buffer is decided by the new direction field of the iio_buffer struct and should be set after allocating and before registering it. Co-developed-by: Lars-Peter Clausen Signed-off-by: Lars-Peter Clausen Co-developed-by: Alexandru Ardelean Signed-off-by: Alexandru Ardelean Signed-off-by: Mihail Chindris Link: https://lore.kernel.org/r/20211007080035.2531-2-mihail.chindris@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/iio_core.h | 4 ++ drivers/iio/industrialio-buffer.c | 127 +++++++++++++++++++++++++++++++++++++- drivers/iio/industrialio-core.c | 1 + include/linux/iio/buffer.h | 7 +++ include/linux/iio/buffer_impl.h | 11 ++++ 5 files changed, 148 insertions(+), 2 deletions(-) diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h index 8f4a9b264962..61e318431de9 100644 --- a/drivers/iio/iio_core.h +++ b/drivers/iio/iio_core.h @@ -68,12 +68,15 @@ __poll_t iio_buffer_poll_wrapper(struct file *filp, struct poll_table_struct *wait); ssize_t iio_buffer_read_wrapper(struct file *filp, char __user *buf, size_t n, loff_t *f_ps); +ssize_t iio_buffer_write_wrapper(struct file *filp, const char __user *buf, + size_t n, loff_t *f_ps); int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev); void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev); #define iio_buffer_poll_addr (&iio_buffer_poll_wrapper) #define iio_buffer_read_outer_addr (&iio_buffer_read_wrapper) +#define iio_buffer_write_outer_addr (&iio_buffer_write_wrapper) void iio_disable_all_buffers(struct iio_dev *indio_dev); void iio_buffer_wakeup_poll(struct iio_dev *indio_dev); @@ -83,6 +86,7 @@ void iio_device_detach_buffers(struct iio_dev *indio_dev); #define iio_buffer_poll_addr NULL #define iio_buffer_read_outer_addr NULL +#define iio_buffer_write_outer_addr NULL static inline int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev) { diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 4209e933ab80..b884d78657cb 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -120,6 +120,9 @@ static ssize_t iio_buffer_read(struct file *filp, char __user *buf, if (!rb || !rb->access->read) return -EINVAL; + if (rb->direction != IIO_BUFFER_DIRECTION_IN) + return -EPERM; + datum_size = rb->bytes_per_datum; /* @@ -161,6 +164,65 @@ static ssize_t iio_buffer_read(struct file *filp, char __user *buf, return ret; } +static size_t iio_buffer_space_available(struct iio_buffer *buf) +{ + if (buf->access->space_available) + return buf->access->space_available(buf); + + return SIZE_MAX; +} + +static ssize_t iio_buffer_write(struct file *filp, const char __user *buf, + size_t n, loff_t *f_ps) +{ + struct iio_dev_buffer_pair *ib = filp->private_data; + struct iio_buffer *rb = ib->buffer; + struct iio_dev *indio_dev = ib->indio_dev; + DEFINE_WAIT_FUNC(wait, woken_wake_function); + int ret; + size_t written; + + if (!indio_dev->info) + return -ENODEV; + + if (!rb || !rb->access->write) + return -EINVAL; + + if (rb->direction != IIO_BUFFER_DIRECTION_OUT) + return -EPERM; + + written = 0; + add_wait_queue(&rb->pollq, &wait); + do { + if (indio_dev->info == NULL) + return -ENODEV; + + if (!iio_buffer_space_available(rb)) { + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + + wait_woken(&wait, TASK_INTERRUPTIBLE, + MAX_SCHEDULE_TIMEOUT); + continue; + } + + ret = rb->access->write(rb, n - written, buf + written); + if (ret == 0 && (filp->f_flags & O_NONBLOCK)) + ret = -EAGAIN; + + if (ret > 0) { + written += ret; + if (written != n && !(filp->f_flags & O_NONBLOCK)) + continue; + } + } while (ret == 0); + remove_wait_queue(&rb->pollq, &wait); + + return ret < 0 ? ret : n; +} + /** * iio_buffer_poll() - poll the buffer to find out if it has data * @filp: File structure pointer for device access @@ -181,8 +243,18 @@ static __poll_t iio_buffer_poll(struct file *filp, return 0; poll_wait(filp, &rb->pollq, wait); - if (iio_buffer_ready(indio_dev, rb, rb->watermark, 0)) - return EPOLLIN | EPOLLRDNORM; + + switch (rb->direction) { + case IIO_BUFFER_DIRECTION_IN: + if (iio_buffer_ready(indio_dev, rb, rb->watermark, 0)) + return EPOLLIN | EPOLLRDNORM; + break; + case IIO_BUFFER_DIRECTION_OUT: + if (iio_buffer_space_available(rb)) + return EPOLLOUT | EPOLLWRNORM; + break; + } + return 0; } @@ -199,6 +271,19 @@ ssize_t iio_buffer_read_wrapper(struct file *filp, char __user *buf, return iio_buffer_read(filp, buf, n, f_ps); } +ssize_t iio_buffer_write_wrapper(struct file *filp, const char __user *buf, + size_t n, loff_t *f_ps) +{ + struct iio_dev_buffer_pair *ib = filp->private_data; + struct iio_buffer *rb = ib->buffer; + + /* check if buffer was opened through new API */ + if (test_bit(IIO_BUSY_BIT_POS, &rb->flags)) + return -EBUSY; + + return iio_buffer_write(filp, buf, n, f_ps); +} + __poll_t iio_buffer_poll_wrapper(struct file *filp, struct poll_table_struct *wait) { @@ -231,6 +316,15 @@ void iio_buffer_wakeup_poll(struct iio_dev *indio_dev) } } +int iio_pop_from_buffer(struct iio_buffer *buffer, void *data) +{ + if (!buffer || !buffer->access || !buffer->access->remove_from) + return -EINVAL; + + return buffer->access->remove_from(buffer, data); +} +EXPORT_SYMBOL_GPL(iio_pop_from_buffer); + void iio_buffer_init(struct iio_buffer *buffer) { INIT_LIST_HEAD(&buffer->demux_list); @@ -1156,6 +1250,10 @@ int iio_update_buffers(struct iio_dev *indio_dev, if (insert_buffer == remove_buffer) return 0; + if (insert_buffer && + (insert_buffer->direction == IIO_BUFFER_DIRECTION_OUT)) + return -EINVAL; + mutex_lock(&iio_dev_opaque->info_exist_lock); mutex_lock(&indio_dev->mlock); @@ -1277,6 +1375,22 @@ static ssize_t iio_dma_show_data_available(struct device *dev, return sysfs_emit(buf, "%zu\n", iio_buffer_data_available(buffer)); } +static ssize_t direction_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; + + switch (buffer->direction) { + case IIO_BUFFER_DIRECTION_IN: + return sprintf(buf, "in\n"); + case IIO_BUFFER_DIRECTION_OUT: + return sprintf(buf, "out\n"); + default: + return -EINVAL; + } +} + static DEVICE_ATTR(length, S_IRUGO | S_IWUSR, iio_buffer_read_length, iio_buffer_write_length); static struct device_attribute dev_attr_length_ro = __ATTR(length, @@ -1289,12 +1403,20 @@ static struct device_attribute dev_attr_watermark_ro = __ATTR(watermark, S_IRUGO, iio_buffer_show_watermark, NULL); static DEVICE_ATTR(data_available, S_IRUGO, iio_dma_show_data_available, NULL); +static DEVICE_ATTR_RO(direction); +/* + * When adding new attributes here, put the at the end, at least until + * the code that handles the length/length_ro & watermark/watermark_ro + * assignments gets cleaned up. Otherwise these can create some weird + * duplicate attributes errors under some setups. + */ static struct attribute *iio_buffer_attrs[] = { &dev_attr_length.attr, &dev_attr_enable.attr, &dev_attr_watermark.attr, &dev_attr_data_available.attr, + &dev_attr_direction.attr, }; #define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr) @@ -1397,6 +1519,7 @@ static const struct file_operations iio_buffer_chrdev_fileops = { .owner = THIS_MODULE, .llseek = noop_llseek, .read = iio_buffer_read, + .write = iio_buffer_write, .poll = iio_buffer_poll, .release = iio_buffer_chrdev_release, }; diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 2dbb37e09b8c..537a08549a69 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1822,6 +1822,7 @@ static const struct file_operations iio_buffer_fileops = { .owner = THIS_MODULE, .llseek = noop_llseek, .read = iio_buffer_read_outer_addr, + .write = iio_buffer_write_outer_addr, .poll = iio_buffer_poll_addr, .unlocked_ioctl = iio_ioctl, .compat_ioctl = compat_ptr_ioctl, diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h index 451379a3984a..418b1307d3f2 100644 --- a/include/linux/iio/buffer.h +++ b/include/linux/iio/buffer.h @@ -11,8 +11,15 @@ struct iio_buffer; +enum iio_buffer_direction { + IIO_BUFFER_DIRECTION_IN, + IIO_BUFFER_DIRECTION_OUT, +}; + int iio_push_to_buffers(struct iio_dev *indio_dev, const void *data); +int iio_pop_from_buffer(struct iio_buffer *buffer, void *data); + /** * iio_push_to_buffers_with_timestamp() - push data and timestamp to buffers * @indio_dev: iio_dev structure for device. diff --git a/include/linux/iio/buffer_impl.h b/include/linux/iio/buffer_impl.h index 245b32918ae1..e2ca8ea23e19 100644 --- a/include/linux/iio/buffer_impl.h +++ b/include/linux/iio/buffer_impl.h @@ -7,6 +7,7 @@ #ifdef CONFIG_IIO_BUFFER #include +#include struct iio_dev; struct iio_buffer; @@ -23,6 +24,10 @@ struct iio_buffer; * @read: try to get a specified number of bytes (must exist) * @data_available: indicates how much data is available for reading from * the buffer. + * @remove_from: remove scan from buffer. Drivers should calls this to + * remove a scan from a buffer. + * @write: try to write a number of bytes + * @space_available: returns the amount of bytes available in a buffer * @request_update: if a parameter change has been marked, update underlying * storage. * @set_bytes_per_datum:set number of bytes per datum @@ -49,6 +54,9 @@ struct iio_buffer_access_funcs { int (*store_to)(struct iio_buffer *buffer, const void *data); int (*read)(struct iio_buffer *buffer, size_t n, char __user *buf); size_t (*data_available)(struct iio_buffer *buffer); + int (*remove_from)(struct iio_buffer *buffer, void *data); + int (*write)(struct iio_buffer *buffer, size_t n, const char __user *buf); + size_t (*space_available)(struct iio_buffer *buffer); int (*request_update)(struct iio_buffer *buffer); @@ -80,6 +88,9 @@ struct iio_buffer { /** @bytes_per_datum: Size of individual datum including timestamp. */ size_t bytes_per_datum; + /* @direction: Direction of the data stream (in/out). */ + enum iio_buffer_direction direction; + /** * @access: Buffer access functions associated with the * implementation. -- cgit v1.2.3-70-g09d2 From 1546d6718dc92b27935931ee4e4c2e9893ef3066 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 7 Oct 2021 08:00:31 +0000 Subject: iio: kfifo-buffer: Add output buffer support Add output buffer support to the kfifo buffer implementation. The implementation is straight forward and mostly just wraps the kfifo API to provide the required operations. Signed-off-by: Lars-Peter Clausen Signed-off-by: Alexandru Ardelean Signed-off-by: Mihail Chindris Link: https://lore.kernel.org/r/20211007080035.2531-3-mihail.chindris@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/buffer/kfifo_buf.c | 50 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/drivers/iio/buffer/kfifo_buf.c b/drivers/iio/buffer/kfifo_buf.c index 516eb3465de1..416d35a61ae2 100644 --- a/drivers/iio/buffer/kfifo_buf.c +++ b/drivers/iio/buffer/kfifo_buf.c @@ -138,10 +138,60 @@ static void iio_kfifo_buffer_release(struct iio_buffer *buffer) kfree(kf); } +static size_t iio_kfifo_buf_space_available(struct iio_buffer *r) +{ + struct iio_kfifo *kf = iio_to_kfifo(r); + size_t avail; + + mutex_lock(&kf->user_lock); + avail = kfifo_avail(&kf->kf); + mutex_unlock(&kf->user_lock); + + return avail; +} + +static int iio_kfifo_remove_from(struct iio_buffer *r, void *data) +{ + int ret; + struct iio_kfifo *kf = iio_to_kfifo(r); + + if (kfifo_size(&kf->kf) < 1) + return -EBUSY; + + ret = kfifo_out(&kf->kf, data, 1); + if (ret != 1) + return -EBUSY; + + wake_up_interruptible_poll(&r->pollq, EPOLLOUT | EPOLLWRNORM); + + return 0; +} + +static int iio_kfifo_write(struct iio_buffer *r, size_t n, + const char __user *buf) +{ + struct iio_kfifo *kf = iio_to_kfifo(r); + int ret, copied; + + mutex_lock(&kf->user_lock); + if (!kfifo_initialized(&kf->kf) || n < kfifo_esize(&kf->kf)) + ret = -EINVAL; + else + ret = kfifo_from_user(&kf->kf, buf, n, &copied); + mutex_unlock(&kf->user_lock); + if (ret) + return ret; + + return copied; +} + static const struct iio_buffer_access_funcs kfifo_access_funcs = { .store_to = &iio_store_to_kfifo, .read = &iio_read_kfifo, .data_available = iio_kfifo_buf_data_available, + .remove_from = &iio_kfifo_remove_from, + .write = &iio_kfifo_write, + .space_available = &iio_kfifo_buf_space_available, .request_update = &iio_request_update_kfifo, .set_bytes_per_datum = &iio_set_bytes_per_datum_kfifo, .set_length = &iio_set_length_kfifo, -- cgit v1.2.3-70-g09d2 From c02cd5c19c17698f12b731e898127095f9bc2921 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Thu, 7 Oct 2021 08:00:32 +0000 Subject: iio: triggered-buffer: extend support to configure output buffers Now that output (kfifo) buffers are supported, we need to extend the {devm_}iio_triggered_buffer_setup_ext() parameter list to take a direction parameter. This allows us to attach an output triggered buffer to a DAC device. Unfortunately it's a bit difficult to add another macro to avoid changing 5 drivers where {devm_}iio_triggered_buffer_setup_ext() is used. Well, it's doable, but may not be worth the trouble vs just updating all these 5 drivers. Signed-off-by: Alexandru Ardelean Signed-off-by: Mihail Chindris Link: https://lore.kernel.org/r/20211007080035.2531-4-mihail.chindris@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl372.c | 1 + drivers/iio/accel/bmc150-accel-core.c | 1 + drivers/iio/adc/at91-sama5d2_adc.c | 4 ++-- drivers/iio/buffer/industrialio-triggered-buffer.c | 8 ++++++-- drivers/iio/common/hid-sensors/hid-sensor-trigger.c | 5 +++-- include/linux/iio/triggered_buffer.h | 11 +++++++++-- 6 files changed, 22 insertions(+), 8 deletions(-) diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index fc9592407717..758952584f8c 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -1214,6 +1214,7 @@ int adxl372_probe(struct device *dev, struct regmap *regmap, ret = devm_iio_triggered_buffer_setup_ext(dev, indio_dev, NULL, adxl372_trigger_handler, + IIO_BUFFER_DIRECTION_IN, &adxl372_buffer_ops, adxl372_fifo_attributes); if (ret < 0) diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index e8693a42ad46..63216321cdb5 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -1734,6 +1734,7 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq, ret = iio_triggered_buffer_setup_ext(indio_dev, &iio_pollfunc_store_time, bmc150_accel_trigger_handler, + IIO_BUFFER_DIRECTION_IN, &bmc150_accel_buffer_ops, fifo_attrs); if (ret < 0) { diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index dabe8cdcfd08..4c922ef634f8 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -1894,8 +1894,8 @@ static int at91_adc_buffer_and_trigger_init(struct device *dev, fifo_attrs = NULL; ret = devm_iio_triggered_buffer_setup_ext(&indio->dev, indio, - &iio_pollfunc_store_time, - &at91_adc_trigger_handler, &at91_buffer_setup_ops, fifo_attrs); + &iio_pollfunc_store_time, &at91_adc_trigger_handler, + IIO_BUFFER_DIRECTION_IN, &at91_buffer_setup_ops, fifo_attrs); if (ret < 0) { dev_err(dev, "couldn't initialize the buffer.\n"); return ret; diff --git a/drivers/iio/buffer/industrialio-triggered-buffer.c b/drivers/iio/buffer/industrialio-triggered-buffer.c index f77c4538141e..8d4fc97d1005 100644 --- a/drivers/iio/buffer/industrialio-triggered-buffer.c +++ b/drivers/iio/buffer/industrialio-triggered-buffer.c @@ -19,6 +19,7 @@ * @indio_dev: IIO device structure * @h: Function which will be used as pollfunc top half * @thread: Function which will be used as pollfunc bottom half + * @direction: Direction of the data stream (in/out). * @setup_ops: Buffer setup functions to use for this device. * If NULL the default setup functions for triggered * buffers will be used. @@ -38,6 +39,7 @@ int iio_triggered_buffer_setup_ext(struct iio_dev *indio_dev, irqreturn_t (*h)(int irq, void *p), irqreturn_t (*thread)(int irq, void *p), + enum iio_buffer_direction direction, const struct iio_buffer_setup_ops *setup_ops, const struct attribute **buffer_attrs) { @@ -68,6 +70,7 @@ int iio_triggered_buffer_setup_ext(struct iio_dev *indio_dev, /* Flag that polled ring buffering is possible */ indio_dev->modes |= INDIO_BUFFER_TRIGGERED; + buffer->direction = direction; buffer->attrs = buffer_attrs; ret = iio_device_attach_buffer(indio_dev, buffer); @@ -105,13 +108,14 @@ int devm_iio_triggered_buffer_setup_ext(struct device *dev, struct iio_dev *indio_dev, irqreturn_t (*h)(int irq, void *p), irqreturn_t (*thread)(int irq, void *p), + enum iio_buffer_direction direction, const struct iio_buffer_setup_ops *ops, const struct attribute **buffer_attrs) { int ret; - ret = iio_triggered_buffer_setup_ext(indio_dev, h, thread, ops, - buffer_attrs); + ret = iio_triggered_buffer_setup_ext(indio_dev, h, thread, direction, + ops, buffer_attrs); if (ret) return ret; diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index a4ec11a3b68a..1151434038d4 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -241,8 +241,9 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, fifo_attrs = NULL; ret = iio_triggered_buffer_setup_ext(indio_dev, - &iio_pollfunc_store_time, - NULL, NULL, fifo_attrs); + &iio_pollfunc_store_time, NULL, + IIO_BUFFER_DIRECTION_IN, + NULL, fifo_attrs); if (ret) { dev_err(&indio_dev->dev, "Triggered Buffer Setup Failed\n"); return ret; diff --git a/include/linux/iio/triggered_buffer.h b/include/linux/iio/triggered_buffer.h index 7f154d1f8739..7490b05fc5b2 100644 --- a/include/linux/iio/triggered_buffer.h +++ b/include/linux/iio/triggered_buffer.h @@ -2,6 +2,7 @@ #ifndef _LINUX_IIO_TRIGGERED_BUFFER_H_ #define _LINUX_IIO_TRIGGERED_BUFFER_H_ +#include #include struct attribute; @@ -11,21 +12,27 @@ struct iio_buffer_setup_ops; int iio_triggered_buffer_setup_ext(struct iio_dev *indio_dev, irqreturn_t (*h)(int irq, void *p), irqreturn_t (*thread)(int irq, void *p), + enum iio_buffer_direction direction, const struct iio_buffer_setup_ops *setup_ops, const struct attribute **buffer_attrs); void iio_triggered_buffer_cleanup(struct iio_dev *indio_dev); #define iio_triggered_buffer_setup(indio_dev, h, thread, setup_ops) \ - iio_triggered_buffer_setup_ext((indio_dev), (h), (thread), (setup_ops), NULL) + iio_triggered_buffer_setup_ext((indio_dev), (h), (thread), \ + IIO_BUFFER_DIRECTION_IN, (setup_ops), \ + NULL) int devm_iio_triggered_buffer_setup_ext(struct device *dev, struct iio_dev *indio_dev, irqreturn_t (*h)(int irq, void *p), irqreturn_t (*thread)(int irq, void *p), + enum iio_buffer_direction direction, const struct iio_buffer_setup_ops *ops, const struct attribute **buffer_attrs); #define devm_iio_triggered_buffer_setup(dev, indio_dev, h, thread, setup_ops) \ - devm_iio_triggered_buffer_setup_ext((dev), (indio_dev), (h), (thread), (setup_ops), NULL) + devm_iio_triggered_buffer_setup_ext((dev), (indio_dev), (h), (thread), \ + IIO_BUFFER_DIRECTION_IN, \ + (setup_ops), NULL) #endif -- cgit v1.2.3-70-g09d2 From 885b9790c25a5fb8b253971c107744b17d8c29e7 Mon Sep 17 00:00:00 2001 From: Mihail Chindris Date: Thu, 7 Oct 2021 08:00:37 +0000 Subject: drivers:iio:dac:ad5766.c: Add trigger buffer This chip is able to generate waveform and using an with the output trigger buffer will be easy to generate one. Signed-off-by: Mihail Chindris Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20211007080035.2531-7-mihail.chindris@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5766.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/drivers/iio/dac/ad5766.c b/drivers/iio/dac/ad5766.c index 3104ec32dfac..2b1edcb25444 100644 --- a/drivers/iio/dac/ad5766.c +++ b/drivers/iio/dac/ad5766.c @@ -5,10 +5,13 @@ * Copyright 2019-2020 Analog Devices Inc. */ #include +#include #include #include #include #include +#include +#include #include #include #include @@ -455,6 +458,7 @@ static const struct iio_chan_spec_ext_info ad5766_ext_info[] = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | \ BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = (_chan), \ .scan_type = { \ .sign = 'u', \ .realbits = (_bits), \ @@ -576,6 +580,35 @@ static int ad5766_default_setup(struct ad5766_state *st) return __ad5766_spi_write(st, AD5766_CMD_SPAN_REG, st->crt_range); } +static irqreturn_t ad5766_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct iio_buffer *buffer = indio_dev->buffer; + struct ad5766_state *st = iio_priv(indio_dev); + int ret, ch, i; + u16 data[ARRAY_SIZE(ad5766_channels)]; + + ret = iio_pop_from_buffer(buffer, data); + if (ret) + goto done; + + i = 0; + mutex_lock(&st->lock); + for_each_set_bit(ch, indio_dev->active_scan_mask, + st->chip_info->num_channels - 1) + __ad5766_spi_write(st, AD5766_CMD_WR_IN_REG(ch), data[i++]); + + __ad5766_spi_write(st, AD5766_CMD_SW_LDAC, + *indio_dev->active_scan_mask); + mutex_unlock(&st->lock); + +done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + static int ad5766_probe(struct spi_device *spi) { enum ad5766_type type; @@ -609,6 +642,15 @@ static int ad5766_probe(struct spi_device *spi) if (ret) return ret; + /* Configure trigger buffer */ + ret = devm_iio_triggered_buffer_setup_ext(&spi->dev, indio_dev, NULL, + ad5766_trigger_handler, + IIO_BUFFER_DIRECTION_OUT, + NULL, + NULL); + if (ret) + return ret; + return devm_iio_device_register(&spi->dev, indio_dev); } -- cgit v1.2.3-70-g09d2 From fa0b148eb396da85e34469d22730da5dfba30f0d Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 13 Oct 2021 22:32:09 +0200 Subject: iio: accel: bma400: Make bma400_remove() return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When an i2c or spi driver's remove function returns a non-zero error code nothing happens apart from emitting a generic error message. Make this error message more device specific and return zero instead in the remove callbacks. As the return value of bma400_remove() is unused then, change the function to not yield a return value. Signed-off-by: Uwe Kleine-König Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20211013203223.2694577-2-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bma400.h | 2 +- drivers/iio/accel/bma400_core.c | 7 ++++--- drivers/iio/accel/bma400_i2c.c | 4 +++- drivers/iio/accel/bma400_spi.c | 4 +++- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/iio/accel/bma400.h b/drivers/iio/accel/bma400.h index 5ad10db9819f..c4c8d74155c2 100644 --- a/drivers/iio/accel/bma400.h +++ b/drivers/iio/accel/bma400.h @@ -94,6 +94,6 @@ extern const struct regmap_config bma400_regmap_config; int bma400_probe(struct device *dev, struct regmap *regmap, const char *name); -int bma400_remove(struct device *dev); +void bma400_remove(struct device *dev); #endif diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c index 21520e022a21..fd2647b728d3 100644 --- a/drivers/iio/accel/bma400_core.c +++ b/drivers/iio/accel/bma400_core.c @@ -828,7 +828,7 @@ int bma400_probe(struct device *dev, struct regmap *regmap, const char *name) } EXPORT_SYMBOL(bma400_probe); -int bma400_remove(struct device *dev) +void bma400_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct bma400_data *data = iio_priv(indio_dev); @@ -838,12 +838,13 @@ int bma400_remove(struct device *dev) ret = bma400_set_power_mode(data, POWER_MODE_SLEEP); mutex_unlock(&data->mutex); + if (ret) + dev_warn(dev, "Failed to put device into sleep mode (%pe)\n", ERR_PTR(ret)); + regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators); iio_device_unregister(indio_dev); - - return ret; } EXPORT_SYMBOL(bma400_remove); diff --git a/drivers/iio/accel/bma400_i2c.c b/drivers/iio/accel/bma400_i2c.c index 9dcb7cc9996e..f50df5310beb 100644 --- a/drivers/iio/accel/bma400_i2c.c +++ b/drivers/iio/accel/bma400_i2c.c @@ -29,7 +29,9 @@ static int bma400_i2c_probe(struct i2c_client *client, static int bma400_i2c_remove(struct i2c_client *client) { - return bma400_remove(&client->dev); + bma400_remove(&client->dev); + + return 0; } static const struct i2c_device_id bma400_i2c_ids[] = { diff --git a/drivers/iio/accel/bma400_spi.c b/drivers/iio/accel/bma400_spi.c index 7c2825904e08..9f622e37477b 100644 --- a/drivers/iio/accel/bma400_spi.c +++ b/drivers/iio/accel/bma400_spi.c @@ -89,7 +89,9 @@ static int bma400_spi_probe(struct spi_device *spi) static int bma400_spi_remove(struct spi_device *spi) { - return bma400_remove(&spi->dev); + bma400_remove(&spi->dev); + + return 0; } static const struct spi_device_id bma400_spi_ids[] = { -- cgit v1.2.3-70-g09d2 From 9713964f08d70d4373ea0a27e23119f6b938ca4e Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 13 Oct 2021 22:32:10 +0200 Subject: iio: accel: bmc150: Make bmc150_accel_core_remove() return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up to now bmc150_accel_core_remove() returns zero unconditionally. Make it return void instead which makes it easier to see in the callers that there is no error to handle. Also the return value of i2c and spi remove callbacks is ignored anyway. Signed-off-by: Uwe Kleine-König Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20211013203223.2694577-3-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bmc150-accel-core.c | 4 +--- drivers/iio/accel/bmc150-accel-i2c.c | 4 +++- drivers/iio/accel/bmc150-accel-spi.c | 4 +++- drivers/iio/accel/bmc150-accel.h | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index 63216321cdb5..b0678c351e82 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -1800,7 +1800,7 @@ err_disable_regulators: } EXPORT_SYMBOL_GPL(bmc150_accel_core_probe); -int bmc150_accel_core_remove(struct device *dev) +void bmc150_accel_core_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct bmc150_accel_data *data = iio_priv(indio_dev); @@ -1820,8 +1820,6 @@ int bmc150_accel_core_remove(struct device *dev) regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators); - - return 0; } EXPORT_SYMBOL_GPL(bmc150_accel_core_remove); diff --git a/drivers/iio/accel/bmc150-accel-i2c.c b/drivers/iio/accel/bmc150-accel-i2c.c index 88bd8a25f142..9e52df9a8f07 100644 --- a/drivers/iio/accel/bmc150-accel-i2c.c +++ b/drivers/iio/accel/bmc150-accel-i2c.c @@ -213,7 +213,9 @@ static int bmc150_accel_remove(struct i2c_client *client) { bmc150_acpi_dual_accel_remove(client); - return bmc150_accel_core_remove(&client->dev); + bmc150_accel_core_remove(&client->dev); + + return 0; } static const struct acpi_device_id bmc150_accel_acpi_match[] = { diff --git a/drivers/iio/accel/bmc150-accel-spi.c b/drivers/iio/accel/bmc150-accel-spi.c index 191e312dc91a..11559567cb39 100644 --- a/drivers/iio/accel/bmc150-accel-spi.c +++ b/drivers/iio/accel/bmc150-accel-spi.c @@ -37,7 +37,9 @@ static int bmc150_accel_probe(struct spi_device *spi) static int bmc150_accel_remove(struct spi_device *spi) { - return bmc150_accel_core_remove(&spi->dev); + bmc150_accel_core_remove(&spi->dev); + + return 0; } static const struct acpi_device_id bmc150_accel_acpi_match[] = { diff --git a/drivers/iio/accel/bmc150-accel.h b/drivers/iio/accel/bmc150-accel.h index 1bb5023e8ed9..7775c5edaeef 100644 --- a/drivers/iio/accel/bmc150-accel.h +++ b/drivers/iio/accel/bmc150-accel.h @@ -88,7 +88,7 @@ struct bmc150_accel_data { int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq, enum bmc150_type type, const char *name, bool block_supported); -int bmc150_accel_core_remove(struct device *dev); +void bmc150_accel_core_remove(struct device *dev); extern const struct dev_pm_ops bmc150_accel_pm_ops; extern const struct regmap_config bmc150_regmap_conf; -- cgit v1.2.3-70-g09d2 From bcf9d61a2dcb05d1de46ead784c333043ef2e7f2 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 13 Oct 2021 22:32:11 +0200 Subject: iio: accel: bmi088: Make bmi088_accel_core_remove() return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up to now bmi088_accel_core_remove() returns zero unconditionally. Make it return void instead which makes it easier to see in the callers that there is no error to handle. Also the return value of spi remove callbacks is ignored anyway. Signed-off-by: Uwe Kleine-König Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20211013203223.2694577-4-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bmi088-accel-core.c | 4 +--- drivers/iio/accel/bmi088-accel-spi.c | 4 +++- drivers/iio/accel/bmi088-accel.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/iio/accel/bmi088-accel-core.c b/drivers/iio/accel/bmi088-accel-core.c index a06dae5c971d..d74465214feb 100644 --- a/drivers/iio/accel/bmi088-accel-core.c +++ b/drivers/iio/accel/bmi088-accel-core.c @@ -536,7 +536,7 @@ int bmi088_accel_core_probe(struct device *dev, struct regmap *regmap, EXPORT_SYMBOL_GPL(bmi088_accel_core_probe); -int bmi088_accel_core_remove(struct device *dev) +void bmi088_accel_core_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct bmi088_accel_data *data = iio_priv(indio_dev); @@ -546,8 +546,6 @@ int bmi088_accel_core_remove(struct device *dev) pm_runtime_disable(dev); pm_runtime_set_suspended(dev); bmi088_accel_power_down(data); - - return 0; } EXPORT_SYMBOL_GPL(bmi088_accel_core_remove); diff --git a/drivers/iio/accel/bmi088-accel-spi.c b/drivers/iio/accel/bmi088-accel-spi.c index dd1e3f6cf211..758ad2f12896 100644 --- a/drivers/iio/accel/bmi088-accel-spi.c +++ b/drivers/iio/accel/bmi088-accel-spi.c @@ -58,7 +58,9 @@ static int bmi088_accel_probe(struct spi_device *spi) static int bmi088_accel_remove(struct spi_device *spi) { - return bmi088_accel_core_remove(&spi->dev); + bmi088_accel_core_remove(&spi->dev); + + return 0; } static const struct spi_device_id bmi088_accel_id[] = { diff --git a/drivers/iio/accel/bmi088-accel.h b/drivers/iio/accel/bmi088-accel.h index 5c25f16b672c..5d40c7cf1cbc 100644 --- a/drivers/iio/accel/bmi088-accel.h +++ b/drivers/iio/accel/bmi088-accel.h @@ -13,6 +13,6 @@ extern const struct dev_pm_ops bmi088_accel_pm_ops; int bmi088_accel_core_probe(struct device *dev, struct regmap *regmap, int irq, const char *name, bool block_supported); -int bmi088_accel_core_remove(struct device *dev); +void bmi088_accel_core_remove(struct device *dev); #endif /* BMI088_ACCEL_H */ -- cgit v1.2.3-70-g09d2 From df2171c668bdecc9122cc8f66bd0639baeae5c6b Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 13 Oct 2021 22:32:12 +0200 Subject: iio: accel: kxsd9: Make kxsd9_common_remove() return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up to now kxsd9_common_remove() returns zero unconditionally. Make it return void instead which makes it easier to see in the callers that there is no error to handle. Also the return value of i2c and spi remove callbacks is ignored anyway. Signed-off-by: Uwe Kleine-König Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20211013203223.2694577-5-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/accel/kxsd9-i2c.c | 4 +++- drivers/iio/accel/kxsd9-spi.c | 4 +++- drivers/iio/accel/kxsd9.c | 4 +--- drivers/iio/accel/kxsd9.h | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/iio/accel/kxsd9-i2c.c b/drivers/iio/accel/kxsd9-i2c.c index b580d605f848..274b41a6e603 100644 --- a/drivers/iio/accel/kxsd9-i2c.c +++ b/drivers/iio/accel/kxsd9-i2c.c @@ -34,7 +34,9 @@ static int kxsd9_i2c_probe(struct i2c_client *i2c, static int kxsd9_i2c_remove(struct i2c_client *client) { - return kxsd9_common_remove(&client->dev); + kxsd9_common_remove(&client->dev); + + return 0; } static const struct of_device_id kxsd9_of_match[] = { diff --git a/drivers/iio/accel/kxsd9-spi.c b/drivers/iio/accel/kxsd9-spi.c index 7971ec1eeb7e..441e6b764281 100644 --- a/drivers/iio/accel/kxsd9-spi.c +++ b/drivers/iio/accel/kxsd9-spi.c @@ -34,7 +34,9 @@ static int kxsd9_spi_probe(struct spi_device *spi) static int kxsd9_spi_remove(struct spi_device *spi) { - return kxsd9_common_remove(&spi->dev); + kxsd9_common_remove(&spi->dev); + + return 0; } static const struct spi_device_id kxsd9_spi_id[] = { diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c index bf7ed9e7d00f..2faf85ca996e 100644 --- a/drivers/iio/accel/kxsd9.c +++ b/drivers/iio/accel/kxsd9.c @@ -478,7 +478,7 @@ err_power_down: } EXPORT_SYMBOL(kxsd9_common_probe); -int kxsd9_common_remove(struct device *dev) +void kxsd9_common_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct kxsd9_state *st = iio_priv(indio_dev); @@ -489,8 +489,6 @@ int kxsd9_common_remove(struct device *dev) pm_runtime_put_noidle(dev); pm_runtime_disable(dev); kxsd9_power_down(st); - - return 0; } EXPORT_SYMBOL(kxsd9_common_remove); diff --git a/drivers/iio/accel/kxsd9.h b/drivers/iio/accel/kxsd9.h index 5e3ca212f5be..c04dbfa4e0d0 100644 --- a/drivers/iio/accel/kxsd9.h +++ b/drivers/iio/accel/kxsd9.h @@ -8,6 +8,6 @@ int kxsd9_common_probe(struct device *dev, struct regmap *map, const char *name); -int kxsd9_common_remove(struct device *dev); +void kxsd9_common_remove(struct device *dev); extern const struct dev_pm_ops kxsd9_dev_pm_ops; -- cgit v1.2.3-70-g09d2 From 523742f2112237e57db13176f7456aef65c1731a Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 13 Oct 2021 22:32:13 +0200 Subject: iio: accel: mma7455: Make mma7455_core_remove() return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up to now mma7455_core_remove() returns zero unconditionally. Make it return void instead which makes it easier to see in the callers that there is no error to handle. Also the return value of i2c and spi remove callbacks is ignored anyway. Signed-off-by: Uwe Kleine-König Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20211013203223.2694577-6-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma7455.h | 2 +- drivers/iio/accel/mma7455_core.c | 4 +--- drivers/iio/accel/mma7455_i2c.c | 4 +++- drivers/iio/accel/mma7455_spi.c | 4 +++- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/iio/accel/mma7455.h b/drivers/iio/accel/mma7455.h index 4e3fa988f690..1fcc4b64b3af 100644 --- a/drivers/iio/accel/mma7455.h +++ b/drivers/iio/accel/mma7455.h @@ -11,6 +11,6 @@ extern const struct regmap_config mma7455_core_regmap; int mma7455_core_probe(struct device *dev, struct regmap *regmap, const char *name); -int mma7455_core_remove(struct device *dev); +void mma7455_core_remove(struct device *dev); #endif diff --git a/drivers/iio/accel/mma7455_core.c b/drivers/iio/accel/mma7455_core.c index 922bd38ff6ea..777c6c384b09 100644 --- a/drivers/iio/accel/mma7455_core.c +++ b/drivers/iio/accel/mma7455_core.c @@ -294,7 +294,7 @@ int mma7455_core_probe(struct device *dev, struct regmap *regmap, } EXPORT_SYMBOL_GPL(mma7455_core_probe); -int mma7455_core_remove(struct device *dev) +void mma7455_core_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct mma7455_data *mma7455 = iio_priv(indio_dev); @@ -304,8 +304,6 @@ int mma7455_core_remove(struct device *dev) regmap_write(mma7455->regmap, MMA7455_REG_MCTL, MMA7455_MCTL_MODE_STANDBY); - - return 0; } EXPORT_SYMBOL_GPL(mma7455_core_remove); diff --git a/drivers/iio/accel/mma7455_i2c.c b/drivers/iio/accel/mma7455_i2c.c index cddeaa9e230a..8a5256516f9f 100644 --- a/drivers/iio/accel/mma7455_i2c.c +++ b/drivers/iio/accel/mma7455_i2c.c @@ -28,7 +28,9 @@ static int mma7455_i2c_probe(struct i2c_client *i2c, static int mma7455_i2c_remove(struct i2c_client *i2c) { - return mma7455_core_remove(&i2c->dev); + mma7455_core_remove(&i2c->dev); + + return 0; } static const struct i2c_device_id mma7455_i2c_ids[] = { diff --git a/drivers/iio/accel/mma7455_spi.c b/drivers/iio/accel/mma7455_spi.c index eb82cdfa8abc..ecf690692dcc 100644 --- a/drivers/iio/accel/mma7455_spi.c +++ b/drivers/iio/accel/mma7455_spi.c @@ -24,7 +24,9 @@ static int mma7455_spi_probe(struct spi_device *spi) static int mma7455_spi_remove(struct spi_device *spi) { - return mma7455_core_remove(&spi->dev); + mma7455_core_remove(&spi->dev); + + return 0; } static const struct spi_device_id mma7455_spi_ids[] = { -- cgit v1.2.3-70-g09d2 From d6220554e428c6a7e2848470b18b41da4be0c208 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 13 Oct 2021 22:32:15 +0200 Subject: iio: dac: ad5380: Make ad5380_remove() return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up to now ad5380_remove() returns zero unconditionally. Make it return void instead which makes it easier to see in the callers that there is no error to handle. Also the return value of i2c and spi remove callbacks is ignored anyway. Signed-off-by: Uwe Kleine-König Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20211013203223.2694577-8-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5380.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c index 53db5b4e4c53..8ca26bb4b62f 100644 --- a/drivers/iio/dac/ad5380.c +++ b/drivers/iio/dac/ad5380.c @@ -444,7 +444,7 @@ error_free_reg: return ret; } -static int ad5380_remove(struct device *dev) +static void ad5380_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct ad5380_state *st = iio_priv(indio_dev); @@ -453,11 +453,8 @@ static int ad5380_remove(struct device *dev) kfree(indio_dev->channels); - if (!IS_ERR(st->vref_reg)) { + if (!IS_ERR(st->vref_reg)) regulator_disable(st->vref_reg); - } - - return 0; } static bool ad5380_reg_false(struct device *dev, unsigned int reg) @@ -493,7 +490,9 @@ static int ad5380_spi_probe(struct spi_device *spi) static int ad5380_spi_remove(struct spi_device *spi) { - return ad5380_remove(&spi->dev); + ad5380_remove(&spi->dev); + + return 0; } static const struct spi_device_id ad5380_spi_ids[] = { @@ -566,7 +565,9 @@ static int ad5380_i2c_probe(struct i2c_client *i2c, static int ad5380_i2c_remove(struct i2c_client *i2c) { - return ad5380_remove(&i2c->dev); + ad5380_remove(&i2c->dev); + + return 0; } static const struct i2c_device_id ad5380_i2c_ids[] = { -- cgit v1.2.3-70-g09d2 From 1f10848f18555980e7379532d1859245d2d847c9 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 13 Oct 2021 22:32:16 +0200 Subject: iio: dac: ad5446: Make ad5446_remove() return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up to now ad5446_remove() returns zero unconditionally. Make it return void instead which makes it easier to see in the callers that there is no error to handle. Also the return value of i2c and spi remove callbacks is ignored anyway. Signed-off-by: Uwe Kleine-König Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20211013203223.2694577-9-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5446.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c index 488ec69967d6..0ef761521e87 100644 --- a/drivers/iio/dac/ad5446.c +++ b/drivers/iio/dac/ad5446.c @@ -283,7 +283,7 @@ error_disable_reg: return ret; } -static int ad5446_remove(struct device *dev) +static void ad5446_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct ad5446_state *st = iio_priv(indio_dev); @@ -291,8 +291,6 @@ static int ad5446_remove(struct device *dev) iio_device_unregister(indio_dev); if (!IS_ERR(st->reg)) regulator_disable(st->reg); - - return 0; } #if IS_ENABLED(CONFIG_SPI_MASTER) @@ -495,7 +493,9 @@ static int ad5446_spi_probe(struct spi_device *spi) static int ad5446_spi_remove(struct spi_device *spi) { - return ad5446_remove(&spi->dev); + ad5446_remove(&spi->dev); + + return 0; } static struct spi_driver ad5446_spi_driver = { @@ -572,7 +572,9 @@ static int ad5446_i2c_probe(struct i2c_client *i2c, static int ad5446_i2c_remove(struct i2c_client *i2c) { - return ad5446_remove(&i2c->dev); + ad5446_remove(&i2c->dev); + + return 0; } static const struct i2c_device_id ad5446_i2c_ids[] = { -- cgit v1.2.3-70-g09d2 From 72ba4505622ddc765f227fb24270fc202193aab5 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 13 Oct 2021 22:32:17 +0200 Subject: iio: dac: ad5592r: Make ad5592r_remove() return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up to now ad5592r_remove() returns zero unconditionally. Make it return void instead which makes it easier to see in the callers that there is no error to handle. Also the return value of i2c and spi remove callbacks is ignored anyway. Signed-off-by: Uwe Kleine-König Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20211013203223.2694577-10-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5592r-base.c | 4 +--- drivers/iio/dac/ad5592r-base.h | 2 +- drivers/iio/dac/ad5592r.c | 4 +++- drivers/iio/dac/ad5593r.c | 4 +++- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/iio/dac/ad5592r-base.c b/drivers/iio/dac/ad5592r-base.c index 0405e92b9e8c..2fcc59728fd6 100644 --- a/drivers/iio/dac/ad5592r-base.c +++ b/drivers/iio/dac/ad5592r-base.c @@ -663,7 +663,7 @@ error_disable_reg: } EXPORT_SYMBOL_GPL(ad5592r_probe); -int ad5592r_remove(struct device *dev) +void ad5592r_remove(struct device *dev) { struct iio_dev *iio_dev = dev_get_drvdata(dev); struct ad5592r_state *st = iio_priv(iio_dev); @@ -674,8 +674,6 @@ int ad5592r_remove(struct device *dev) if (st->reg) regulator_disable(st->reg); - - return 0; } EXPORT_SYMBOL_GPL(ad5592r_remove); diff --git a/drivers/iio/dac/ad5592r-base.h b/drivers/iio/dac/ad5592r-base.h index 23dac2f1ff8a..2a22ef691996 100644 --- a/drivers/iio/dac/ad5592r-base.h +++ b/drivers/iio/dac/ad5592r-base.h @@ -71,6 +71,6 @@ struct ad5592r_state { int ad5592r_probe(struct device *dev, const char *name, const struct ad5592r_rw_ops *ops); -int ad5592r_remove(struct device *dev); +void ad5592r_remove(struct device *dev); #endif /* __DRIVERS_IIO_DAC_AD5592R_BASE_H__ */ diff --git a/drivers/iio/dac/ad5592r.c b/drivers/iio/dac/ad5592r.c index 41f651500668..6bfd7951e18c 100644 --- a/drivers/iio/dac/ad5592r.c +++ b/drivers/iio/dac/ad5592r.c @@ -132,7 +132,9 @@ static int ad5592r_spi_probe(struct spi_device *spi) static int ad5592r_spi_remove(struct spi_device *spi) { - return ad5592r_remove(&spi->dev); + ad5592r_remove(&spi->dev); + + return 0; } static const struct spi_device_id ad5592r_spi_ids[] = { diff --git a/drivers/iio/dac/ad5593r.c b/drivers/iio/dac/ad5593r.c index 5b4df36fdc2a..64dd7a0bddf7 100644 --- a/drivers/iio/dac/ad5593r.c +++ b/drivers/iio/dac/ad5593r.c @@ -99,7 +99,9 @@ static int ad5593r_i2c_probe(struct i2c_client *i2c, static int ad5593r_i2c_remove(struct i2c_client *i2c) { - return ad5592r_remove(&i2c->dev); + ad5592r_remove(&i2c->dev); + + return 0; } static const struct i2c_device_id ad5593r_i2c_ids[] = { -- cgit v1.2.3-70-g09d2 From 3ceed0211a909563b0b68b11d3a316d9f2887293 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 13 Oct 2021 22:32:18 +0200 Subject: iio: dac: ad5686: Make ad5686_remove() return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up to now ad5686_remove() returns zero unconditionally. Make it return void instead which makes it easier to see in the callers that there is no error to handle. Also the return value of i2c and spi remove callbacks is ignored anyway. Signed-off-by: Uwe Kleine-König Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20211013203223.2694577-11-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686-spi.c | 4 +++- drivers/iio/dac/ad5686.c | 4 +--- drivers/iio/dac/ad5686.h | 2 +- drivers/iio/dac/ad5696-i2c.c | 4 +++- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c index 0188ded5137c..2628810fdbb1 100644 --- a/drivers/iio/dac/ad5686-spi.c +++ b/drivers/iio/dac/ad5686-spi.c @@ -97,7 +97,9 @@ static int ad5686_spi_probe(struct spi_device *spi) static int ad5686_spi_remove(struct spi_device *spi) { - return ad5686_remove(&spi->dev); + ad5686_remove(&spi->dev); + + return 0; } static const struct spi_device_id ad5686_spi_id[] = { diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index fcb64f20ff64..8f001db775f4 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -538,7 +538,7 @@ error_disable_reg: } EXPORT_SYMBOL_GPL(ad5686_probe); -int ad5686_remove(struct device *dev) +void ad5686_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct ad5686_state *st = iio_priv(indio_dev); @@ -546,8 +546,6 @@ int ad5686_remove(struct device *dev) iio_device_unregister(indio_dev); if (!IS_ERR(st->reg)) regulator_disable(st->reg); - - return 0; } EXPORT_SYMBOL_GPL(ad5686_remove); diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index f89a6f92b427..cd5fff9e9d53 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -154,7 +154,7 @@ int ad5686_probe(struct device *dev, const char *name, ad5686_write_func write, ad5686_read_func read); -int ad5686_remove(struct device *dev); +void ad5686_remove(struct device *dev); #endif /* __DRIVERS_IIO_DAC_AD5686_H__ */ diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c index 24a6a4a5a2e0..93f0e0e66c22 100644 --- a/drivers/iio/dac/ad5696-i2c.c +++ b/drivers/iio/dac/ad5696-i2c.c @@ -67,7 +67,9 @@ static int ad5686_i2c_probe(struct i2c_client *i2c, static int ad5686_i2c_remove(struct i2c_client *i2c) { - return ad5686_remove(&i2c->dev); + ad5686_remove(&i2c->dev); + + return 0; } static const struct i2c_device_id ad5686_i2c_id[] = { -- cgit v1.2.3-70-g09d2 From c7143c49c6041da1739765055645337e082abeb4 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 13 Oct 2021 22:32:19 +0200 Subject: iio: health: afe4403: Don't return an error in .remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only effect of returning an error in an spi .remove() callback is that the spi core issues another warning message. Don't report the same problem twice and return 0 unconditionally instead. Also degrade the log level to warning, as nothing really bad is expected from a failure to put the device in suspend mode. Signed-off-by: Uwe Kleine-König Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20211013203223.2694577-12-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/health/afe4403.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index e89185544d0f..97b82f9a8e45 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -589,10 +589,8 @@ static int afe4403_remove(struct spi_device *spi) iio_trigger_unregister(afe->trig); ret = regulator_disable(afe->regulator); - if (ret) { - dev_err(afe->dev, "Unable to disable regulator\n"); - return ret; - } + if (ret) + dev_warn(afe->dev, "Unable to disable regulator\n"); return 0; } -- cgit v1.2.3-70-g09d2 From 4b6fb9f3e98ca157ae09803731c7bd6d90519a6d Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 13 Oct 2021 22:32:21 +0200 Subject: iio: magn: hmc5843: Make hmc5843_common_remove() return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up to now hmc5843_common_remove() returns zero unconditionally. Make it return void instead which makes it easier to see in the callers that there is no error to handle. Also the return value of i2c and spi remove callbacks is ignored anyway. Signed-off-by: Uwe Kleine-König Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20211013203223.2694577-14-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/hmc5843.h | 2 +- drivers/iio/magnetometer/hmc5843_core.c | 4 +--- drivers/iio/magnetometer/hmc5843_i2c.c | 4 +++- drivers/iio/magnetometer/hmc5843_spi.c | 4 +++- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/iio/magnetometer/hmc5843.h b/drivers/iio/magnetometer/hmc5843.h index 242f742f2643..9120c8bbf3dd 100644 --- a/drivers/iio/magnetometer/hmc5843.h +++ b/drivers/iio/magnetometer/hmc5843.h @@ -50,7 +50,7 @@ struct hmc5843_data { int hmc5843_common_probe(struct device *dev, struct regmap *regmap, enum hmc5843_ids id, const char *name); -int hmc5843_common_remove(struct device *dev); +void hmc5843_common_remove(struct device *dev); int hmc5843_common_suspend(struct device *dev); int hmc5843_common_resume(struct device *dev); diff --git a/drivers/iio/magnetometer/hmc5843_core.c b/drivers/iio/magnetometer/hmc5843_core.c index cf62057480cf..f08726bf5ec3 100644 --- a/drivers/iio/magnetometer/hmc5843_core.c +++ b/drivers/iio/magnetometer/hmc5843_core.c @@ -671,7 +671,7 @@ buffer_setup_err: } EXPORT_SYMBOL(hmc5843_common_probe); -int hmc5843_common_remove(struct device *dev) +void hmc5843_common_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); @@ -680,8 +680,6 @@ int hmc5843_common_remove(struct device *dev) /* sleep mode to save power */ hmc5843_set_mode(iio_priv(indio_dev), HMC5843_MODE_SLEEP); - - return 0; } EXPORT_SYMBOL(hmc5843_common_remove); diff --git a/drivers/iio/magnetometer/hmc5843_i2c.c b/drivers/iio/magnetometer/hmc5843_i2c.c index 67fe657fdb3e..bc6e12f1d521 100644 --- a/drivers/iio/magnetometer/hmc5843_i2c.c +++ b/drivers/iio/magnetometer/hmc5843_i2c.c @@ -67,7 +67,9 @@ static int hmc5843_i2c_probe(struct i2c_client *cli, static int hmc5843_i2c_remove(struct i2c_client *client) { - return hmc5843_common_remove(&client->dev); + hmc5843_common_remove(&client->dev); + + return 0; } static const struct i2c_device_id hmc5843_id[] = { diff --git a/drivers/iio/magnetometer/hmc5843_spi.c b/drivers/iio/magnetometer/hmc5843_spi.c index d827554c346e..89cf59a62c28 100644 --- a/drivers/iio/magnetometer/hmc5843_spi.c +++ b/drivers/iio/magnetometer/hmc5843_spi.c @@ -76,7 +76,9 @@ static int hmc5843_spi_probe(struct spi_device *spi) static int hmc5843_spi_remove(struct spi_device *spi) { - return hmc5843_common_remove(&spi->dev); + hmc5843_common_remove(&spi->dev); + + return 0; } static const struct spi_device_id hmc5843_id[] = { -- cgit v1.2.3-70-g09d2 From 6dcfe3fe936030e83b30f38a223d026c01d6fe88 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 13 Oct 2021 22:32:22 +0200 Subject: iio: potentiometer: max5487: Don't return an error in .remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only effect of returning an error in an spi .remove() callback is that the spi core issues a generic warning message. Instead emit a more specific error message and return 0 to not report the same issue twice. Signed-off-by: Uwe Kleine-König Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20211013203223.2694577-15-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/potentiometer/max5487.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/iio/potentiometer/max5487.c b/drivers/iio/potentiometer/max5487.c index 1c0d46a96200..007c2bd324cb 100644 --- a/drivers/iio/potentiometer/max5487.c +++ b/drivers/iio/potentiometer/max5487.c @@ -115,11 +115,16 @@ static int max5487_spi_probe(struct spi_device *spi) static int max5487_spi_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); + int ret; iio_device_unregister(indio_dev); /* save both wiper regs to NV regs */ - return max5487_write_cmd(spi, MAX5487_COPY_AB_TO_NV); + ret = max5487_write_cmd(spi, MAX5487_COPY_AB_TO_NV); + if (ret) + dev_warn(&spi->dev, "Failed to save wiper regs to NV regs\n"); + + return 0; } static const struct spi_device_id max5487_id[] = { -- cgit v1.2.3-70-g09d2 From f840cbed7a7c48a89ff64f7e34c3b41adb4158c6 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 13 Oct 2021 22:32:23 +0200 Subject: iio: pressure: ms5611: Make ms5611_remove() return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up to now ms5611_remove() returns zero unconditionally. Make it return void instead which makes it easier to see in the callers that there is no error to handle. Also the return value of i2c and spi remove callbacks is ignored anyway. Signed-off-by: Uwe Kleine-König Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20211013203223.2694577-16-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/ms5611.h | 2 +- drivers/iio/pressure/ms5611_core.c | 4 +--- drivers/iio/pressure/ms5611_i2c.c | 4 +++- drivers/iio/pressure/ms5611_spi.c | 4 +++- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/iio/pressure/ms5611.h b/drivers/iio/pressure/ms5611.h index bc06271fa38b..86b1c4b1820d 100644 --- a/drivers/iio/pressure/ms5611.h +++ b/drivers/iio/pressure/ms5611.h @@ -61,6 +61,6 @@ struct ms5611_state { int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, const char *name, int type); -int ms5611_remove(struct iio_dev *indio_dev); +void ms5611_remove(struct iio_dev *indio_dev); #endif /* _MS5611_H */ diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c index 214b0d25f598..ee75f08655c9 100644 --- a/drivers/iio/pressure/ms5611_core.c +++ b/drivers/iio/pressure/ms5611_core.c @@ -474,13 +474,11 @@ err_fini: } EXPORT_SYMBOL(ms5611_probe); -int ms5611_remove(struct iio_dev *indio_dev) +void ms5611_remove(struct iio_dev *indio_dev) { iio_device_unregister(indio_dev); iio_triggered_buffer_cleanup(indio_dev); ms5611_fini(indio_dev); - - return 0; } EXPORT_SYMBOL(ms5611_remove); diff --git a/drivers/iio/pressure/ms5611_i2c.c b/drivers/iio/pressure/ms5611_i2c.c index 7c04f730430c..5c82d80f85b6 100644 --- a/drivers/iio/pressure/ms5611_i2c.c +++ b/drivers/iio/pressure/ms5611_i2c.c @@ -110,7 +110,9 @@ static int ms5611_i2c_probe(struct i2c_client *client, static int ms5611_i2c_remove(struct i2c_client *client) { - return ms5611_remove(i2c_get_clientdata(client)); + ms5611_remove(i2c_get_clientdata(client)); + + return 0; } static const struct of_device_id ms5611_i2c_matches[] = { diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c index 45d3a7d5be8e..79bed64c9b68 100644 --- a/drivers/iio/pressure/ms5611_spi.c +++ b/drivers/iio/pressure/ms5611_spi.c @@ -112,7 +112,9 @@ static int ms5611_spi_probe(struct spi_device *spi) static int ms5611_spi_remove(struct spi_device *spi) { - return ms5611_remove(spi_get_drvdata(spi)); + ms5611_remove(spi_get_drvdata(spi)); + + return 0; } static const struct of_device_id ms5611_spi_matches[] = { -- cgit v1.2.3-70-g09d2 From 0336d605daeea142c3ec9b143d0131a137f45108 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 13 Oct 2021 16:43:26 +0200 Subject: iio: imx8qxp-adc: mark PM functions as __maybe_unused Without CONFIG_PM_SLEEP, the runtime suspend/resume functions are unused, producing a warning: drivers/iio/adc/imx8qxp-adc.c:433:12: error: 'imx8qxp_adc_runtime_resume' defined but not used [-Werror=unused-function] 433 | static int imx8qxp_adc_runtime_resume(struct device *dev) | ^~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/iio/adc/imx8qxp-adc.c:419:12: error: 'imx8qxp_adc_runtime_suspend' defined but not used [-Werror=unused-function] 419 | static int imx8qxp_adc_runtime_suspend(struct device *dev) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~ Mark them as __maybe_unused to shut up the compiler. Fixes: 1e23dcaa1a9f ("iio: imx8qxp-adc: Add driver support for NXP IMX8QXP ADC") Signed-off-by: Arnd Bergmann Reviewed-by: Cai Huoqing Link: https://lore.kernel.org/r/20211013144338.2261316-1-arnd@kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/adc/imx8qxp-adc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/imx8qxp-adc.c b/drivers/iio/adc/imx8qxp-adc.c index 5030e0d8318d..901dd8e1b32f 100644 --- a/drivers/iio/adc/imx8qxp-adc.c +++ b/drivers/iio/adc/imx8qxp-adc.c @@ -416,7 +416,7 @@ static int imx8qxp_adc_remove(struct platform_device *pdev) return 0; } -static int imx8qxp_adc_runtime_suspend(struct device *dev) +static __maybe_unused int imx8qxp_adc_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct imx8qxp_adc *adc = iio_priv(indio_dev); @@ -430,7 +430,7 @@ static int imx8qxp_adc_runtime_suspend(struct device *dev) return 0; } -static int imx8qxp_adc_runtime_resume(struct device *dev) +static __maybe_unused int imx8qxp_adc_runtime_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct imx8qxp_adc *adc = iio_priv(indio_dev); -- cgit v1.2.3-70-g09d2 From 32e9f56a96d8d0f23cb2aeb2a3cd18d40393e787 Mon Sep 17 00:00:00 2001 From: Todd Kjos Date: Fri, 15 Oct 2021 16:38:11 -0700 Subject: binder: don't detect sender/target during buffer cleanup When freeing txn buffers, binder_transaction_buffer_release() attempts to detect whether the current context is the target by comparing current->group_leader to proc->tsk. This is an unreliable test. Instead explicitly pass an 'is_failure' boolean. Detecting the sender was being used as a way to tell if the transaction failed to be sent. When cleaning up after failing to send a transaction, there is no need to close the fds associated with a BINDER_TYPE_FDA object. Now 'is_failure' can be used to accurately detect this case. Fixes: 44d8047f1d87 ("binder: use standard functions to allocate fds") Cc: stable Acked-by: Christian Brauner Signed-off-by: Todd Kjos Link: https://lore.kernel.org/r/20211015233811.3532235-1-tkjos@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 9edacc8b9768..fe4c3b49eec1 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1870,7 +1870,7 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, binder_dec_node(buffer->target_node, 1, 0); off_start_offset = ALIGN(buffer->data_size, sizeof(void *)); - off_end_offset = is_failure ? failed_at : + off_end_offset = is_failure && failed_at ? failed_at : off_start_offset + buffer->offsets_size; for (buffer_offset = off_start_offset; buffer_offset < off_end_offset; buffer_offset += sizeof(binder_size_t)) { @@ -1956,9 +1956,8 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, binder_size_t fd_buf_size; binder_size_t num_valid; - if (proc->tsk != current->group_leader) { + if (is_failure) { /* - * Nothing to do if running in sender context * The fd fixups have not been applied so no * fds need to be closed. */ @@ -3185,6 +3184,7 @@ err_invalid_target_handle: * binder_free_buf() - free the specified buffer * @proc: binder proc that owns buffer * @buffer: buffer to be freed + * @is_failure: failed to send transaction * * If buffer for an async transaction, enqueue the next async * transaction from the node. @@ -3194,7 +3194,7 @@ err_invalid_target_handle: static void binder_free_buf(struct binder_proc *proc, struct binder_thread *thread, - struct binder_buffer *buffer) + struct binder_buffer *buffer, bool is_failure) { binder_inner_proc_lock(proc); if (buffer->transaction) { @@ -3222,7 +3222,7 @@ binder_free_buf(struct binder_proc *proc, binder_node_inner_unlock(buf_node); } trace_binder_transaction_buffer_release(buffer); - binder_transaction_buffer_release(proc, thread, buffer, 0, false); + binder_transaction_buffer_release(proc, thread, buffer, 0, is_failure); binder_alloc_free_buf(&proc->alloc, buffer); } @@ -3424,7 +3424,7 @@ static int binder_thread_write(struct binder_proc *proc, proc->pid, thread->pid, (u64)data_ptr, buffer->debug_id, buffer->transaction ? "active" : "finished"); - binder_free_buf(proc, thread, buffer); + binder_free_buf(proc, thread, buffer, false); break; } @@ -4117,7 +4117,7 @@ retry: buffer->transaction = NULL; binder_cleanup_transaction(t, "fd fixups failed", BR_FAILED_REPLY); - binder_free_buf(proc, thread, buffer); + binder_free_buf(proc, thread, buffer, true); binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, "%d:%d %stransaction %d fd fixups failed %d/%d, line %d\n", proc->pid, thread->pid, -- cgit v1.2.3-70-g09d2 From 15c9a359094ec6251578b02387436bc64f11a477 Mon Sep 17 00:00:00 2001 From: Ziyang Xuan Date: Sat, 16 Oct 2021 13:20:47 +0800 Subject: char: xillybus: fix msg_ep UAF in xillyusb_probe() When endpoint_alloc() return failed in xillyusb_setup_base_eps(), 'xdev->msg_ep' will be freed but not set to NULL. That lets program enter fail handling to cleanup_dev() in xillyusb_probe(). Check for 'xdev->msg_ep' is invalid in cleanup_dev() because 'xdev->msg_ep' did not set to NULL when was freed. So the UAF problem for 'xdev->msg_ep' is triggered. ================================================================== BUG: KASAN: use-after-free in fifo_mem_release+0x1f4/0x210 CPU: 0 PID: 166 Comm: kworker/0:2 Not tainted 5.15.0-rc5+ #19 Call Trace: dump_stack_lvl+0xe2/0x152 print_address_description.constprop.0+0x21/0x140 ? fifo_mem_release+0x1f4/0x210 kasan_report.cold+0x7f/0x11b ? xillyusb_probe+0x530/0x700 ? fifo_mem_release+0x1f4/0x210 fifo_mem_release+0x1f4/0x210 ? __sanitizer_cov_trace_pc+0x1d/0x50 endpoint_dealloc+0x35/0x2b0 cleanup_dev+0x90/0x120 xillyusb_probe+0x59a/0x700 ... Freed by task 166: kasan_save_stack+0x1b/0x40 kasan_set_track+0x1c/0x30 kasan_set_free_info+0x20/0x30 __kasan_slab_free+0x109/0x140 kfree+0x117/0x4c0 xillyusb_probe+0x606/0x700 Set 'xdev->msg_ep' to NULL after being freed in xillyusb_setup_base_eps() to fix the UAF problem. Fixes: a53d1202aef1 ("char: xillybus: Add driver for XillyUSB (Xillybus variant for USB)") Cc: stable Acked-by: Eli Billauer Signed-off-by: Ziyang Xuan Link: https://lore.kernel.org/r/20211016052047.1611983-1-william.xuanziyang@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/char/xillybus/xillyusb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/char/xillybus/xillyusb.c b/drivers/char/xillybus/xillyusb.c index e7f88f35c702..dc3551796e5e 100644 --- a/drivers/char/xillybus/xillyusb.c +++ b/drivers/char/xillybus/xillyusb.c @@ -1912,6 +1912,7 @@ static int xillyusb_setup_base_eps(struct xillyusb_dev *xdev) dealloc: endpoint_dealloc(xdev->msg_ep); /* Also frees FIFO mem if allocated */ + xdev->msg_ep = NULL; return -ENOMEM; } -- cgit v1.2.3-70-g09d2 From c3ed761c9e1e4987406671b326dab48a048614ee Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 17 Oct 2021 14:01:06 -0500 Subject: counter/counter-sysfs: use sysfs_emit everywhere In the counter subsystem, we are already using sysfs_emit(), but there were a few places where we were still using sprintf() in *_show() functions. For consistency and added protections, use sysfs_emit() everywhere. Suggested-by: Greg KH Acked-by: William Breathitt Gray Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20211017190106.3472645-1-david@lechnology.com Signed-off-by: Greg Kroah-Hartman --- drivers/counter/counter-sysfs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c index 1ccd771da25f..c2fddbb0d442 100644 --- a/drivers/counter/counter-sysfs.c +++ b/drivers/counter/counter-sysfs.c @@ -113,7 +113,7 @@ static ssize_t counter_comp_u8_show(struct device *dev, /* data should already be boolean but ensure just to be safe */ data = !!data; - return sprintf(buf, "%u\n", (unsigned int)data); + return sysfs_emit(buf, "%u\n", (unsigned int)data); } static ssize_t counter_comp_u8_store(struct device *dev, @@ -196,7 +196,7 @@ static ssize_t counter_comp_u32_show(struct device *dev, case COUNTER_COMP_COUNT_MODE: return sysfs_emit(buf, "%s\n", counter_count_mode_str[data]); default: - return sprintf(buf, "%u\n", (unsigned int)data); + return sysfs_emit(buf, "%u\n", (unsigned int)data); } } @@ -300,7 +300,7 @@ static ssize_t counter_comp_u64_show(struct device *dev, if (err < 0) return err; - return sprintf(buf, "%llu\n", (unsigned long long)data); + return sysfs_emit(buf, "%llu\n", (unsigned long long)data); } static ssize_t counter_comp_u64_store(struct device *dev, @@ -539,7 +539,7 @@ static ssize_t counter_comp_id_show(struct device *dev, { const size_t id = (size_t)to_counter_attribute(attr)->comp.priv; - return sprintf(buf, "%zu\n", id); + return sysfs_emit(buf, "%zu\n", id); } static int counter_comp_id_attr_create(struct device *const dev, -- cgit v1.2.3-70-g09d2 From f5245a5fdf757f50a6c905fc16cceb1a6146ccf5 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 17 Oct 2021 13:55:21 -0500 Subject: counter: drop chrdev_lock This removes the chrdev_lock from the counter subsystem. This was intended to prevent opening the chrdev more than once. However, this doesn't work in practice since userspace can duplicate file descriptors and pass file descriptors to other processes. Since this protection can't be relied on, it is best to just remove it. Suggested-by: Greg KH Acked-by: William Breathitt Gray Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20211017185521.3468640-1-david@lechnology.com Signed-off-by: Greg Kroah-Hartman --- drivers/counter/counter-chrdev.c | 6 ------ drivers/counter/counter-sysfs.c | 13 +++---------- include/linux/counter.h | 7 ------- 3 files changed, 3 insertions(+), 23 deletions(-) diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c index 967c94ae95bb..b747dc81cfc6 100644 --- a/drivers/counter/counter-chrdev.c +++ b/drivers/counter/counter-chrdev.c @@ -384,10 +384,6 @@ static int counter_chrdev_open(struct inode *inode, struct file *filp) typeof(*counter), chrdev); - /* Ensure chrdev is not opened more than 1 at a time */ - if (!atomic_add_unless(&counter->chrdev_lock, 1, 1)) - return -EBUSY; - get_device(&counter->dev); filp->private_data = counter; @@ -419,7 +415,6 @@ out_unlock: mutex_unlock(&counter->ops_exist_lock); put_device(&counter->dev); - atomic_dec(&counter->chrdev_lock); return ret; } @@ -445,7 +440,6 @@ int counter_chrdev_add(struct counter_device *const counter) mutex_init(&counter->events_lock); /* Initialize character device */ - atomic_set(&counter->chrdev_lock, 0); cdev_init(&counter->chrdev, &counter_fops); /* Allocate Counter events queue */ diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c index c2fddbb0d442..8c2d7c29ea59 100644 --- a/drivers/counter/counter-sysfs.c +++ b/drivers/counter/counter-sysfs.c @@ -796,25 +796,18 @@ static int counter_events_queue_size_write(struct counter_device *counter, u64 val) { DECLARE_KFIFO_PTR(events, struct counter_event); - int err = 0; - - /* Ensure chrdev is not opened more than 1 at a time */ - if (!atomic_add_unless(&counter->chrdev_lock, 1, 1)) - return -EBUSY; + int err; /* Allocate new events queue */ err = kfifo_alloc(&events, val, GFP_KERNEL); if (err) - goto exit_early; + return err; /* Swap in new events queue */ kfifo_free(&counter->events); counter->events.kfifo = events.kfifo; -exit_early: - atomic_dec(&counter->chrdev_lock); - - return err; + return 0; } static struct counter_comp counter_num_signals_comp = diff --git a/include/linux/counter.h b/include/linux/counter.h index 22b14a552b1d..0fd99e255a50 100644 --- a/include/linux/counter.h +++ b/include/linux/counter.h @@ -297,7 +297,6 @@ struct counter_ops { * @events: queue of detected Counter events * @events_wait: wait queue to allow blocking reads of Counter events * @events_lock: lock to protect Counter events queue read operations - * @chrdev_lock: lock to limit chrdev to a single open at a time * @ops_exist_lock: lock to prevent use during removal */ struct counter_device { @@ -325,12 +324,6 @@ struct counter_device { DECLARE_KFIFO_PTR(events, struct counter_event); wait_queue_head_t events_wait; struct mutex events_lock; - /* - * chrdev_lock is locked by counter_chrdev_open() and unlocked by - * counter_chrdev_release(), so a mutex is not possible here because - * chrdev_lock will invariably be held when returning to user space - */ - atomic_t chrdev_lock; struct mutex ops_exist_lock; }; -- cgit v1.2.3-70-g09d2 From 8135cc5b270b3f224615bdee8bd7d66afee87991 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 19 Oct 2021 19:09:01 +0530 Subject: MAINTAINERS: Update the entry for MHI bus Since Hemant is not carrying out any maintainership duties let's make him as a dedicated reviewer. Also add the new mailing lists dedicated for MHI in subspace mailing list server. Reviewed-by: Hemant Kumar Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20211019133901.173966-1-manivannan.sadhasivam@linaro.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 8f414775978a..37982beaf91f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12214,7 +12214,8 @@ F: arch/arm64/boot/dts/marvell/armada-3720-uDPU.dts MHI BUS M: Manivannan Sadhasivam -M: Hemant Kumar +R: Hemant Kumar +L: mhi@lists.linux.dev L: linux-arm-msm@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/mani/mhi.git -- cgit v1.2.3-70-g09d2 From d4032cce4538a9d9e8e21e62608a111712842b77 Mon Sep 17 00:00:00 2001 From: Nikita Travkin Date: Wed, 6 Oct 2021 21:30:55 +0500 Subject: dt-bindings: vendor-prefixes: Document liteon vendor prefix Add vendor prefix for LITE-ON Technology Corp. Signed-off-by: Nikita Travkin Acked-by: Rob Herring Link: https://lore.kernel.org/r/20211006163058.145842-1-nikita@trvn.ru Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 42b529a8e5db..be42b4b2fcd5 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -653,6 +653,8 @@ patternProperties: description: Linux-specific binding "^linx,.*": description: Linx Technologies + "^liteon,.*": + description: LITE-ON Technology Corp. "^litex,.*": description: LiteX SoC builder "^lltc,.*": -- cgit v1.2.3-70-g09d2 From ec39f1ead4e4c0a9e66c066d6e566fe1362d1c6e Mon Sep 17 00:00:00 2001 From: Nikita Travkin Date: Wed, 6 Oct 2021 21:30:56 +0500 Subject: dt-bindings: iio: light: Document ltr501 light sensor bindings Add devicetree bindings for ltr501, ltr559 and ltr301. Signed-off-by: Nikita Travkin Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211006163058.145842-2-nikita@trvn.ru Signed-off-by: Jonathan Cameron --- .../bindings/iio/light/liteon,ltr501.yaml | 51 ++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/light/liteon,ltr501.yaml diff --git a/Documentation/devicetree/bindings/iio/light/liteon,ltr501.yaml b/Documentation/devicetree/bindings/iio/light/liteon,ltr501.yaml new file mode 100644 index 000000000000..db0407bc9209 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/liteon,ltr501.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/light/liteon,ltr501.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: LiteON LTR501 I2C Proximity and Light sensor + +maintainers: + - Nikita Travkin + +properties: + compatible: + enum: + - liteon,ltr501 + - liteon,ltr559 + - liteon,ltr301 + + reg: + maxItems: 1 + + vdd-supply: true + vddio-supply: true + + interrupts: + maxItems: 1 + +additionalProperties: false + +required: + - compatible + - reg + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + light-sensor@23 { + compatible = "liteon,ltr559"; + reg = <0x23>; + vdd-supply = <&pm8916_l17>; + vddio-supply = <&pm8916_l6>; + + interrupt-parent = <&msmgpio>; + interrupts = <115 IRQ_TYPE_EDGE_FALLING>; + }; + }; -- cgit v1.2.3-70-g09d2 From f6ec898c9ab9ec3c7dad5cc41fc3f873cdb7f502 Mon Sep 17 00:00:00 2001 From: Nikita Travkin Date: Wed, 6 Oct 2021 21:30:57 +0500 Subject: iio: light: ltr501: Add rudimentary regulator support On some platforms the supply regulators must be enabled before the sensor can work. Add vdd and vddio regulators for the sensor and IO bus power respectively. Signed-off-by: Nikita Travkin Link: https://lore.kernel.org/r/20211006163058.145842-3-nikita@trvn.ru Signed-off-by: Jonathan Cameron --- drivers/iio/light/ltr501.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index 1830221da48d..57851c8ef1c6 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -151,6 +152,7 @@ struct ltr501_chip_info { struct ltr501_data { struct i2c_client *client; + struct regulator_bulk_data regulators[2]; struct mutex lock_als, lock_ps; const struct ltr501_chip_info *chip_info; u8 als_contr, ps_contr; @@ -1379,6 +1381,13 @@ static const struct regmap_config ltr501_regmap_config = { .volatile_reg = ltr501_is_volatile_reg, }; +static void ltr501_disable_regulators(void *d) +{ + struct ltr501_data *data = d; + + regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators); +} + static int ltr501_powerdown(struct ltr501_data *data) { return ltr501_write_contr(data, data->als_contr & @@ -1423,6 +1432,25 @@ static int ltr501_probe(struct i2c_client *client, mutex_init(&data->lock_als); mutex_init(&data->lock_ps); + data->regulators[0].supply = "vdd"; + data->regulators[1].supply = "vddio"; + ret = devm_regulator_bulk_get(&client->dev, + ARRAY_SIZE(data->regulators), + data->regulators); + if (ret) + return dev_err_probe(&client->dev, ret, + "Failed to get regulators\n"); + + ret = regulator_bulk_enable(ARRAY_SIZE(data->regulators), + data->regulators); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&client->dev, + ltr501_disable_regulators, data); + if (ret) + return ret; + data->reg_it = devm_regmap_field_alloc(&client->dev, regmap, reg_field_it); if (IS_ERR(data->reg_it)) { -- cgit v1.2.3-70-g09d2 From 26fa68c1d7a1d6504d6cc158db015acc1a8bc0fd Mon Sep 17 00:00:00 2001 From: Nikita Travkin Date: Wed, 6 Oct 2021 21:30:58 +0500 Subject: iio: light: ltr501: Add of_device_id table Add of_device_id table so the driver can be used on DT platforms without relying on i2c_device_id fallback. (So DT schema validation is possible) Signed-off-by: Nikita Travkin Link: https://lore.kernel.org/r/20211006163058.145842-4-nikita@trvn.ru Signed-off-by: Jonathan Cameron --- drivers/iio/light/ltr501.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index 57851c8ef1c6..7e51aaac0bf8 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -1609,9 +1609,18 @@ static const struct i2c_device_id ltr501_id[] = { }; MODULE_DEVICE_TABLE(i2c, ltr501_id); +static const struct of_device_id ltr501_of_match[] = { + { .compatible = "liteon,ltr501", }, + { .compatible = "liteon,ltr559", }, + { .compatible = "liteon,ltr301", }, + {} +}; +MODULE_DEVICE_TABLE(of, ltr501_of_match); + static struct i2c_driver ltr501_driver = { .driver = { .name = LTR501_DRV_NAME, + .of_match_table = ltr501_of_match, .pm = <r501_pm_ops, .acpi_match_table = ACPI_PTR(ltr_acpi_match), }, -- cgit v1.2.3-70-g09d2 From 0be844470eb929dd2a1388c6dc6d7d6ea2edaf2e Mon Sep 17 00:00:00 2001 From: André Gustavo Nakagomi Lopez Date: Fri, 15 Oct 2021 15:47:12 -0300 Subject: iio: adc: lpc18xx_adc: Convert probe to device managed version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The remove function and the goto sections are not necessary if devm functions are used. Convert device register to devm version. Add hook functions to release device resources, and use them inside probe with devm_add_action, which will release resources on driver detach. To maintain the order of which device resources were released/reseted, register the hook functions as soon as resources are obtained/initialized. Since devres actions are called on driver detach, the remove function and the error-handling goto sections are no longer necessary. Signed-off-by: André Gustavo Nakagomi Lopez Link: https://lore.kernel.org/r/YWnMsGlc/I35gYvy@Andryuu.br Signed-off-by: Jonathan Cameron --- drivers/iio/adc/lpc18xx_adc.c | 61 ++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/drivers/iio/adc/lpc18xx_adc.c b/drivers/iio/adc/lpc18xx_adc.c index 3566990ae87d..7d50107427ac 100644 --- a/drivers/iio/adc/lpc18xx_adc.c +++ b/drivers/iio/adc/lpc18xx_adc.c @@ -115,6 +115,23 @@ static const struct iio_info lpc18xx_adc_info = { .read_raw = lpc18xx_adc_read_raw, }; +static void lpc18xx_clear_cr_reg(void *data) +{ + struct lpc18xx_adc *adc = data; + + writel(0, adc->base + LPC18XX_ADC_CR); +} + +static void lpc18xx_clk_disable(void *clk) +{ + clk_disable_unprepare(clk); +} + +static void lpc18xx_regulator_disable(void *vref) +{ + regulator_disable(vref); +} + static int lpc18xx_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; @@ -127,7 +144,6 @@ static int lpc18xx_adc_probe(struct platform_device *pdev) if (!indio_dev) return -ENOMEM; - platform_set_drvdata(pdev, indio_dev); adc = iio_priv(indio_dev); adc->dev = &pdev->dev; mutex_init(&adc->lock); @@ -163,44 +179,30 @@ static int lpc18xx_adc_probe(struct platform_device *pdev) return ret; } + ret = devm_add_action_or_reset(&pdev->dev, lpc18xx_regulator_disable, adc->vref); + if (ret) + return ret; + ret = clk_prepare_enable(adc->clk); if (ret) { dev_err(&pdev->dev, "unable to enable clock\n"); - goto dis_reg; + return ret; } + ret = devm_add_action_or_reset(&pdev->dev, lpc18xx_clk_disable, + adc->clk); + if (ret) + return ret; + adc->cr_reg = (clkdiv << LPC18XX_ADC_CR_CLKDIV_SHIFT) | LPC18XX_ADC_CR_PDN; writel(adc->cr_reg, adc->base + LPC18XX_ADC_CR); - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "unable to register device\n"); - goto dis_clk; - } - - return 0; - -dis_clk: - writel(0, adc->base + LPC18XX_ADC_CR); - clk_disable_unprepare(adc->clk); -dis_reg: - regulator_disable(adc->vref); - return ret; -} - -static int lpc18xx_adc_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - struct lpc18xx_adc *adc = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - writel(0, adc->base + LPC18XX_ADC_CR); - clk_disable_unprepare(adc->clk); - regulator_disable(adc->vref); + ret = devm_add_action_or_reset(&pdev->dev, lpc18xx_clear_cr_reg, adc); + if (ret) + return ret; - return 0; + return devm_iio_device_register(&pdev->dev, indio_dev); } static const struct of_device_id lpc18xx_adc_match[] = { @@ -211,7 +213,6 @@ MODULE_DEVICE_TABLE(of, lpc18xx_adc_match); static struct platform_driver lpc18xx_adc_driver = { .probe = lpc18xx_adc_probe, - .remove = lpc18xx_adc_remove, .driver = { .name = "lpc18xx-adc", .of_match_table = lpc18xx_adc_match, -- cgit v1.2.3-70-g09d2 From eeb82b54bb03a0899505996e8da7d67b4ff4aec2 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 15 Oct 2021 16:32:54 +0100 Subject: iio: buffer: Fix uninitialized variable ret When !iio_buffer_space_available(rb) is true and signal_pending(current) is false the end of the do-while loop is reached and the uninitialized variable ret is zero checked. Fix this by initializing variable ret to zero. Addresses-Coverity: ("Uninitialized scalar variable") Fixes: 9eeee3b0bf19 ("iio: Add output buffer support") Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20211015153254.33783-1-colin.king@canonical.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index b884d78657cb..c97fb8462395 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -179,7 +179,7 @@ static ssize_t iio_buffer_write(struct file *filp, const char __user *buf, struct iio_buffer *rb = ib->buffer; struct iio_dev *indio_dev = ib->indio_dev; DEFINE_WAIT_FUNC(wait, woken_wake_function); - int ret; + int ret = 0; size_t written; if (!indio_dev->info) -- cgit v1.2.3-70-g09d2 From 08e9734afc7f4bf01d3011215405d77c98613827 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Fri, 8 Oct 2021 17:28:49 +0800 Subject: iio: adc: ab8500-gpadc: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20211008092858.495-1-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ab8500-gpadc.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/iio/adc/ab8500-gpadc.c b/drivers/iio/adc/ab8500-gpadc.c index 7b5212ba5501..4c46a201d4ef 100644 --- a/drivers/iio/adc/ab8500-gpadc.c +++ b/drivers/iio/adc/ab8500-gpadc.c @@ -1103,17 +1103,15 @@ static int ab8500_gpadc_probe(struct platform_device *pdev) return ret; gpadc->irq_sw = platform_get_irq_byname(pdev, "SW_CONV_END"); - if (gpadc->irq_sw < 0) { - dev_err(dev, "failed to get platform sw_conv_end irq\n"); - return gpadc->irq_sw; - } + if (gpadc->irq_sw < 0) + return dev_err_probe(dev, gpadc->irq_sw, + "failed to get platform sw_conv_end irq\n"); if (is_ab8500(gpadc->ab8500)) { gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END"); - if (gpadc->irq_hw < 0) { - dev_err(dev, "failed to get platform hw_conv_end irq\n"); - return gpadc->irq_hw; - } + if (gpadc->irq_hw < 0) + return dev_err_probe(dev, gpadc->irq_hw, + "failed to get platform hw_conv_end irq\n"); } else { gpadc->irq_hw = 0; } @@ -1146,11 +1144,9 @@ static int ab8500_gpadc_probe(struct platform_device *pdev) /* The VTVout LDO used to power the AB8500 GPADC */ gpadc->vddadc = devm_regulator_get(dev, "vddadc"); - if (IS_ERR(gpadc->vddadc)) { - ret = PTR_ERR(gpadc->vddadc); - dev_err(dev, "failed to get vddadc\n"); - return ret; - } + if (IS_ERR(gpadc->vddadc)) + return dev_err_probe(dev, PTR_ERR(gpadc->vddadc), + "failed to get vddadc\n"); ret = regulator_enable(gpadc->vddadc); if (ret) { -- cgit v1.2.3-70-g09d2 From 1c17abbc953e287a27f660d08822051013e509ce Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Fri, 8 Oct 2021 17:28:50 +0800 Subject: iio: adc: imx7d_adc: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20211008092858.495-2-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/imx7d_adc.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/iio/adc/imx7d_adc.c b/drivers/iio/adc/imx7d_adc.c index 4969a5f941e3..092f8d296527 100644 --- a/drivers/iio/adc/imx7d_adc.c +++ b/drivers/iio/adc/imx7d_adc.c @@ -493,22 +493,16 @@ static int imx7d_adc_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) - return irq; + return dev_err_probe(dev, irq, "Failed getting irq\n"); info->clk = devm_clk_get(dev, "adc"); - if (IS_ERR(info->clk)) { - ret = PTR_ERR(info->clk); - dev_err(dev, "Failed getting clock, err = %d\n", ret); - return ret; - } + if (IS_ERR(info->clk)) + return dev_err_probe(dev, PTR_ERR(info->clk), "Failed getting clock\n"); info->vref = devm_regulator_get(dev, "vref"); - if (IS_ERR(info->vref)) { - ret = PTR_ERR(info->vref); - dev_err(dev, - "Failed getting reference voltage, err = %d\n", ret); - return ret; - } + if (IS_ERR(info->vref)) + return dev_err_probe(dev, PTR_ERR(info->vref), + "Failed getting reference voltage\n"); platform_set_drvdata(pdev, indio_dev); -- cgit v1.2.3-70-g09d2 From 922f694b582286ce82e1140a522d20a203fddba9 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Fri, 8 Oct 2021 17:28:51 +0800 Subject: iio: adc: lpc18xx_adc: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20211008092858.495-3-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/lpc18xx_adc.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/iio/adc/lpc18xx_adc.c b/drivers/iio/adc/lpc18xx_adc.c index 7d50107427ac..ceefa4d793cf 100644 --- a/drivers/iio/adc/lpc18xx_adc.c +++ b/drivers/iio/adc/lpc18xx_adc.c @@ -153,19 +153,17 @@ static int lpc18xx_adc_probe(struct platform_device *pdev) return PTR_ERR(adc->base); adc->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(adc->clk)) { - dev_err(&pdev->dev, "error getting clock\n"); - return PTR_ERR(adc->clk); - } + if (IS_ERR(adc->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(adc->clk), + "error getting clock\n"); rate = clk_get_rate(adc->clk); clkdiv = DIV_ROUND_UP(rate, LPC18XX_ADC_CLK_TARGET); adc->vref = devm_regulator_get(&pdev->dev, "vref"); - if (IS_ERR(adc->vref)) { - dev_err(&pdev->dev, "error getting regulator\n"); - return PTR_ERR(adc->vref); - } + if (IS_ERR(adc->vref)) + return dev_err_probe(&pdev->dev, PTR_ERR(adc->vref), + "error getting regulator\n"); indio_dev->name = dev_name(&pdev->dev); indio_dev->info = &lpc18xx_adc_info; -- cgit v1.2.3-70-g09d2 From 9444794b58bf25787baf4c4c69e05077b3795547 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Fri, 8 Oct 2021 17:28:52 +0800 Subject: iio: adc: max1118: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20211008092858.495-4-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1118.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/max1118.c b/drivers/iio/adc/max1118.c index 8cec9d949083..a41bc570be21 100644 --- a/drivers/iio/adc/max1118.c +++ b/drivers/iio/adc/max1118.c @@ -221,10 +221,9 @@ static int max1118_probe(struct spi_device *spi) if (id->driver_data == max1118) { adc->reg = devm_regulator_get(&spi->dev, "vref"); - if (IS_ERR(adc->reg)) { - dev_err(&spi->dev, "failed to get vref regulator\n"); - return PTR_ERR(adc->reg); - } + if (IS_ERR(adc->reg)) + return dev_err_probe(&spi->dev, PTR_ERR(adc->reg), + "failed to get vref regulator\n"); ret = regulator_enable(adc->reg); if (ret) return ret; -- cgit v1.2.3-70-g09d2 From 070a83ff635cc0d4b895ccf8eefd70aa09cbae14 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Fri, 8 Oct 2021 17:28:53 +0800 Subject: iio: adc: max1241: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20211008092858.495-5-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1241.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/max1241.c b/drivers/iio/adc/max1241.c index b60f8448f21a..a5afd84af58b 100644 --- a/drivers/iio/adc/max1241.c +++ b/drivers/iio/adc/max1241.c @@ -148,10 +148,9 @@ static int max1241_probe(struct spi_device *spi) mutex_init(&adc->lock); adc->vdd = devm_regulator_get(dev, "vdd"); - if (IS_ERR(adc->vdd)) { - dev_err(dev, "failed to get vdd regulator\n"); - return PTR_ERR(adc->vdd); - } + if (IS_ERR(adc->vdd)) + return dev_err_probe(dev, PTR_ERR(adc->vdd), + "failed to get vdd regulator\n"); ret = regulator_enable(adc->vdd); if (ret) @@ -164,10 +163,9 @@ static int max1241_probe(struct spi_device *spi) } adc->vref = devm_regulator_get(dev, "vref"); - if (IS_ERR(adc->vref)) { - dev_err(dev, "failed to get vref regulator\n"); - return PTR_ERR(adc->vref); - } + if (IS_ERR(adc->vref)) + return dev_err_probe(dev, PTR_ERR(adc->vref), + "failed to get vref regulator\n"); ret = regulator_enable(adc->vref); if (ret) @@ -182,7 +180,8 @@ static int max1241_probe(struct spi_device *spi) adc->shutdown = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH); if (IS_ERR(adc->shutdown)) - return PTR_ERR(adc->shutdown); + return dev_err_probe(dev, PTR_ERR(adc->shutdown), + "cannot get shutdown gpio\n"); if (adc->shutdown) dev_dbg(dev, "shutdown pin passed, low-power mode enabled"); -- cgit v1.2.3-70-g09d2 From a5999024b5ba0e6e00a8b95f1d758c69d470b0a3 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Fri, 8 Oct 2021 17:28:54 +0800 Subject: iio: adc: meson_saradc: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Reviewed-by: Martin Blumenstingl Tested-by: Martin Blumenstingl #Meson8b Odroid-C1 Link: https://lore.kernel.org/r/20211008092858.495-6-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/meson_saradc.c | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index 705d5e11a54b..62cc6fb0ef85 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -1230,35 +1230,31 @@ static int meson_sar_adc_probe(struct platform_device *pdev) return ret; priv->clkin = devm_clk_get(&pdev->dev, "clkin"); - if (IS_ERR(priv->clkin)) { - dev_err(&pdev->dev, "failed to get clkin\n"); - return PTR_ERR(priv->clkin); - } + if (IS_ERR(priv->clkin)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->clkin), + "failed to get clkin\n"); priv->core_clk = devm_clk_get(&pdev->dev, "core"); - if (IS_ERR(priv->core_clk)) { - dev_err(&pdev->dev, "failed to get core clk\n"); - return PTR_ERR(priv->core_clk); - } + if (IS_ERR(priv->core_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->core_clk), + "failed to get core clk\n"); priv->adc_clk = devm_clk_get(&pdev->dev, "adc_clk"); if (IS_ERR(priv->adc_clk)) { - if (PTR_ERR(priv->adc_clk) == -ENOENT) { + if (PTR_ERR(priv->adc_clk) == -ENOENT) priv->adc_clk = NULL; - } else { - dev_err(&pdev->dev, "failed to get adc clk\n"); - return PTR_ERR(priv->adc_clk); - } + else + return dev_err_probe(&pdev->dev, PTR_ERR(priv->adc_clk), + "failed to get adc clk\n"); } priv->adc_sel_clk = devm_clk_get(&pdev->dev, "adc_sel"); if (IS_ERR(priv->adc_sel_clk)) { - if (PTR_ERR(priv->adc_sel_clk) == -ENOENT) { + if (PTR_ERR(priv->adc_sel_clk) == -ENOENT) priv->adc_sel_clk = NULL; - } else { - dev_err(&pdev->dev, "failed to get adc_sel clk\n"); - return PTR_ERR(priv->adc_sel_clk); - } + else + return dev_err_probe(&pdev->dev, PTR_ERR(priv->adc_sel_clk), + "failed to get adc_sel clk\n"); } /* on pre-GXBB SoCs the SAR ADC itself provides the ADC clock: */ @@ -1269,10 +1265,9 @@ static int meson_sar_adc_probe(struct platform_device *pdev) } priv->vref = devm_regulator_get(&pdev->dev, "vref"); - if (IS_ERR(priv->vref)) { - dev_err(&pdev->dev, "failed to get vref regulator\n"); - return PTR_ERR(priv->vref); - } + if (IS_ERR(priv->vref)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->vref), + "failed to get vref regulator\n"); priv->calibscale = MILLION; -- cgit v1.2.3-70-g09d2 From 94f08a06685e9ef724d9a55a5192b38cb9e4f71e Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Fri, 8 Oct 2021 17:28:55 +0800 Subject: iio: adc: qcom-pm8xxx-xoadc: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. BTW, change the return value from 'ENXIO' to 'ENODEV', perfer ENODEV which means no such device. Signed-off-by: Cai Huoqing Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20211008092858.495-7-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/qcom-pm8xxx-xoadc.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c index 0610bf254771..21d7eff645c3 100644 --- a/drivers/iio/adc/qcom-pm8xxx-xoadc.c +++ b/drivers/iio/adc/qcom-pm8xxx-xoadc.c @@ -910,16 +910,15 @@ static int pm8xxx_xoadc_probe(struct platform_device *pdev) map = dev_get_regmap(dev->parent, NULL); if (!map) { dev_err(dev, "parent regmap unavailable.\n"); - return -ENXIO; + return -ENODEV; } adc->map = map; /* Bring up regulator */ adc->vref = devm_regulator_get(dev, "xoadc-ref"); - if (IS_ERR(adc->vref)) { - dev_err(dev, "failed to get XOADC VREF regulator\n"); - return PTR_ERR(adc->vref); - } + if (IS_ERR(adc->vref)) + return dev_err_probe(dev, PTR_ERR(adc->vref), + "failed to get XOADC VREF regulator\n"); ret = regulator_enable(adc->vref); if (ret) { dev_err(dev, "failed to enable XOADC VREF regulator\n"); -- cgit v1.2.3-70-g09d2 From 8f46a93bdc730dd2022a83ccde66c36a6599ecd6 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Fri, 8 Oct 2021 17:28:56 +0800 Subject: iio: adc: rockchip_saradc: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20211008092858.495-8-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/rockchip_saradc.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c index a56a0d7337ca..14b8df4ca9c8 100644 --- a/drivers/iio/adc/rockchip_saradc.c +++ b/drivers/iio/adc/rockchip_saradc.c @@ -360,7 +360,8 @@ static int rockchip_saradc_probe(struct platform_device *pdev) if (IS_ERR(info->reset)) { ret = PTR_ERR(info->reset); if (ret != -ENOENT) - return ret; + return dev_err_probe(&pdev->dev, ret, + "failed to get saradc-apb\n"); dev_dbg(&pdev->dev, "no reset control found\n"); info->reset = NULL; @@ -370,7 +371,7 @@ static int rockchip_saradc_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) - return irq; + return dev_err_probe(&pdev->dev, irq, "failed to get irq\n"); ret = devm_request_irq(&pdev->dev, irq, rockchip_saradc_isr, 0, dev_name(&pdev->dev), info); @@ -380,23 +381,19 @@ static int rockchip_saradc_probe(struct platform_device *pdev) } info->pclk = devm_clk_get(&pdev->dev, "apb_pclk"); - if (IS_ERR(info->pclk)) { - dev_err(&pdev->dev, "failed to get pclk\n"); - return PTR_ERR(info->pclk); - } + if (IS_ERR(info->pclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(info->pclk), + "failed to get pclk\n"); info->clk = devm_clk_get(&pdev->dev, "saradc"); - if (IS_ERR(info->clk)) { - dev_err(&pdev->dev, "failed to get adc clock\n"); - return PTR_ERR(info->clk); - } + if (IS_ERR(info->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(info->clk), + "failed to get adc clock\n"); info->vref = devm_regulator_get(&pdev->dev, "vref"); - if (IS_ERR(info->vref)) { - dev_err(&pdev->dev, "failed to get regulator, %ld\n", - PTR_ERR(info->vref)); - return PTR_ERR(info->vref); - } + if (IS_ERR(info->vref)) + return dev_err_probe(&pdev->dev, PTR_ERR(info->vref), + "failed to get regulator\n"); if (info->reset) rockchip_saradc_reset_controller(info->reset); -- cgit v1.2.3-70-g09d2 From 42351035dc1530363b323b4583307e866dc4464e Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Fri, 8 Oct 2021 17:28:57 +0800 Subject: iio: adc: ti-ads7950: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20211008092858.495-9-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads7950.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c index a2b83f0bd526..a7efa3eada2c 100644 --- a/drivers/iio/adc/ti-ads7950.c +++ b/drivers/iio/adc/ti-ads7950.c @@ -600,8 +600,8 @@ static int ti_ads7950_probe(struct spi_device *spi) st->reg = devm_regulator_get(&spi->dev, "vref"); if (IS_ERR(st->reg)) { - dev_err(&spi->dev, "Failed to get regulator \"vref\"\n"); - ret = PTR_ERR(st->reg); + ret = dev_err_probe(&spi->dev, PTR_ERR(st->reg), + "Failed to get regulator \"vref\"\n"); goto error_destroy_mutex; } -- cgit v1.2.3-70-g09d2 From 0d31d91e614505803a0788b92f9f1a83178d8a9a Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Fri, 8 Oct 2021 17:26:52 +0800 Subject: iio: light: cm3605: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20211008092656.421-1-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/cm3605.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/iio/light/cm3605.c b/drivers/iio/light/cm3605.c index 4c83953672be..3e7fb16ab1f6 100644 --- a/drivers/iio/light/cm3605.c +++ b/drivers/iio/light/cm3605.c @@ -159,6 +159,7 @@ static int cm3605_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; enum iio_chan_type ch_type; u32 rset; + int irq; int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*cm3605)); @@ -195,12 +196,9 @@ static int cm3605_probe(struct platform_device *pdev) cm3605->aout = devm_iio_channel_get(dev, "aout"); if (IS_ERR(cm3605->aout)) { - if (PTR_ERR(cm3605->aout) == -ENODEV) { - dev_err(dev, "no ADC, deferring...\n"); - return -EPROBE_DEFER; - } - dev_err(dev, "failed to get AOUT ADC channel\n"); - return PTR_ERR(cm3605->aout); + ret = PTR_ERR(cm3605->aout); + ret = (ret == -ENODEV) ? -EPROBE_DEFER : ret; + return dev_err_probe(dev, ret, "failed to get AOUT ADC channel\n"); } ret = iio_get_channel_type(cm3605->aout, &ch_type); if (ret < 0) @@ -211,10 +209,10 @@ static int cm3605_probe(struct platform_device *pdev) } cm3605->vdd = devm_regulator_get(dev, "vdd"); - if (IS_ERR(cm3605->vdd)) { - dev_err(dev, "failed to get VDD regulator\n"); - return PTR_ERR(cm3605->vdd); - } + if (IS_ERR(cm3605->vdd)) + return dev_err_probe(dev, PTR_ERR(cm3605->vdd), + "failed to get VDD regulator\n"); + ret = regulator_enable(cm3605->vdd); if (ret) { dev_err(dev, "failed to enable VDD regulator\n"); @@ -223,13 +221,16 @@ static int cm3605_probe(struct platform_device *pdev) cm3605->aset = devm_gpiod_get(dev, "aset", GPIOD_OUT_HIGH); if (IS_ERR(cm3605->aset)) { - dev_err(dev, "no ASET GPIO\n"); - ret = PTR_ERR(cm3605->aset); + ret = dev_err_probe(dev, PTR_ERR(cm3605->aset), "no ASET GPIO\n"); goto out_disable_vdd; } - ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0), - cm3605_prox_irq, NULL, 0, "cm3605", indio_dev); + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return dev_err_probe(dev, irq, "failed to get irq\n"); + + ret = devm_request_threaded_irq(dev, irq, cm3605_prox_irq, + NULL, 0, "cm3605", indio_dev); if (ret) { dev_err(dev, "unable to request IRQ\n"); goto out_disable_aset; -- cgit v1.2.3-70-g09d2 From 77b91b1cbc26f2d7eebe7294035325076de6e1cc Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Fri, 8 Oct 2021 17:26:54 +0800 Subject: iio: light: gp2ap002: Make use of the helper function dev_err_probe() When possible use dev_err_probe help to properly deal with the PROBE_DEFER error, the benefit is that DEFER issue will be logged in the devices_deferred debugfs file. Using dev_err_probe() can reduce code size, and the error value gets printed. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20211008092656.421-3-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/gp2ap002.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c index f960be7d4001..c6d1d88d3775 100644 --- a/drivers/iio/light/gp2ap002.c +++ b/drivers/iio/light/gp2ap002.c @@ -503,12 +503,9 @@ static int gp2ap002_probe(struct i2c_client *client, if (!gp2ap002->is_gp2ap002s00f) { gp2ap002->alsout = devm_iio_channel_get(dev, "alsout"); if (IS_ERR(gp2ap002->alsout)) { - if (PTR_ERR(gp2ap002->alsout) == -ENODEV) { - dev_err(dev, "no ADC, deferring...\n"); - return -EPROBE_DEFER; - } - dev_err(dev, "failed to get ALSOUT ADC channel\n"); - return PTR_ERR(gp2ap002->alsout); + ret = PTR_ERR(gp2ap002->alsout); + ret = (ret == -ENODEV) ? -EPROBE_DEFER : ret; + return dev_err_probe(dev, ret, "failed to get ALSOUT ADC channel\n"); } ret = iio_get_channel_type(gp2ap002->alsout, &ch_type); if (ret < 0) @@ -521,15 +518,14 @@ static int gp2ap002_probe(struct i2c_client *client, } gp2ap002->vdd = devm_regulator_get(dev, "vdd"); - if (IS_ERR(gp2ap002->vdd)) { - dev_err(dev, "failed to get VDD regulator\n"); - return PTR_ERR(gp2ap002->vdd); - } + if (IS_ERR(gp2ap002->vdd)) + return dev_err_probe(dev, PTR_ERR(gp2ap002->vdd), + "failed to get VDD regulator\n"); + gp2ap002->vio = devm_regulator_get(dev, "vio"); - if (IS_ERR(gp2ap002->vio)) { - dev_err(dev, "failed to get VIO regulator\n"); - return PTR_ERR(gp2ap002->vio); - } + if (IS_ERR(gp2ap002->vio)) + return dev_err_probe(dev, PTR_ERR(gp2ap002->vio), + "failed to get VIO regulator\n"); /* Operating voltage 2.4V .. 3.6V according to datasheet */ ret = regulator_set_voltage(gp2ap002->vdd, 2400000, 3600000); -- cgit v1.2.3-70-g09d2 From 94be878c882d8d784ff44c639bf55f3b029f85af Mon Sep 17 00:00:00 2001 From: Teng Qi Date: Mon, 11 Oct 2021 19:40:03 +0800 Subject: iio: imu: st_lsm6dsx: Avoid potential array overflow in st_lsm6dsx_set_odr() The length of hw->settings->odr_table is 2 and ref_sensor->id is an enum variable whose value is between 0 and 5. However, the value ST_LSM6DSX_ID_MAX (i.e. 5) is not caught properly in switch (sensor->id) { If ref_sensor->id is ST_LSM6DSX_ID_MAX, an array overflow will ocurrs in function st_lsm6dsx_check_odr(): odr_table = &sensor->hw->settings->odr_table[sensor->id]; and in function st_lsm6dsx_set_odr(): reg = &hw->settings->odr_table[ref_sensor->id].reg; To avoid this array overflow, handle ST_LSM6DSX_ID_GYRO explicitly and return -EINVAL for the default case. The enum value ST_LSM6DSX_ID_MAX is only present as an easy way to check the limit and as such is never used, however this is not locally obvious. Reported-by: TOTE Robot Signed-off-by: Teng Qi Acked-by: Lorenzo Bianconi Link: https://lore.kernel.org/r/20211011114003.976221-1-starmiku1207184332@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 0f54df85134a..f2cbbc756459 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -1281,6 +1281,8 @@ st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u32 req_odr) int err; switch (sensor->id) { + case ST_LSM6DSX_ID_GYRO: + break; case ST_LSM6DSX_ID_EXT0: case ST_LSM6DSX_ID_EXT1: case ST_LSM6DSX_ID_EXT2: @@ -1306,8 +1308,8 @@ st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u32 req_odr) } break; } - default: - break; + default: /* should never occur */ + return -EINVAL; } if (req_odr > 0) { -- cgit v1.2.3-70-g09d2 From dd4efd05c565529dad8722301d69e0c19a3292e9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 17 Oct 2021 14:15:51 +0200 Subject: iio: xilinx-xadc: Remove `irq` field from state struct Since commit 2a9685d1a3b7 ("iio: adc: xilinx: use more devres helpers and remove remove()") the `irq` field from XADC driver state struct is only used in the `probe()` function. Use the local `irq` variable throughout the `probe()` function and remove the now unused field from the state struct. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211017121551.24063-1-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/xilinx-xadc-core.c | 5 ++--- drivers/iio/adc/xilinx-xadc.h | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index 198d2916266d..83bea5ef765d 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -1332,7 +1332,6 @@ static int xadc_probe(struct platform_device *pdev) xadc = iio_priv(indio_dev); xadc->ops = id->data; - xadc->irq = irq; init_completion(&xadc->completion); mutex_init(&xadc->mutex); spin_lock_init(&xadc->lock); @@ -1397,7 +1396,7 @@ static int xadc_probe(struct platform_device *pdev) } } - ret = devm_request_irq(dev, xadc->irq, xadc->ops->interrupt_handler, 0, + ret = devm_request_irq(dev, irq, xadc->ops->interrupt_handler, 0, dev_name(dev), indio_dev); if (ret) return ret; @@ -1407,7 +1406,7 @@ static int xadc_probe(struct platform_device *pdev) if (ret) return ret; - ret = xadc->ops->setup(pdev, indio_dev, xadc->irq); + ret = xadc->ops->setup(pdev, indio_dev, irq); if (ret) return ret; diff --git a/drivers/iio/adc/xilinx-xadc.h b/drivers/iio/adc/xilinx-xadc.h index 8b80195725e9..7d78ce698967 100644 --- a/drivers/iio/adc/xilinx-xadc.h +++ b/drivers/iio/adc/xilinx-xadc.h @@ -67,7 +67,6 @@ struct xadc { spinlock_t lock; struct completion completion; - int irq; }; enum xadc_type { -- cgit v1.2.3-70-g09d2 From 1ea3615b6168eaaf07445c8d32a4c6bc965d9579 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 17 Oct 2021 17:03:03 +0100 Subject: iio: accel: sca3000: Use sign_extend32() instead of opencoding sign extension. Whilst nice to get rid of this non obvious code, this also clears a static checker warning: drivers/iio/accel/sca3000.c:734 sca3000_read_raw() warn: no-op. '((*val) << 19) >> 19' Reported-by: Dan Carpenter Signed-off-by: Jonathan Cameron Reviewed-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211017160303.72441-1-jic23@kernel.org --- drivers/iio/accel/sca3000.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index cb753a43533c..c6b75308148a 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -731,8 +731,7 @@ static int sca3000_read_raw(struct iio_dev *indio_dev, return ret; } *val = (be16_to_cpup((__be16 *)st->rx) >> 3) & 0x1FFF; - *val = ((*val) << (sizeof(*val) * 8 - 13)) >> - (sizeof(*val) * 8 - 13); + *val = sign_extend32(*val, 12); } else { /* get the temperature when available */ ret = sca3000_read_data_short(st, -- cgit v1.2.3-70-g09d2 From 664b9879f56ef97d2ac98ec66e846814669b7e4a Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Thu, 14 Oct 2021 15:12:22 +0200 Subject: dt-bindings: iio: stm32-adc: add generic channel binding Add ADC generic channel binding. This binding should be used as an alternate to legacy channel properties whenever possible. ADC generic channel binding allows to identify supported internal channels through the following reserved label names: "vddcore", "vrefint" and "vbat". This binding also allows to set a different sampling time for each channel. Signed-off-by: Olivier Moysan Reviewed-by: Fabrice Gasnier Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211014131228.4692-2-olivier.moysan@foss.st.com Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/st,stm32-adc.yaml | 100 +++++++++++++++++++-- 1 file changed, 93 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml index a58334c3bb76..2083e1723970 100644 --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml @@ -222,6 +222,12 @@ patternProperties: '#io-channel-cells': const: 1 + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + interrupts: description: | IRQ Line for the ADC instance. Valid values are: @@ -256,6 +262,7 @@ patternProperties: - 20 channels, numbered from 0 to 19 (for in0..in19) on stm32h7 and stm32mp1. $ref: /schemas/types.yaml#/definitions/uint32-array + deprecated: true st,adc-diff-channels: description: | @@ -265,7 +272,9 @@ patternProperties: , ,... vinp and vinn are numbered from 0 to 19. Note: At least one of "st,adc-channels" or "st,adc-diff-channels" is - required. Both properties can be used together. Some channels can be + required if no adc generic channel is defined. These legacy channel + properties are exclusive with adc generic channel bindings. + Both properties can be used together. Some channels can be used as single-ended and some other ones as differential (mixed). But channels can't be configured both as single-ended and differential. $ref: /schemas/types.yaml#/definitions/uint32-matrix @@ -279,6 +288,7 @@ patternProperties: "vinn" indicates negative input number minimum: 0 maximum: 19 + deprecated: true st,min-sample-time-nsecs: description: @@ -289,6 +299,42 @@ patternProperties: list, to set sample time resp. for all channels, or independently for each channel. $ref: /schemas/types.yaml#/definitions/uint32-array + deprecated: true + + patternProperties: + "^channel@([0-9]|1[0-9])$": + type: object + $ref: "adc.yaml" + description: Represents the external channels which are connected to the ADC. + + properties: + reg: + items: + minimum: 0 + maximum: 19 + + label: + description: | + Unique name to identify which channel this is. + Reserved label names "vddcore", "vrefint" and "vbat" + are used to identify internal channels with matching names. + + diff-channels: + $ref: /schemas/types.yaml#/definitions/uint32-array + items: + minimum: 0 + maximum: 19 + + st,min-sample-time-ns: + description: | + Minimum sampling time in nanoseconds. Depending on hardware (board) + e.g. high/low analog input source impedance, fine tune of ADC + sampling time may be recommended. + + required: + - reg + + additionalProperties: false allOf: - if: @@ -369,12 +415,6 @@ patternProperties: additionalProperties: false - anyOf: - - required: - - st,adc-channels - - required: - - st,adc-diff-channels - required: - compatible - reg @@ -451,4 +491,50 @@ examples: // other adc child node follow... }; + - | + // Example 3: with stm32mp157c to setup ADC2 with: + // - internal channels 13, 14, 15. + #include + #include + adc122: adc@48003000 { + compatible = "st,stm32mp1-adc-core"; + reg = <0x48003000 0x400>; + interrupts = , + ; + clocks = <&rcc ADC12>, <&rcc ADC12_K>; + clock-names = "bus", "adc"; + booster-supply = <&booster>; + vdd-supply = <&vdd>; + vdda-supply = <&vdda>; + vref-supply = <&vref>; + st,syscfg = <&syscfg>; + interrupt-controller; + #interrupt-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + adc@100 { + compatible = "st,stm32mp1-adc"; + #io-channel-cells = <1>; + reg = <0x100>; + interrupts = <1>; + #address-cells = <1>; + #size-cells = <0>; + channel@13 { + reg = <13>; + label = "vrefint"; + st,min-sample-time-ns = <9000>; + }; + channel@14 { + reg = <14>; + label = "vddcore"; + st,min-sample-time-ns = <9000>; + }; + channel@15 { + reg = <15>; + label = "vbat"; + st,min-sample-time-ns = <9000>; + }; + }; + }; + ... -- cgit v1.2.3-70-g09d2 From 6cd4ed8eb89348fa5cf0b87bb420fe39531c9b59 Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Thu, 14 Oct 2021 15:12:23 +0200 Subject: dt-bindings: iio: stm32-adc: add nvmem support for vrefint internal channel Add support of nvmem. This allows to retrieve calibration data from OTP for vrefint internal channel. Signed-off-by: Olivier Moysan Reviewed-by: Fabrice Gasnier Acked-by: Rob Herring Link: https://lore.kernel.org/r/20211014131228.4692-3-olivier.moysan@foss.st.com Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml index 2083e1723970..ec0450d111a9 100644 --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml @@ -301,6 +301,14 @@ patternProperties: $ref: /schemas/types.yaml#/definitions/uint32-array deprecated: true + nvmem-cells: + items: + - description: Phandle to the calibration vrefint data provided by otp + + nvmem-cell-names: + items: + - const: vrefint + patternProperties: "^channel@([0-9]|1[0-9])$": type: object -- cgit v1.2.3-70-g09d2 From 45571a361c09fe89ebdefe533b3800d89d5934e3 Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Thu, 14 Oct 2021 15:12:24 +0200 Subject: iio: adc: stm32-adc: split channel init into several routines Split stm32_adc_chan_of_init channel initialization function into several routines to increase readability and prepare channel generic binding handling. Signed-off-by: Olivier Moysan Reviewed-by: Fabrice Gasnier Link: https://lore.kernel.org/r/20211014131228.4692-4-olivier.moysan@foss.st.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc.c | 131 +++++++++++++++++++++++++++++--------------- 1 file changed, 87 insertions(+), 44 deletions(-) diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 5088de835bb1..2f137d14f141 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -194,6 +194,7 @@ struct stm32_adc_cfg { * @smpr_val: sampling time settings (e.g. smpr1 / smpr2) * @cal: optional calibration data on some devices * @chan_name: channel name array + * @num_diff: number of differential channels */ struct stm32_adc { struct stm32_adc_common *common; @@ -217,6 +218,7 @@ struct stm32_adc { u32 smpr_val[2]; struct stm32_adc_calib cal; char chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ]; + u32 num_diff; }; struct stm32_adc_diff_channel { @@ -1706,17 +1708,11 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev, } } -static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) +static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm32_adc *adc) { struct device_node *node = indio_dev->dev.of_node; - struct stm32_adc *adc = iio_priv(indio_dev); const struct stm32_adc_info *adc_info = adc->cfg->adc_info; - struct stm32_adc_diff_channel diff[STM32_ADC_CH_MAX]; - struct property *prop; - const __be32 *cur; - struct iio_chan_spec *channels; - int scan_index = 0, num_channels = 0, num_diff = 0, ret, i; - u32 val, smp = 0; + int num_channels = 0, ret; ret = of_property_count_u32_elems(node, "st,adc-channels"); if (ret > adc_info->max_channels) { @@ -1727,41 +1723,54 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) } ret = of_property_count_elems_of_size(node, "st,adc-diff-channels", - sizeof(*diff)); + sizeof(struct stm32_adc_diff_channel)); if (ret > adc_info->max_channels) { dev_err(&indio_dev->dev, "Bad st,adc-diff-channels?\n"); return -EINVAL; } else if (ret > 0) { - int size = ret * sizeof(*diff) / sizeof(u32); - - num_diff = ret; + adc->num_diff = ret; num_channels += ret; + } + + return num_channels; +} + +static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev, + struct stm32_adc *adc, + struct iio_chan_spec *channels) +{ + struct device_node *node = indio_dev->dev.of_node; + const struct stm32_adc_info *adc_info = adc->cfg->adc_info; + struct stm32_adc_diff_channel diff[STM32_ADC_CH_MAX]; + u32 num_diff = adc->num_diff; + int size = num_diff * sizeof(*diff) / sizeof(u32); + int scan_index = 0, val, ret, i; + struct property *prop; + const __be32 *cur; + + if (num_diff) { ret = of_property_read_u32_array(node, "st,adc-diff-channels", (u32 *)diff, size); - if (ret) + if (ret) { + dev_err(&indio_dev->dev, "Failed to get diff channels %d\n", ret); return ret; - } + } - if (!num_channels) { - dev_err(&indio_dev->dev, "No channels configured\n"); - return -ENODATA; - } + for (i = 0; i < num_diff; i++) { + if (diff[i].vinp >= adc_info->max_channels || + diff[i].vinn >= adc_info->max_channels) { + dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n", + diff[i].vinp, diff[i].vinn); + return -EINVAL; + } - /* Optional sample time is provided either for each, or all channels */ - ret = of_property_count_u32_elems(node, "st,min-sample-time-nsecs"); - if (ret > 1 && ret != num_channels) { - dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n"); - return -EINVAL; + stm32_adc_chan_init_one(indio_dev, &channels[scan_index], + diff[i].vinp, diff[i].vinn, + scan_index, true); + scan_index++; + } } - if (timestamping) - num_channels++; - - channels = devm_kcalloc(&indio_dev->dev, num_channels, - sizeof(struct iio_chan_spec), GFP_KERNEL); - if (!channels) - return -ENOMEM; - of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) { if (val >= adc_info->max_channels) { dev_err(&indio_dev->dev, "Invalid channel %d\n", val); @@ -1771,8 +1780,7 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) /* Channel can't be configured both as single-ended & diff */ for (i = 0; i < num_diff; i++) { if (val == diff[i].vinp) { - dev_err(&indio_dev->dev, - "channel %d miss-configured\n", val); + dev_err(&indio_dev->dev, "channel %d misconfigured\n", val); return -EINVAL; } } @@ -1781,19 +1789,54 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) scan_index++; } - for (i = 0; i < num_diff; i++) { - if (diff[i].vinp >= adc_info->max_channels || - diff[i].vinn >= adc_info->max_channels) { - dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n", - diff[i].vinp, diff[i].vinn); - return -EINVAL; - } - stm32_adc_chan_init_one(indio_dev, &channels[scan_index], - diff[i].vinp, diff[i].vinn, scan_index, - true); - scan_index++; + return scan_index; +} + +static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) +{ + struct device_node *node = indio_dev->dev.of_node; + struct stm32_adc *adc = iio_priv(indio_dev); + const struct stm32_adc_info *adc_info = adc->cfg->adc_info; + struct iio_chan_spec *channels; + int scan_index = 0, num_channels = 0, ret, i; + u32 smp = 0; + + ret = stm32_adc_get_legacy_chan_count(indio_dev, adc); + if (ret < 0) + return ret; + num_channels = ret; + + if (!num_channels) { + dev_err(&indio_dev->dev, "No channels configured\n"); + return -ENODATA; } + if (num_channels > adc_info->max_channels) { + dev_err(&indio_dev->dev, "Channel number [%d] exceeds %d\n", + num_channels, adc_info->max_channels); + return -EINVAL; + } + + /* Optional sample time is provided either for each, or all channels */ + ret = of_property_count_u32_elems(node, "st,min-sample-time-nsecs"); + if (ret > 1 && ret != num_channels) { + dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n"); + return -EINVAL; + } + + if (timestamping) + num_channels++; + + channels = devm_kcalloc(&indio_dev->dev, num_channels, + sizeof(struct iio_chan_spec), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels); + if (ret < 0) + return ret; + scan_index = ret; + for (i = 0; i < scan_index; i++) { /* * Using of_property_read_u32_index(), smp value will only be -- cgit v1.2.3-70-g09d2 From 95bc818404b2c1a61611365ecf434a52f01ee97d Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Thu, 14 Oct 2021 15:12:25 +0200 Subject: iio: adc: stm32-adc: add support of generic channels binding Add support of generic IIO channel binding: ./devicetree/bindings/iio/adc/adc.yaml Keep support of st,adc-channels and st,adc-diff-channels for backward compatibility. Signed-off-by: Olivier Moysan Reviewed-by: Fabrice Gasnier Link: https://lore.kernel.org/r/20211014131228.4692-5-olivier.moysan@foss.st.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc.c | 95 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 86 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 2f137d14f141..85d09cbd41ae 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -35,7 +35,7 @@ #define STM32H7_BOOST_CLKRATE 20000000UL #define STM32_ADC_CH_MAX 20 /* max number of channels */ -#define STM32_ADC_CH_SZ 10 /* max channel name size */ +#define STM32_ADC_CH_SZ 16 /* max channel name size */ #define STM32_ADC_MAX_SQ 16 /* SQ1..SQ16 */ #define STM32_ADC_MAX_SMP 7 /* SMPx range is [0..7] */ #define STM32_ADC_TIMEOUT_US 100000 @@ -1792,6 +1792,73 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev, return scan_index; } +static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev, + struct stm32_adc *adc, + struct iio_chan_spec *channels) +{ + struct device_node *node = indio_dev->dev.of_node; + const struct stm32_adc_info *adc_info = adc->cfg->adc_info; + struct device_node *child; + const char *name; + int val, scan_index = 0, ret; + bool differential; + u32 vin[2]; + + for_each_available_child_of_node(node, child) { + ret = of_property_read_u32(child, "reg", &val); + if (ret) { + dev_err(&indio_dev->dev, "Missing channel index %d\n", ret); + goto err; + } + + ret = of_property_read_string(child, "label", &name); + /* label is optional */ + if (!ret) { + if (strlen(name) >= STM32_ADC_CH_SZ) { + dev_err(&indio_dev->dev, "Label %s exceeds %d characters\n", + name, STM32_ADC_CH_SZ); + return -EINVAL; + } + strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ); + } else if (ret != -EINVAL) { + dev_err(&indio_dev->dev, "Invalid label %d\n", ret); + goto err; + } + + if (val >= adc_info->max_channels) { + dev_err(&indio_dev->dev, "Invalid channel %d\n", val); + ret = -EINVAL; + goto err; + } + + differential = false; + ret = of_property_read_u32_array(child, "diff-channels", vin, 2); + /* diff-channels is optional */ + if (!ret) { + differential = true; + if (vin[0] != val || vin[1] >= adc_info->max_channels) { + dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n", + vin[0], vin[1]); + goto err; + } + } else if (ret != -EINVAL) { + dev_err(&indio_dev->dev, "Invalid diff-channels property %d\n", ret); + goto err; + } + + stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val, + vin[1], scan_index, differential); + scan_index++; + } + + return scan_index; + +err: + of_node_put(child); + + return ret; +} + static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) { struct device_node *node = indio_dev->dev.of_node; @@ -1800,15 +1867,22 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) struct iio_chan_spec *channels; int scan_index = 0, num_channels = 0, ret, i; u32 smp = 0; + bool legacy = false; - ret = stm32_adc_get_legacy_chan_count(indio_dev, adc); - if (ret < 0) - return ret; - num_channels = ret; - + num_channels = of_get_available_child_count(node); + /* If no channels have been found, fallback to channels legacy properties. */ if (!num_channels) { - dev_err(&indio_dev->dev, "No channels configured\n"); - return -ENODATA; + legacy = true; + + ret = stm32_adc_get_legacy_chan_count(indio_dev, adc); + if (!ret) { + dev_err(indio_dev->dev.parent, "No channel found\n"); + return -ENODATA; + } else if (ret < 0) { + return ret; + } + + num_channels = ret; } if (num_channels > adc_info->max_channels) { @@ -1832,7 +1906,10 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) if (!channels) return -ENOMEM; - ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels); + if (legacy) + ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels); + else + ret = stm32_adc_generic_chan_init(indio_dev, adc, channels); if (ret < 0) return ret; scan_index = ret; -- cgit v1.2.3-70-g09d2 From aec6e0d8f0fe2e99c6486ebf1979fe2a03890e39 Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Thu, 14 Oct 2021 15:12:26 +0200 Subject: iio: adc: stm32-adc: add support of internal channels Add support of ADC2 internal channels VDDCORE, VREFINT and VBAT. The reserved label name "vddcore", "vrefint" and "vbat" must be used in Device Tree channel node, to enable the corresponding internal channel. Note: This patch does not provide support of internal channels for F4 and H7. Signed-off-by: Olivier Moysan Reviewed-by: Fabrice Gasnier Link: https://lore.kernel.org/r/20211014131228.4692-6-olivier.moysan@foss.st.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc-core.c | 1 + drivers/iio/adc/stm32-adc-core.h | 10 +++ drivers/iio/adc/stm32-adc.c | 138 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 146 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index c088cb990193..b6e18eb101f7 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -659,6 +659,7 @@ static int stm32_adc_probe(struct platform_device *pdev) priv->cfg = (const struct stm32_adc_priv_cfg *) of_match_device(dev->driver->of_match_table, dev)->data; + spin_lock_init(&priv->common.lock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->common.base = devm_ioremap_resource(&pdev->dev, res); diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h index 2322809bfd2f..faedf7a49555 100644 --- a/drivers/iio/adc/stm32-adc-core.h +++ b/drivers/iio/adc/stm32-adc-core.h @@ -102,6 +102,9 @@ #define STM32H7_ADC_CALFACT 0xC4 #define STM32H7_ADC_CALFACT2 0xC8 +/* STM32MP1 - ADC2 instance option register */ +#define STM32MP1_ADC2_OR 0xD0 + /* STM32H7 - common registers for all ADC instances */ #define STM32H7_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00) #define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08) @@ -168,23 +171,30 @@ enum stm32h7_adc_dmngt { #define STM32H7_EOC_MST BIT(2) /* STM32H7_ADC_CCR - bit fields */ +#define STM32H7_VBATEN BIT(24) +#define STM32H7_VREFEN BIT(22) #define STM32H7_PRESC_SHIFT 18 #define STM32H7_PRESC_MASK GENMASK(21, 18) #define STM32H7_CKMODE_SHIFT 16 #define STM32H7_CKMODE_MASK GENMASK(17, 16) +/* STM32MP1_ADC2_OR - bit fields */ +#define STM32MP1_VDDCOREEN BIT(0) + /** * struct stm32_adc_common - stm32 ADC driver common data (for all instances) * @base: control registers base cpu addr * @phys_base: control registers base physical addr * @rate: clock rate used for analog circuitry * @vref_mv: vref voltage (mv) + * @lock: spinlock */ struct stm32_adc_common { void __iomem *base; phys_addr_t phys_base; unsigned long rate; int vref_mv; + spinlock_t lock; /* lock for common register */ }; #endif diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 85d09cbd41ae..943ca03f4d31 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -77,6 +77,30 @@ enum stm32_adc_extsel { STM32_EXT20, }; +enum stm32_adc_int_ch { + STM32_ADC_INT_CH_NONE = -1, + STM32_ADC_INT_CH_VDDCORE, + STM32_ADC_INT_CH_VREFINT, + STM32_ADC_INT_CH_VBAT, + STM32_ADC_INT_CH_NB, +}; + +/** + * struct stm32_adc_ic - ADC internal channels + * @name: name of the internal channel + * @idx: internal channel enum index + */ +struct stm32_adc_ic { + const char *name; + u32 idx; +}; + +static const struct stm32_adc_ic stm32_adc_ic[STM32_ADC_INT_CH_NB] = { + { "vddcore", STM32_ADC_INT_CH_VDDCORE }, + { "vrefint", STM32_ADC_INT_CH_VREFINT }, + { "vbat", STM32_ADC_INT_CH_VBAT }, +}; + /** * struct stm32_adc_trig_info - ADC trigger info * @name: name of the trigger, corresponding to its source @@ -126,6 +150,9 @@ struct stm32_adc_regs { * @res: resolution selection register & bitfield * @smpr: smpr1 & smpr2 registers offset array * @smp_bits: smpr1 & smpr2 index and bitfields + * @or_vdd: option register & vddcore bitfield + * @ccr_vbat: common register & vbat bitfield + * @ccr_vref: common register & vrefint bitfield */ struct stm32_adc_regspec { const u32 dr; @@ -139,6 +166,9 @@ struct stm32_adc_regspec { const struct stm32_adc_regs res; const u32 smpr[2]; const struct stm32_adc_regs *smp_bits; + const struct stm32_adc_regs or_vdd; + const struct stm32_adc_regs ccr_vbat; + const struct stm32_adc_regs ccr_vref; }; struct stm32_adc; @@ -195,6 +225,7 @@ struct stm32_adc_cfg { * @cal: optional calibration data on some devices * @chan_name: channel name array * @num_diff: number of differential channels + * @int_ch: internal channel indexes array */ struct stm32_adc { struct stm32_adc_common *common; @@ -219,6 +250,7 @@ struct stm32_adc { struct stm32_adc_calib cal; char chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ]; u32 num_diff; + int int_ch[STM32_ADC_INT_CH_NB]; }; struct stm32_adc_diff_channel { @@ -451,6 +483,24 @@ static const struct stm32_adc_regspec stm32h7_adc_regspec = { .smp_bits = stm32h7_smp_bits, }; +static const struct stm32_adc_regspec stm32mp1_adc_regspec = { + .dr = STM32H7_ADC_DR, + .ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE }, + .ier_ovr = { STM32H7_ADC_IER, STM32H7_OVRIE }, + .isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC }, + .isr_ovr = { STM32H7_ADC_ISR, STM32H7_OVR }, + .sqr = stm32h7_sq, + .exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT }, + .extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK, + STM32H7_EXTSEL_SHIFT }, + .res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT }, + .smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 }, + .smp_bits = stm32h7_smp_bits, + .or_vdd = { STM32MP1_ADC2_OR, STM32MP1_VDDCOREEN }, + .ccr_vbat = { STM32H7_ADC_CCR, STM32H7_VBATEN }, + .ccr_vref = { STM32H7_ADC_CCR, STM32H7_VREFEN }, +}; + /* * STM32 ADC registers access routines * @adc: stm32 adc instance @@ -489,6 +539,14 @@ static void stm32_adc_set_bits(struct stm32_adc *adc, u32 reg, u32 bits) spin_unlock_irqrestore(&adc->lock, flags); } +static void stm32_adc_set_bits_common(struct stm32_adc *adc, u32 reg, u32 bits) +{ + spin_lock(&adc->common->lock); + writel_relaxed(readl_relaxed(adc->common->base + reg) | bits, + adc->common->base + reg); + spin_unlock(&adc->common->lock); +} + static void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits) { unsigned long flags; @@ -498,6 +556,14 @@ static void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits) spin_unlock_irqrestore(&adc->lock, flags); } +static void stm32_adc_clr_bits_common(struct stm32_adc *adc, u32 reg, u32 bits) +{ + spin_lock(&adc->common->lock); + writel_relaxed(readl_relaxed(adc->common->base + reg) & ~bits, + adc->common->base + reg); + spin_unlock(&adc->common->lock); +} + /** * stm32_adc_conv_irq_enable() - Enable end of conversion interrupt * @adc: stm32 adc instance @@ -579,6 +645,60 @@ err_clk_dis: return ret; } +static void stm32_adc_int_ch_enable(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + u32 i; + + for (i = 0; i < STM32_ADC_INT_CH_NB; i++) { + if (adc->int_ch[i] == STM32_ADC_INT_CH_NONE) + continue; + + switch (i) { + case STM32_ADC_INT_CH_VDDCORE: + dev_dbg(&indio_dev->dev, "Enable VDDCore\n"); + stm32_adc_set_bits(adc, adc->cfg->regs->or_vdd.reg, + adc->cfg->regs->or_vdd.mask); + break; + case STM32_ADC_INT_CH_VREFINT: + dev_dbg(&indio_dev->dev, "Enable VREFInt\n"); + stm32_adc_set_bits_common(adc, adc->cfg->regs->ccr_vref.reg, + adc->cfg->regs->ccr_vref.mask); + break; + case STM32_ADC_INT_CH_VBAT: + dev_dbg(&indio_dev->dev, "Enable VBAT\n"); + stm32_adc_set_bits_common(adc, adc->cfg->regs->ccr_vbat.reg, + adc->cfg->regs->ccr_vbat.mask); + break; + } + } +} + +static void stm32_adc_int_ch_disable(struct stm32_adc *adc) +{ + u32 i; + + for (i = 0; i < STM32_ADC_INT_CH_NB; i++) { + if (adc->int_ch[i] == STM32_ADC_INT_CH_NONE) + continue; + + switch (i) { + case STM32_ADC_INT_CH_VDDCORE: + stm32_adc_clr_bits(adc, adc->cfg->regs->or_vdd.reg, + adc->cfg->regs->or_vdd.mask); + break; + case STM32_ADC_INT_CH_VREFINT: + stm32_adc_clr_bits_common(adc, adc->cfg->regs->ccr_vref.reg, + adc->cfg->regs->ccr_vref.mask); + break; + case STM32_ADC_INT_CH_VBAT: + stm32_adc_clr_bits_common(adc, adc->cfg->regs->ccr_vbat.reg, + adc->cfg->regs->ccr_vbat.mask); + break; + } + } +} + /** * stm32f4_adc_start_conv() - Start conversions for regular channels. * @indio_dev: IIO device instance @@ -947,11 +1067,13 @@ static int stm32h7_adc_prepare(struct iio_dev *indio_dev) goto pwr_dwn; calib = ret; + stm32_adc_int_ch_enable(indio_dev); + stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel); ret = stm32h7_adc_enable(indio_dev); if (ret) - goto pwr_dwn; + goto ch_disable; /* Either restore or read calibration result for future reference */ if (calib) @@ -967,6 +1089,8 @@ static int stm32h7_adc_prepare(struct iio_dev *indio_dev) disable: stm32h7_adc_disable(indio_dev); +ch_disable: + stm32_adc_int_ch_disable(adc); pwr_dwn: stm32h7_adc_enter_pwr_down(adc); @@ -978,6 +1102,7 @@ static void stm32h7_adc_unprepare(struct iio_dev *indio_dev) struct stm32_adc *adc = iio_priv(indio_dev); stm32h7_adc_disable(indio_dev); + stm32_adc_int_ch_disable(adc); stm32h7_adc_enter_pwr_down(adc); } @@ -1800,7 +1925,7 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev, const struct stm32_adc_info *adc_info = adc->cfg->adc_info; struct device_node *child; const char *name; - int val, scan_index = 0, ret; + int val, scan_index = 0, ret, i; bool differential; u32 vin[2]; @@ -1820,6 +1945,10 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev, return -EINVAL; } strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ); + for (i = 0; i < STM32_ADC_INT_CH_NB; i++) { + if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ)) + adc->int_ch[i] = val; + } } else if (ret != -EINVAL) { dev_err(&indio_dev->dev, "Invalid label %d\n", ret); goto err; @@ -1869,6 +1998,9 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) u32 smp = 0; bool legacy = false; + for (i = 0; i < STM32_ADC_INT_CH_NB; i++) + adc->int_ch[i] = STM32_ADC_INT_CH_NONE; + num_channels = of_get_available_child_count(node); /* If no channels have been found, fallback to channels legacy properties. */ if (!num_channels) { @@ -2219,7 +2351,7 @@ static const struct stm32_adc_cfg stm32h7_adc_cfg = { }; static const struct stm32_adc_cfg stm32mp1_adc_cfg = { - .regs = &stm32h7_adc_regspec, + .regs = &stm32mp1_adc_regspec, .adc_info = &stm32h7_adc_info, .trigs = stm32h7_adc_trigs, .has_vregready = true, -- cgit v1.2.3-70-g09d2 From 0e346b2cfa855d6e6990dbdb220983fbf2bb0b92 Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Thu, 14 Oct 2021 15:12:27 +0200 Subject: iio: adc: stm32-adc: add vrefint calibration support Add support of vrefint calibration. If a channel is labeled as vrefint, get vrefint calibration from non volatile memory for this channel. vrefint channel is exposed as a processed channel returning the actual value of vrefp: vrefp = 3.3 x vrefint_cal / vrefint_data A conversion on vrefint channel allows to update scale factor according to vrefint deviation, compared to vrefint calibration value. Signed-off-by: Olivier Moysan Reviewed-by: Fabrice Gasnier Link: https://lore.kernel.org/r/20211014131228.4692-7-olivier.moysan@foss.st.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc.c | 74 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 943ca03f4d31..5269bb87d094 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ #define STM32_ADC_TIMEOUT_US 100000 #define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000)) #define STM32_ADC_HW_STOP_DELAY_MS 100 +#define STM32_ADC_VREFINT_VOLTAGE 3300 #define STM32_DMA_BUFFER_SIZE PAGE_SIZE @@ -137,6 +139,16 @@ struct stm32_adc_regs { int shift; }; +/** + * struct stm32_adc_vrefint - stm32 ADC internal reference voltage data + * @vrefint_cal: vrefint calibration value from nvmem + * @vrefint_data: vrefint actual value + */ +struct stm32_adc_vrefint { + u32 vrefint_cal; + u32 vrefint_data; +}; + /** * struct stm32_adc_regspec - stm32 registers definition * @dr: data register offset @@ -186,6 +198,7 @@ struct stm32_adc; * @unprepare: optional unprepare routine (disable, power-down) * @irq_clear: routine to clear irqs * @smp_cycles: programmable sampling time (ADC clock cycles) + * @ts_vrefint_ns: vrefint minimum sampling time in ns */ struct stm32_adc_cfg { const struct stm32_adc_regspec *regs; @@ -199,6 +212,7 @@ struct stm32_adc_cfg { void (*unprepare)(struct iio_dev *); void (*irq_clear)(struct iio_dev *indio_dev, u32 msk); const unsigned int *smp_cycles; + const unsigned int ts_vrefint_ns; }; /** @@ -223,6 +237,7 @@ struct stm32_adc_cfg { * @pcsel: bitmask to preselect channels on some devices * @smpr_val: sampling time settings (e.g. smpr1 / smpr2) * @cal: optional calibration data on some devices + * @vrefint: internal reference voltage data * @chan_name: channel name array * @num_diff: number of differential channels * @int_ch: internal channel indexes array @@ -248,6 +263,7 @@ struct stm32_adc { u32 pcsel; u32 smpr_val[2]; struct stm32_adc_calib cal; + struct stm32_adc_vrefint vrefint; char chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ]; u32 num_diff; int int_ch[STM32_ADC_INT_CH_NB]; @@ -1339,6 +1355,7 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_PROCESSED: ret = iio_device_claim_direct_mode(indio_dev); if (ret) return ret; @@ -1346,6 +1363,10 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev, ret = stm32_adc_single_conv(indio_dev, chan, val); else ret = -EINVAL; + + if (mask == IIO_CHAN_INFO_PROCESSED && adc->vrefint.vrefint_cal) + *val = STM32_ADC_VREFINT_VOLTAGE * adc->vrefint.vrefint_cal / *val; + iio_device_release_direct_mode(indio_dev); return ret; @@ -1815,7 +1836,10 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev, chan->datasheet_name = name; chan->scan_index = scan_index; chan->indexed = 1; - chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + if (chan->channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT]) + chan->info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED); + else + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET); chan->scan_type.sign = 'u'; @@ -1917,6 +1941,36 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev, return scan_index; } +static int stm32_adc_populate_int_ch(struct iio_dev *indio_dev, const char *ch_name, + int chan) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + u16 vrefint; + int i, ret; + + for (i = 0; i < STM32_ADC_INT_CH_NB; i++) { + if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) { + adc->int_ch[i] = chan; + + if (stm32_adc_ic[i].idx != STM32_ADC_INT_CH_VREFINT) + continue; + + /* Get calibration data for vrefint channel */ + ret = nvmem_cell_read_u16(&indio_dev->dev, "vrefint", &vrefint); + if (ret && ret != -ENOENT) { + return dev_err_probe(&indio_dev->dev, ret, + "nvmem access error\n"); + } + if (ret == -ENOENT) + dev_dbg(&indio_dev->dev, "vrefint calibration not found\n"); + else + adc->vrefint.vrefint_cal = vrefint; + } + } + + return 0; +} + static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev, struct stm32_adc *adc, struct iio_chan_spec *channels) @@ -1925,7 +1979,7 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev, const struct stm32_adc_info *adc_info = adc->cfg->adc_info; struct device_node *child; const char *name; - int val, scan_index = 0, ret, i; + int val, scan_index = 0, ret; bool differential; u32 vin[2]; @@ -1945,10 +1999,9 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev, return -EINVAL; } strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ); - for (i = 0; i < STM32_ADC_INT_CH_NB; i++) { - if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ)) - adc->int_ch[i] = val; - } + ret = stm32_adc_populate_int_ch(indio_dev, name, val); + if (ret) + goto err; } else if (ret != -EINVAL) { dev_err(&indio_dev->dev, "Invalid label %d\n", ret); goto err; @@ -2055,6 +2108,14 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) */ of_property_read_u32_index(node, "st,min-sample-time-nsecs", i, &smp); + + /* + * For vrefint channel, ensure that the sampling time cannot + * be lower than the one specified in the datasheet + */ + if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT]) + smp = max(smp, adc->cfg->ts_vrefint_ns); + /* Prepare sampling time settings */ stm32_adc_smpr_init(adc, channels[i].channel, smp); } @@ -2361,6 +2422,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = { .unprepare = stm32h7_adc_unprepare, .smp_cycles = stm32h7_adc_smp_cycles, .irq_clear = stm32h7_adc_irq_clear, + .ts_vrefint_ns = 4300, }; static const struct of_device_id stm32_adc_of_match[] = { -- cgit v1.2.3-70-g09d2 From 796e5d0b1e9baa2c80c234efd54d561e6d1cf1e7 Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Thu, 14 Oct 2021 15:12:28 +0200 Subject: iio: adc: stm32-adc: use generic binding for sample-time Add st,min-sample-time-nsecs to channel generic binding. Sample time can be defined par channel node. If a channel is configured as differential, the same sample time applies for both inputs. Keep support of legacy st,min-sample-time-nsecs property for backward compatibility. Signed-off-by: Olivier Moysan Reviewed-by: Fabrice Gasnier Link: https://lore.kernel.org/r/20211014131228.4692-8-olivier.moysan@foss.st.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc.c | 70 ++++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 5269bb87d094..6245434f8377 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -1805,6 +1805,13 @@ static void stm32_adc_smpr_init(struct stm32_adc *adc, int channel, u32 smp_ns) u32 period_ns, shift = smpr->shift, mask = smpr->mask; unsigned int smp, r = smpr->reg; + /* + * For vrefint channel, ensure that the sampling time cannot + * be lower than the one specified in the datasheet + */ + if (channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT]) + smp_ns = max(smp_ns, adc->cfg->ts_vrefint_ns); + /* Determine sampling time (ADC clock cycles) */ period_ns = NSEC_PER_SEC / adc->common->rate; for (smp = 0; smp <= STM32_ADC_MAX_SMP; smp++) @@ -1881,6 +1888,13 @@ static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm num_channels += ret; } + /* Optional sample time is provided either for each, or all channels */ + ret = of_property_count_u32_elems(node, "st,min-sample-time-nsecs"); + if (ret > 1 && ret != num_channels) { + dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n"); + return -EINVAL; + } + return num_channels; } @@ -1896,6 +1910,7 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev, int scan_index = 0, val, ret, i; struct property *prop; const __be32 *cur; + u32 smp = 0; if (num_diff) { ret = of_property_read_u32_array(node, "st,adc-diff-channels", @@ -1938,6 +1953,19 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev, scan_index++; } + for (i = 0; i < scan_index; i++) { + /* + * Using of_property_read_u32_index(), smp value will only be + * modified if valid u32 value can be decoded. This allows to + * get either no value, 1 shared value for all indexes, or one + * value per channel. + */ + of_property_read_u32_index(node, "st,min-sample-time-nsecs", i, &smp); + + /* Prepare sampling time settings */ + stm32_adc_smpr_init(adc, channels[i].channel, smp); + } + return scan_index; } @@ -2030,6 +2058,19 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev, stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val, vin[1], scan_index, differential); + + ret = of_property_read_u32(child, "st,min-sample-time-ns", &val); + /* st,min-sample-time-ns is optional */ + if (!ret) { + stm32_adc_smpr_init(adc, channels[scan_index].channel, val); + if (differential) + stm32_adc_smpr_init(adc, vin[1], val); + } else if (ret != -EINVAL) { + dev_err(&indio_dev->dev, "Invalid st,min-sample-time-ns property %d\n", + ret); + goto err; + } + scan_index++; } @@ -2048,7 +2089,6 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) const struct stm32_adc_info *adc_info = adc->cfg->adc_info; struct iio_chan_spec *channels; int scan_index = 0, num_channels = 0, ret, i; - u32 smp = 0; bool legacy = false; for (i = 0; i < STM32_ADC_INT_CH_NB; i++) @@ -2076,13 +2116,6 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) return -EINVAL; } - /* Optional sample time is provided either for each, or all channels */ - ret = of_property_count_u32_elems(node, "st,min-sample-time-nsecs"); - if (ret > 1 && ret != num_channels) { - dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n"); - return -EINVAL; - } - if (timestamping) num_channels++; @@ -2099,27 +2132,6 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) return ret; scan_index = ret; - for (i = 0; i < scan_index; i++) { - /* - * Using of_property_read_u32_index(), smp value will only be - * modified if valid u32 value can be decoded. This allows to - * get either no value, 1 shared value for all indexes, or one - * value per channel. - */ - of_property_read_u32_index(node, "st,min-sample-time-nsecs", - i, &smp); - - /* - * For vrefint channel, ensure that the sampling time cannot - * be lower than the one specified in the datasheet - */ - if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT]) - smp = max(smp, adc->cfg->ts_vrefint_ns); - - /* Prepare sampling time settings */ - stm32_adc_smpr_init(adc, channels[i].channel, smp); - } - if (timestamping) { struct iio_chan_spec *timestamp = &channels[scan_index]; -- cgit v1.2.3-70-g09d2 From 4cbbe74d906be0bcffbe1e74b43a00f99626a69c Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 12 Oct 2021 11:15:21 +0100 Subject: soundwire: bus: stop dereferencing invalid slave pointer Slave pointer is invalid after end of list iteration, using this would result in below Memory abort. Unable to handle kernel NULL pointer dereference at virtual address 0000000000000004 ... Call trace: __dev_printk+0x34/0x7c _dev_warn+0x6c/0x90 sdw_bus_exit_clk_stop+0x194/0x1d0 swrm_runtime_resume+0x13c/0x238 pm_generic_runtime_resume+0x2c/0x48 __rpm_callback+0x44/0x150 rpm_callback+0x6c/0x78 rpm_resume+0x314/0x558 rpm_resume+0x378/0x558 rpm_resume+0x378/0x558 __pm_runtime_resume+0x3c/0x88 Use bus->dev instead to print this error message. Fixes: b50bb8ba369cd ("soundwire: bus: handle -ENODATA errors in clock stop/start sequences") Signed-off-by: Srinivas Kandagatla Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211012101521.32087-1-srinivas.kandagatla@linaro.org Signed-off-by: Vinod Koul --- drivers/soundwire/bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 1b115734a8f6..67369e941d0d 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1110,7 +1110,7 @@ int sdw_bus_exit_clk_stop(struct sdw_bus *bus) if (!simple_clk_stop) { ret = sdw_bus_wait_for_clk_prep_deprep(bus, SDW_BROADCAST_DEV_NUM); if (ret < 0) - dev_warn(&slave->dev, "clock stop deprepare wait failed:%d\n", ret); + dev_warn(bus->dev, "clock stop deprepare wait failed:%d\n", ret); } list_for_each_entry(slave, &bus->slaves, node) { -- cgit v1.2.3-70-g09d2 From abd9a6049bb59a9bab8cc8b42ccbe4a46c307f92 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 7 Sep 2021 11:56:36 +0100 Subject: soundwire: qcom: add debugfs entry for soundwire register dump For debug purposes add an entry in debugfs to dump Qualcomm SoundWire Controller registers. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20210907105636.3171-1-srinivas.kandagatla@linaro.org Signed-off-by: Vinod Koul --- drivers/soundwire/qcom.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 0ef79d60e88e..a317bea2d42d 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -83,6 +84,7 @@ #define SWRM_DP_PORT_HCTRL_BANK(n, m) (0x1134 + 0x100 * (n - 1) + 0x40 * m) #define SWRM_DP_BLOCK_CTRL3_BANK(n, m) (0x1138 + 0x100 * (n - 1) + 0x40 * m) #define SWRM_DIN_DPn_PCM_PORT_CTRL(n) (0x1054 + 0x100 * (n - 1)) +#define SWR_MSTR_MAX_REG_ADDR (0x1740) #define SWRM_DP_PORT_CTRL_EN_CHAN_SHFT 0x18 #define SWRM_DP_PORT_CTRL_OFFSET2_SHFT 0x10 @@ -127,6 +129,9 @@ struct qcom_swrm_ctrl { struct device *dev; struct regmap *regmap; void __iomem *mmio; +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs; +#endif struct completion broadcast; struct completion enumeration; struct work_struct slave_work; @@ -1192,6 +1197,22 @@ static int qcom_swrm_get_port_config(struct qcom_swrm_ctrl *ctrl) return 0; } +#ifdef CONFIG_DEBUG_FS +static int swrm_reg_show(struct seq_file *s_file, void *data) +{ + struct qcom_swrm_ctrl *swrm = s_file->private; + int reg, reg_val; + + for (reg = 0; reg <= SWR_MSTR_MAX_REG_ADDR; reg += 4) { + swrm->reg_read(swrm, reg, ®_val); + seq_printf(s_file, "0x%.3x: 0x%.2x\n", reg, reg_val); + } + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(swrm_reg); +#endif + static int qcom_swrm_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1302,6 +1323,12 @@ static int qcom_swrm_probe(struct platform_device *pdev) (ctrl->version >> 24) & 0xff, (ctrl->version >> 16) & 0xff, ctrl->version & 0xffff); +#ifdef CONFIG_DEBUG_FS + ctrl->debugfs = debugfs_create_dir("qualcomm-sdw", ctrl->bus.debugfs); + debugfs_create_file("qualcomm-registers", 0400, ctrl->debugfs, ctrl, + &swrm_reg_fops); +#endif + return 0; err_master_add: -- cgit v1.2.3-70-g09d2 From 486a25084155bf633768c26f022201c051d6fd95 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Mon, 18 Oct 2021 14:37:18 +0800 Subject: iio: buffer: Fix memory leak in iio_buffers_alloc_sysfs_and_mask() When 'iio_dev_opaque->buffer_ioctl_handler' alloc fails in iio_buffers_alloc_sysfs_and_mask(), the 'attrs' allocated in iio_buffer_register_legacy_sysfs_groups() will be leaked: unreferenced object 0xffff888108568d00 (size 128): comm "88", pid 2014, jiffies 4294963294 (age 26.920s) hex dump (first 32 bytes): 80 3e da 02 80 88 ff ff 00 3a da 02 80 88 ff ff .>.......:...... 00 35 da 02 80 88 ff ff 00 38 da 02 80 88 ff ff .5.......8...... backtrace: [<0000000095a9e51e>] __kmalloc+0x1a3/0x2f0 [<00000000faa3735e>] iio_buffers_alloc_sysfs_and_mask+0xfa3/0x1480 [industrialio] [<00000000a46384dc>] __iio_device_register+0x52e/0x1b40 [industrialio] [<00000000210af05e>] __devm_iio_device_register+0x22/0x80 [industrialio] [<00000000730d7b41>] adjd_s311_probe+0x195/0x200 [adjd_s311] [<00000000c0f70eb9>] i2c_device_probe+0xa07/0xbb0 The iio_buffer_register_legacy_sysfs_groups() is called in __iio_buffer_alloc_sysfs_and_mask(), so move the iio_buffer_unregister_legacy_sysfs_groups() into __iio_buffer_free_sysfs_and_mask(), then the memory will be freed. Reported-by: Hulk Robot Fixes: d9a625744ed0 ("iio: core: merge buffer/ & scan_elements/ attributes") Signed-off-by: Yang Yingliang Link: https://lore.kernel.org/r/20211018063718.1971240-1-yangyingliang@huawei.com Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 1c3972150ab4..2f98ba70e3d7 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -1587,8 +1587,12 @@ error_cleanup_dynamic: return ret; } -static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer) +static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer, + struct iio_dev *indio_dev, + int index) { + if (index == 0) + iio_buffer_unregister_legacy_sysfs_groups(indio_dev); bitmap_free(buffer->scan_mask); kfree(buffer->buffer_group.name); kfree(buffer->buffer_group.attrs); @@ -1642,7 +1646,7 @@ int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev) error_unwind_sysfs_and_mask: for (; unwind_idx >= 0; unwind_idx--) { buffer = iio_dev_opaque->attached_buffers[unwind_idx]; - __iio_buffer_free_sysfs_and_mask(buffer); + __iio_buffer_free_sysfs_and_mask(buffer, indio_dev, unwind_idx); } return ret; } @@ -1659,11 +1663,9 @@ void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev) iio_device_ioctl_handler_unregister(iio_dev_opaque->buffer_ioctl_handler); kfree(iio_dev_opaque->buffer_ioctl_handler); - iio_buffer_unregister_legacy_sysfs_groups(indio_dev); - for (i = iio_dev_opaque->attached_buffers_cnt - 1; i >= 0; i--) { buffer = iio_dev_opaque->attached_buffers[i]; - __iio_buffer_free_sysfs_and_mask(buffer); + __iio_buffer_free_sysfs_and_mask(buffer, indio_dev, i); } } -- cgit v1.2.3-70-g09d2 From 310e75c72fefa3b0b4535f669c8b37c963a2dba5 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Thu, 21 Oct 2021 19:35:14 +0900 Subject: counter: Cleanup lingering atomic.h includes With the removal of the chrdev_lock atomic flag, the counter-sysfs.c and counter-chrdev.c no longer needs to include the atomic.h header file. Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/20211021103514.955622-1-vilhelm.gray@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/counter/counter-chrdev.c | 1 - drivers/counter/counter-sysfs.c | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c index b747dc81cfc6..0c82613582f1 100644 --- a/drivers/counter/counter-chrdev.c +++ b/drivers/counter/counter-chrdev.c @@ -3,7 +3,6 @@ * Generic Counter character device interface * Copyright (C) 2020 William Breathitt Gray */ -#include #include #include #include diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c index 8c2d7c29ea59..67a988851657 100644 --- a/drivers/counter/counter-sysfs.c +++ b/drivers/counter/counter-sysfs.c @@ -3,7 +3,6 @@ * Generic Counter sysfs interface * Copyright (C) 2020 William Breathitt Gray */ -#include #include #include #include -- cgit v1.2.3-70-g09d2 From 8ac33b8b6841e99a624ace543d92cbf598a91381 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Thu, 21 Oct 2021 19:35:40 +0900 Subject: counter: Fix use-after-free race condition for events_queue_size write A race condition is possible when writing to events_queue_size where the events kfifo is freed during the execution of a kfifo_in(), resulting in a use-after-free. This patch prevents such a scenario by protecting the events queue in operation with a spinlock and locking before performing the events queue size adjustment. The existing events_lock mutex is renamed to events_out_lock to reflect that it only protects events queue out operations. Because the events queue in operations can occur in an interrupt context, a new events_in_lock spinlock is introduced and utilized. Fixes: feff17a550c7 ("counter: Implement events_queue_size sysfs attribute") Cc: David Lechner Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/20211021103540.955639-1-vilhelm.gray@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/counter/counter-chrdev.c | 10 ++++++---- drivers/counter/counter-sysfs.c | 7 +++++++ include/linux/counter.h | 6 ++++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c index 0c82613582f1..b7c62f957a6a 100644 --- a/drivers/counter/counter-chrdev.c +++ b/drivers/counter/counter-chrdev.c @@ -81,10 +81,10 @@ static ssize_t counter_chrdev_read(struct file *filp, char __user *buf, return -ENODEV; } - if (mutex_lock_interruptible(&counter->events_lock)) + if (mutex_lock_interruptible(&counter->events_out_lock)) return -ERESTARTSYS; err = kfifo_to_user(&counter->events, buf, len, &copied); - mutex_unlock(&counter->events_lock); + mutex_unlock(&counter->events_out_lock); if (err < 0) return err; } while (!copied); @@ -436,7 +436,8 @@ int counter_chrdev_add(struct counter_device *const counter) spin_lock_init(&counter->events_list_lock); mutex_init(&counter->n_events_list_lock); init_waitqueue_head(&counter->events_wait); - mutex_init(&counter->events_lock); + spin_lock_init(&counter->events_in_lock); + mutex_init(&counter->events_out_lock); /* Initialize character device */ cdev_init(&counter->chrdev, &counter_fops); @@ -559,7 +560,8 @@ void counter_push_event(struct counter_device *const counter, const u8 event, ev.watch.component = comp_node->component; ev.status = -counter_get_data(counter, comp_node, &ev.value); - copied += kfifo_in(&counter->events, &ev, 1); + copied += kfifo_in_spinlocked_noirqsave(&counter->events, &ev, + 1, &counter->events_in_lock); } exit_early: diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c index 67a988851657..7cc4d1d523ea 100644 --- a/drivers/counter/counter-sysfs.c +++ b/drivers/counter/counter-sysfs.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include #include @@ -796,6 +798,7 @@ static int counter_events_queue_size_write(struct counter_device *counter, { DECLARE_KFIFO_PTR(events, struct counter_event); int err; + unsigned long flags; /* Allocate new events queue */ err = kfifo_alloc(&events, val, GFP_KERNEL); @@ -803,8 +806,12 @@ static int counter_events_queue_size_write(struct counter_device *counter, return err; /* Swap in new events queue */ + mutex_lock(&counter->events_out_lock); + spin_lock_irqsave(&counter->events_in_lock, flags); kfifo_free(&counter->events); counter->events.kfifo = events.kfifo; + spin_unlock_irqrestore(&counter->events_in_lock, flags); + mutex_unlock(&counter->events_out_lock); return 0; } diff --git a/include/linux/counter.h b/include/linux/counter.h index 0fd99e255a50..b7d0a00a61cf 100644 --- a/include/linux/counter.h +++ b/include/linux/counter.h @@ -296,7 +296,8 @@ struct counter_ops { * @n_events_list_lock: lock to protect Counter next events list operations * @events: queue of detected Counter events * @events_wait: wait queue to allow blocking reads of Counter events - * @events_lock: lock to protect Counter events queue read operations + * @events_in_lock: lock to protect Counter events queue in operations + * @events_out_lock: lock to protect Counter events queue out operations * @ops_exist_lock: lock to prevent use during removal */ struct counter_device { @@ -323,7 +324,8 @@ struct counter_device { struct mutex n_events_list_lock; DECLARE_KFIFO_PTR(events, struct counter_event); wait_queue_head_t events_wait; - struct mutex events_lock; + spinlock_t events_in_lock; + struct mutex events_out_lock; struct mutex ops_exist_lock; }; -- cgit v1.2.3-70-g09d2 From 17b5b576ff5faff99a4c8140d521cd4d7fff5c16 Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Thu, 7 Oct 2021 15:46:39 +0200 Subject: mux: add support for delay after muxing Hardware may require some time for the muxed analog signals to settle after the muxing is changed. Allow users of the mux subsystem to specify this delay with the new mux_control_select_delay() function (and the _try equivalent). Signed-off-by: Vincent Whitchurch Reviewed-by: Lars-Peter Clausen Tested-by: Lars-Peter Clausen Acked-by: Peter Rosin Link: https://lore.kernel.org/r/20211007134641.13417-2-vincent.whitchurch@axis.com Signed-off-by: Jonathan Cameron --- drivers/mux/core.c | 38 ++++++++++++++++++++++++++++++++------ include/linux/mux/consumer.h | 23 +++++++++++++++++++---- include/linux/mux/driver.h | 4 ++++ 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/drivers/mux/core.c b/drivers/mux/core.c index 1fb22388e7e0..22f4709768d1 100644 --- a/drivers/mux/core.c +++ b/drivers/mux/core.c @@ -9,6 +9,7 @@ #define pr_fmt(fmt) "mux-core: " fmt +#include #include #include #include @@ -116,6 +117,7 @@ struct mux_chip *mux_chip_alloc(struct device *dev, sema_init(&mux->lock, 1); mux->cached_state = MUX_CACHE_UNKNOWN; mux->idle_state = MUX_IDLE_AS_IS; + mux->last_change = ktime_get(); } device_initialize(&mux_chip->dev); @@ -129,6 +131,8 @@ static int mux_control_set(struct mux_control *mux, int state) int ret = mux->chip->ops->set(mux, state); mux->cached_state = ret < 0 ? MUX_CACHE_UNKNOWN : state; + if (ret >= 0) + mux->last_change = ktime_get(); return ret; } @@ -314,10 +318,25 @@ static int __mux_control_select(struct mux_control *mux, int state) return ret; } +static void mux_control_delay(struct mux_control *mux, unsigned int delay_us) +{ + ktime_t delayend; + s64 remaining; + + if (!delay_us) + return; + + delayend = ktime_add_us(mux->last_change, delay_us); + remaining = ktime_us_delta(delayend, ktime_get()); + if (remaining > 0) + fsleep(remaining); +} + /** - * mux_control_select() - Select the given multiplexer state. + * mux_control_select_delay() - Select the given multiplexer state. * @mux: The mux-control to request a change of state from. * @state: The new requested state. + * @delay_us: The time to delay (in microseconds) if the mux state is changed. * * On successfully selecting the mux-control state, it will be locked until * there is a call to mux_control_deselect(). If the mux-control is already @@ -331,7 +350,8 @@ static int __mux_control_select(struct mux_control *mux, int state) * Return: 0 when the mux-control state has the requested state or a negative * errno on error. */ -int mux_control_select(struct mux_control *mux, unsigned int state) +int mux_control_select_delay(struct mux_control *mux, unsigned int state, + unsigned int delay_us) { int ret; @@ -340,18 +360,21 @@ int mux_control_select(struct mux_control *mux, unsigned int state) return ret; ret = __mux_control_select(mux, state); + if (ret >= 0) + mux_control_delay(mux, delay_us); if (ret < 0) up(&mux->lock); return ret; } -EXPORT_SYMBOL_GPL(mux_control_select); +EXPORT_SYMBOL_GPL(mux_control_select_delay); /** - * mux_control_try_select() - Try to select the given multiplexer state. + * mux_control_try_select_delay() - Try to select the given multiplexer state. * @mux: The mux-control to request a change of state from. * @state: The new requested state. + * @delay_us: The time to delay (in microseconds) if the mux state is changed. * * On successfully selecting the mux-control state, it will be locked until * mux_control_deselect() called. @@ -363,7 +386,8 @@ EXPORT_SYMBOL_GPL(mux_control_select); * Return: 0 when the mux-control state has the requested state or a negative * errno on error. Specifically -EBUSY if the mux-control is contended. */ -int mux_control_try_select(struct mux_control *mux, unsigned int state) +int mux_control_try_select_delay(struct mux_control *mux, unsigned int state, + unsigned int delay_us) { int ret; @@ -371,13 +395,15 @@ int mux_control_try_select(struct mux_control *mux, unsigned int state) return -EBUSY; ret = __mux_control_select(mux, state); + if (ret >= 0) + mux_control_delay(mux, delay_us); if (ret < 0) up(&mux->lock); return ret; } -EXPORT_SYMBOL_GPL(mux_control_try_select); +EXPORT_SYMBOL_GPL(mux_control_try_select_delay); /** * mux_control_deselect() - Deselect the previously selected multiplexer state. diff --git a/include/linux/mux/consumer.h b/include/linux/mux/consumer.h index 5fc6bb2fefad..7a09b040ac39 100644 --- a/include/linux/mux/consumer.h +++ b/include/linux/mux/consumer.h @@ -16,10 +16,25 @@ struct device; struct mux_control; unsigned int mux_control_states(struct mux_control *mux); -int __must_check mux_control_select(struct mux_control *mux, - unsigned int state); -int __must_check mux_control_try_select(struct mux_control *mux, - unsigned int state); +int __must_check mux_control_select_delay(struct mux_control *mux, + unsigned int state, + unsigned int delay_us); +int __must_check mux_control_try_select_delay(struct mux_control *mux, + unsigned int state, + unsigned int delay_us); + +static inline int __must_check mux_control_select(struct mux_control *mux, + unsigned int state) +{ + return mux_control_select_delay(mux, state, 0); +} + +static inline int __must_check mux_control_try_select(struct mux_control *mux, + unsigned int state) +{ + return mux_control_try_select_delay(mux, state, 0); +} + int mux_control_deselect(struct mux_control *mux); struct mux_control *mux_control_get(struct device *dev, const char *mux_name); diff --git a/include/linux/mux/driver.h b/include/linux/mux/driver.h index 627a2c6bc02d..18824064f8c0 100644 --- a/include/linux/mux/driver.h +++ b/include/linux/mux/driver.h @@ -12,6 +12,7 @@ #include #include +#include #include struct mux_chip; @@ -33,6 +34,7 @@ struct mux_control_ops { * @states: The number of mux controller states. * @idle_state: The mux controller state to use when inactive, or one * of MUX_IDLE_AS_IS and MUX_IDLE_DISCONNECT. + * @last_change: Timestamp of last change * * Mux drivers may only change @states and @idle_state, and may only do so * between allocation and registration of the mux controller. Specifically, @@ -47,6 +49,8 @@ struct mux_control { unsigned int states; int idle_state; + + ktime_t last_change; }; /** -- cgit v1.2.3-70-g09d2 From b9221f71c285d4ce557c0a2a95e168f641f3396d Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Thu, 7 Oct 2021 15:46:40 +0200 Subject: dt-bindings: iio: io-channel-mux: Add property for settle time Hardware may require some time for the muxed analog signals to settle after the muxing is changed. Allow this time to be specified in the devicetree. Signed-off-by: Vincent Whitchurch Reviewed-by: Rob Herring Reviewed-by: Lars-Peter Clausen Tested-by: Lars-Peter Clausen Acked-by: Peter Rosin Link: https://lore.kernel.org/r/20211007134641.13417-3-vincent.whitchurch@axis.com Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/multiplexer/io-channel-mux.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml b/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml index 870b043406d8..5a7328042c76 100644 --- a/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml +++ b/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml @@ -39,6 +39,11 @@ properties: description: List of strings, labeling the mux controller states. + settle-time-us: + default: 0 + description: + Time required for analog signals to settle after muxing. + required: - compatible - io-channels -- cgit v1.2.3-70-g09d2 From 39aa50400794080e401a0334fbf0b058a67fc16c Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Thu, 7 Oct 2021 15:46:41 +0200 Subject: iio: multiplexer: iio-mux: Support settle-time-us property If the devicetree specifies that the hardware requires a settle time, pass this time on to the mux APIs. Signed-off-by: Vincent Whitchurch Reviewed-by: Lars-Peter Clausen Tested-by: Lars-Peter Clausen Acked-by: Peter Rosin Link: https://lore.kernel.org/r/20211007134641.13417-4-vincent.whitchurch@axis.com Signed-off-by: Jonathan Cameron --- drivers/iio/multiplexer/iio-mux.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/iio/multiplexer/iio-mux.c b/drivers/iio/multiplexer/iio-mux.c index d54ae5cbe51b..f422d44377df 100644 --- a/drivers/iio/multiplexer/iio-mux.c +++ b/drivers/iio/multiplexer/iio-mux.c @@ -33,6 +33,7 @@ struct mux { struct iio_chan_spec *chan; struct iio_chan_spec_ext_info *ext_info; struct mux_child *child; + u32 delay_us; }; static int iio_mux_select(struct mux *mux, int idx) @@ -42,7 +43,8 @@ static int iio_mux_select(struct mux *mux, int idx) int ret; int i; - ret = mux_control_select(mux->control, chan->channel); + ret = mux_control_select_delay(mux->control, chan->channel, + mux->delay_us); if (ret < 0) { mux->cached_state = -1; return ret; @@ -392,6 +394,9 @@ static int mux_probe(struct platform_device *pdev) mux->parent = parent; mux->cached_state = -1; + mux->delay_us = 0; + of_property_read_u32(np, "settle-time-us", &mux->delay_us); + indio_dev->name = dev_name(dev); indio_dev->info = &mux_info; indio_dev->modes = INDIO_DIRECT_MODE; -- cgit v1.2.3-70-g09d2 From 16cc9aa4a8a4dcd40eabd245f4533e1ea1b1e37f Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Thu, 26 Aug 2021 20:36:22 +0200 Subject: iio: adc: adc128s052: Simplify adc128_probe() Turn 'adc128_probe()' into a full resource managed function to simplify the code. This way, the .remove function can be removed. Doing so, the only 'spi_get_drvdata()' call is removed and the corresponding 'spi_set_drvdata()' can be removed as well. Suggested-by: Alexandru Ardelean Signed-off-by: Christophe JAILLET Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/4fa7fcc59c40e27af0569138d656c698a53dbd44.1630002770.git.christophe.jaillet@wanadoo.fr Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-adc128s052.c | 33 ++++++++++----------------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c index 83c1ae07b3e9..8e7adec87755 100644 --- a/drivers/iio/adc/ti-adc128s052.c +++ b/drivers/iio/adc/ti-adc128s052.c @@ -132,6 +132,11 @@ static const struct iio_info adc128_info = { .read_raw = adc128_read_raw, }; +static void adc128_disable_regulator(void *reg) +{ + regulator_disable(reg); +} + static int adc128_probe(struct spi_device *spi) { struct iio_dev *indio_dev; @@ -151,8 +156,6 @@ static int adc128_probe(struct spi_device *spi) adc = iio_priv(indio_dev); adc->spi = spi; - spi_set_drvdata(spi, indio_dev); - indio_dev->name = spi_get_device_id(spi)->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &adc128_info; @@ -167,29 +170,14 @@ static int adc128_probe(struct spi_device *spi) ret = regulator_enable(adc->reg); if (ret < 0) return ret; - - mutex_init(&adc->lock); - - ret = iio_device_register(indio_dev); + ret = devm_add_action_or_reset(&spi->dev, adc128_disable_regulator, + adc->reg); if (ret) - goto err_disable_regulator; - - return 0; - -err_disable_regulator: - regulator_disable(adc->reg); - return ret; -} - -static int adc128_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct adc128 *adc = iio_priv(indio_dev); + return ret; - iio_device_unregister(indio_dev); - regulator_disable(adc->reg); + mutex_init(&adc->lock); - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct of_device_id adc128_of_match[] = { @@ -231,7 +219,6 @@ static struct spi_driver adc128_driver = { .acpi_match_table = ACPI_PTR(adc128_acpi_match), }, .probe = adc128_probe, - .remove = adc128_remove, .id_table = adc128_id, }; module_spi_driver(adc128_driver); -- cgit v1.2.3-70-g09d2 From 86477c231c16a41d9b9805ce43c7a140a0dae4c0 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Sat, 9 Oct 2021 00:11:21 +0200 Subject: dt-bindings: iio: io-channel-mux: add optional #io-channel-cells Needed for in-kernel use of the child channels of the mux. Fixes problems like this, reported by dtbs_check: adc0mux: '#io-channel-cells' does not match any of the regexes: 'pinctrl-[0-9]+' arch/arm/boot/dts/aspeed-bmc-ampere-mtjade.dt.yaml Suggested-by: Vincent Whitchurch Signed-off-by: Peter Rosin Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml b/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml index 5a7328042c76..80b6229a6fad 100644 --- a/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml +++ b/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml @@ -44,6 +44,9 @@ properties: description: Time required for analog signals to settle after muxing. + "#io-channel-cells": + const: 1 + required: - compatible - io-channels -- cgit v1.2.3-70-g09d2 From 392998a8032a59bff4ec14dc9d5098c60b50baf9 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Sat, 9 Oct 2021 00:11:30 +0200 Subject: dt-bindings: iio: io-channel-mux: allow duplicate channel, labels This is needed since an empty channel label is used to indicate an unused channel, and there can be more that one of those. Fixes the following problem reported by dtbs_check: envelope-detector-mux: channels: ['', '', 'sync-1', 'in', 'out', 'sync-2', 'sys-reg', 'ana-reg'] has non-unique elements arch/arm/boot/dts/at91-tse850-3.dt.yaml Suggested-by: Rob Herring Signed-off-by: Peter Rosin Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/multiplexer/io-channel-mux.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml b/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml index 80b6229a6fad..611ad4444cf0 100644 --- a/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml +++ b/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml @@ -35,9 +35,10 @@ properties: mux-control-names: true channels: - $ref: /schemas/types.yaml#/definitions/string-array + $ref: /schemas/types.yaml#/definitions/non-unique-string-array description: - List of strings, labeling the mux controller states. + List of strings, labeling the mux controller states. An empty + string for a state means that the channel is not available. settle-time-us: default: 0 -- cgit v1.2.3-70-g09d2 From e46e2512ac84bd914212dc4b639d596db543f5b1 Mon Sep 17 00:00:00 2001 From: Roan van Dijk Date: Thu, 21 Oct 2021 16:00:18 +0200 Subject: iio: chemical: scd4x: Add a scale for the co2 concentration reading This patch adds a scale for the co2 concentration reading. The reading is expressed in percent after applying the scale to the raw value. Signed-off-by: Roan van Dijk Link: https://lore.kernel.org/r/20211021140018.3051213-1-roan@protonic.nl Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/scd4x.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/iio/chemical/scd4x.c b/drivers/iio/chemical/scd4x.c index ebebcb117ba2..267bc3c05338 100644 --- a/drivers/iio/chemical/scd4x.c +++ b/drivers/iio/chemical/scd4x.c @@ -353,7 +353,11 @@ static int scd4x_read_raw(struct iio_dev *indio_dev, *val = ret; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - if (chan->type == IIO_TEMP) { + if (chan->type == IIO_CONCENTRATION) { + *val = 0; + *val2 = 100; + return IIO_VAL_INT_PLUS_MICRO; + } else if (chan->type == IIO_TEMP) { *val = 175000; *val2 = 65536; return IIO_VAL_FRACTIONAL; @@ -503,7 +507,8 @@ static const struct iio_chan_spec scd4x_channels[] = { .type = IIO_CONCENTRATION, .channel2 = IIO_MOD_CO2, .modified = 1, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), .address = SCD4X_CO2, .scan_index = SCD4X_CO2, .scan_type = { -- cgit v1.2.3-70-g09d2 From 63aaf6d06d87866dd6b58265711a979ed4968420 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Thu, 21 Oct 2021 14:32:43 +0300 Subject: iio: frequency: adrf6780: add support for ADRF6780 The ADRF6780 is a silicon germanium (SiGe) design, wideband, microwave upconverter optimized for point to point microwave radio designs operating in the 5.9 GHz to 23.6 GHz frequency range. Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADRF6780.pdf Signed-off-by: Antoniu Miclaus Link: https://lore.kernel.org/r/20211021113244.56936-1-antoniu.miclaus@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/Kconfig | 12 + drivers/iio/frequency/Makefile | 1 + drivers/iio/frequency/adrf6780.c | 527 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 540 insertions(+) create mode 100644 drivers/iio/frequency/adrf6780.c diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig index 240b81502512..2c9e0559e8a4 100644 --- a/drivers/iio/frequency/Kconfig +++ b/drivers/iio/frequency/Kconfig @@ -49,5 +49,17 @@ config ADF4371 To compile this driver as a module, choose M here: the module will be called adf4371. + +config ADRF6780 + tristate "Analog Devices ADRF6780 Microwave Upconverter" + depends on SPI + depends on COMMON_CLK + help + Say yes here to build support for Analog Devices ADRF6780 + 5.9 GHz to 23.6 GHz, Wideband, Microwave Upconverter. + + To compile this driver as a module, choose M here: the + module will be called adrf6780. + endmenu endmenu diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile index 518b1e50caef..ae3136c79202 100644 --- a/drivers/iio/frequency/Makefile +++ b/drivers/iio/frequency/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_AD9523) += ad9523.o obj-$(CONFIG_ADF4350) += adf4350.o obj-$(CONFIG_ADF4371) += adf4371.o +obj-$(CONFIG_ADRF6780) += adrf6780.o diff --git a/drivers/iio/frequency/adrf6780.c b/drivers/iio/frequency/adrf6780.c new file mode 100644 index 000000000000..abe8b30fceca --- /dev/null +++ b/drivers/iio/frequency/adrf6780.c @@ -0,0 +1,527 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADRF6780 driver + * + * Copyright 2021 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* ADRF6780 Register Map */ +#define ADRF6780_REG_CONTROL 0x00 +#define ADRF6780_REG_ALARM_READBACK 0x01 +#define ADRF6780_REG_ALARM_MASKS 0x02 +#define ADRF6780_REG_ENABLE 0x03 +#define ADRF6780_REG_LINEARIZE 0x04 +#define ADRF6780_REG_LO_PATH 0x05 +#define ADRF6780_REG_ADC_CONTROL 0x06 +#define ADRF6780_REG_ADC_OUTPUT 0x0C + +/* ADRF6780_REG_CONTROL Map */ +#define ADRF6780_PARITY_EN_MSK BIT(15) +#define ADRF6780_SOFT_RESET_MSK BIT(14) +#define ADRF6780_CHIP_ID_MSK GENMASK(11, 4) +#define ADRF6780_CHIP_ID 0xA +#define ADRF6780_CHIP_REVISION_MSK GENMASK(3, 0) + +/* ADRF6780_REG_ALARM_READBACK Map */ +#define ADRF6780_PARITY_ERROR_MSK BIT(15) +#define ADRF6780_TOO_FEW_ERRORS_MSK BIT(14) +#define ADRF6780_TOO_MANY_ERRORS_MSK BIT(13) +#define ADRF6780_ADDRESS_RANGE_ERROR_MSK BIT(12) + +/* ADRF6780_REG_ENABLE Map */ +#define ADRF6780_VGA_BUFFER_EN_MSK BIT(8) +#define ADRF6780_DETECTOR_EN_MSK BIT(7) +#define ADRF6780_LO_BUFFER_EN_MSK BIT(6) +#define ADRF6780_IF_MODE_EN_MSK BIT(5) +#define ADRF6780_IQ_MODE_EN_MSK BIT(4) +#define ADRF6780_LO_X2_EN_MSK BIT(3) +#define ADRF6780_LO_PPF_EN_MSK BIT(2) +#define ADRF6780_LO_EN_MSK BIT(1) +#define ADRF6780_UC_BIAS_EN_MSK BIT(0) + +/* ADRF6780_REG_LINEARIZE Map */ +#define ADRF6780_RDAC_LINEARIZE_MSK GENMASK(7, 0) + +/* ADRF6780_REG_LO_PATH Map */ +#define ADRF6780_LO_SIDEBAND_MSK BIT(10) +#define ADRF6780_Q_PATH_PHASE_ACCURACY_MSK GENMASK(7, 4) +#define ADRF6780_I_PATH_PHASE_ACCURACY_MSK GENMASK(3, 0) + +/* ADRF6780_REG_ADC_CONTROL Map */ +#define ADRF6780_VDET_OUTPUT_SELECT_MSK BIT(3) +#define ADRF6780_ADC_START_MSK BIT(2) +#define ADRF6780_ADC_EN_MSK BIT(1) +#define ADRF6780_ADC_CLOCK_EN_MSK BIT(0) + +/* ADRF6780_REG_ADC_OUTPUT Map */ +#define ADRF6780_ADC_STATUS_MSK BIT(8) +#define ADRF6780_ADC_VALUE_MSK GENMASK(7, 0) + +struct adrf6780_state { + struct spi_device *spi; + struct clk *clkin; + /* Protect against concurrent accesses to the device */ + struct mutex lock; + bool vga_buff_en; + bool lo_buff_en; + bool if_mode_en; + bool iq_mode_en; + bool lo_x2_en; + bool lo_ppf_en; + bool lo_en; + bool uc_bias_en; + bool lo_sideband; + bool vdet_out_en; + u8 data[3] ____cacheline_aligned; +}; + +static int __adrf6780_spi_read(struct adrf6780_state *st, unsigned int reg, + unsigned int *val) +{ + int ret; + struct spi_transfer t = {0}; + + st->data[0] = 0x80 | (reg << 1); + st->data[1] = 0x0; + st->data[2] = 0x0; + + t.rx_buf = &st->data[0]; + t.tx_buf = &st->data[0]; + t.len = 3; + + ret = spi_sync_transfer(st->spi, &t, 1); + if (ret) + return ret; + + *val = (get_unaligned_be24(&st->data[0]) >> 1) & GENMASK(15, 0); + + return ret; +} + +static int adrf6780_spi_read(struct adrf6780_state *st, unsigned int reg, + unsigned int *val) +{ + int ret; + + mutex_lock(&st->lock); + ret = adrf6780_spi_read(st, reg, val); + mutex_unlock(&st->lock); + + return ret; +} + +static int __adrf6780_spi_write(struct adrf6780_state *st, + unsigned int reg, + unsigned int val) +{ + put_unaligned_be24((val << 1) | (reg << 17), &st->data[0]); + + return spi_write(st->spi, &st->data[0], 3); +} + +static int adrf6780_spi_write(struct adrf6780_state *st, unsigned int reg, + unsigned int val) +{ + int ret; + + mutex_lock(&st->lock); + ret = adrf6780_spi_write(st, reg, val); + mutex_unlock(&st->lock); + + return ret; +} + +static int __adrf6780_spi_update_bits(struct adrf6780_state *st, + unsigned int reg, unsigned int mask, + unsigned int val) +{ + int ret; + unsigned int data, temp; + + ret = __adrf6780_spi_read(st, reg, &data); + if (ret) + return ret; + + temp = (data & ~mask) | (val & mask); + + return __adrf6780_spi_write(st, reg, temp); +} + +static int adrf6780_spi_update_bits(struct adrf6780_state *st, unsigned int reg, + unsigned int mask, unsigned int val) +{ + int ret; + + mutex_lock(&st->lock); + ret = __adrf6780_spi_update_bits(st, reg, mask, val); + mutex_unlock(&st->lock); + + return ret; +} + +static int adrf6780_read_adc_raw(struct adrf6780_state *st, unsigned int *read_val) +{ + int ret; + + mutex_lock(&st->lock); + + ret = __adrf6780_spi_update_bits(st, ADRF6780_REG_ADC_CONTROL, + ADRF6780_ADC_EN_MSK | + ADRF6780_ADC_CLOCK_EN_MSK | + ADRF6780_ADC_START_MSK, + FIELD_PREP(ADRF6780_ADC_EN_MSK, 1) | + FIELD_PREP(ADRF6780_ADC_CLOCK_EN_MSK, 1) | + FIELD_PREP(ADRF6780_ADC_START_MSK, 1)); + if (ret) + goto exit; + + /* Recommended delay for the ADC to be ready*/ + usleep_range(200, 250); + + ret = __adrf6780_spi_read(st, ADRF6780_REG_ADC_OUTPUT, read_val); + if (ret) + goto exit; + + if (!(*read_val & ADRF6780_ADC_STATUS_MSK)) { + ret = -EINVAL; + goto exit; + } + + ret = __adrf6780_spi_update_bits(st, ADRF6780_REG_ADC_CONTROL, + ADRF6780_ADC_START_MSK, + FIELD_PREP(ADRF6780_ADC_START_MSK, 0)); + if (ret) + goto exit; + + ret = __adrf6780_spi_read(st, ADRF6780_REG_ADC_OUTPUT, read_val); + +exit: + mutex_unlock(&st->lock); + return ret; +} + +static int adrf6780_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct adrf6780_state *dev = iio_priv(indio_dev); + unsigned int data; + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = adrf6780_read_adc_raw(dev, &data); + if (ret) + return ret; + + *val = data & ADRF6780_ADC_VALUE_MSK; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + ret = adrf6780_spi_read(dev, ADRF6780_REG_LINEARIZE, &data); + if (ret) + return ret; + + *val = data & ADRF6780_RDAC_LINEARIZE_MSK; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_PHASE: + ret = adrf6780_spi_read(dev, ADRF6780_REG_LO_PATH, &data); + if (ret) + return ret; + + switch (chan->channel2) { + case IIO_MOD_I: + *val = data & ADRF6780_I_PATH_PHASE_ACCURACY_MSK; + + return IIO_VAL_INT; + case IIO_MOD_Q: + *val = FIELD_GET(ADRF6780_Q_PATH_PHASE_ACCURACY_MSK, + data); + + return IIO_VAL_INT; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int adrf6780_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct adrf6780_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SCALE: + return adrf6780_spi_write(st, ADRF6780_REG_LINEARIZE, val); + case IIO_CHAN_INFO_PHASE: + switch (chan->channel2) { + case IIO_MOD_I: + return adrf6780_spi_update_bits(st, + ADRF6780_REG_LO_PATH, + ADRF6780_I_PATH_PHASE_ACCURACY_MSK, + FIELD_PREP(ADRF6780_I_PATH_PHASE_ACCURACY_MSK, val)); + case IIO_MOD_Q: + return adrf6780_spi_update_bits(st, + ADRF6780_REG_LO_PATH, + ADRF6780_Q_PATH_PHASE_ACCURACY_MSK, + FIELD_PREP(ADRF6780_Q_PATH_PHASE_ACCURACY_MSK, val)); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int adrf6780_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int write_val, + unsigned int *read_val) +{ + struct adrf6780_state *st = iio_priv(indio_dev); + + if (read_val) + return adrf6780_spi_read(st, reg, read_val); + else + return adrf6780_spi_write(st, reg, write_val); +} + +static const struct iio_info adrf6780_info = { + .read_raw = adrf6780_read_raw, + .write_raw = adrf6780_write_raw, + .debugfs_reg_access = &adrf6780_reg_access, +}; + +#define ADRF6780_CHAN_ADC(_channel) { \ + .type = IIO_ALTVOLTAGE, \ + .output = 0, \ + .indexed = 1, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ +} + +#define ADRF6780_CHAN_RDAC(_channel) { \ + .type = IIO_ALTVOLTAGE, \ + .output = 1, \ + .indexed = 1, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) \ +} + +#define ADRF6780_CHAN_IQ_PHASE(_channel, rf_comp) { \ + .type = IIO_ALTVOLTAGE, \ + .modified = 1, \ + .output = 1, \ + .indexed = 1, \ + .channel2 = IIO_MOD_##rf_comp, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PHASE) \ +} + +static const struct iio_chan_spec adrf6780_channels[] = { + ADRF6780_CHAN_ADC(0), + ADRF6780_CHAN_RDAC(0), + ADRF6780_CHAN_IQ_PHASE(0, I), + ADRF6780_CHAN_IQ_PHASE(0, Q), +}; + +static int adrf6780_reset(struct adrf6780_state *st) +{ + int ret; + struct spi_device *spi = st->spi; + + ret = __adrf6780_spi_update_bits(st, ADRF6780_REG_CONTROL, + ADRF6780_SOFT_RESET_MSK, + FIELD_PREP(ADRF6780_SOFT_RESET_MSK, 1)); + if (ret) { + dev_err(&spi->dev, "ADRF6780 SPI software reset failed.\n"); + return ret; + } + + ret = __adrf6780_spi_update_bits(st, ADRF6780_REG_CONTROL, + ADRF6780_SOFT_RESET_MSK, + FIELD_PREP(ADRF6780_SOFT_RESET_MSK, 0)); + if (ret) { + dev_err(&spi->dev, "ADRF6780 SPI software reset disable failed.\n"); + return ret; + } + + return 0; +} + +static int adrf6780_init(struct adrf6780_state *st) +{ + int ret; + unsigned int chip_id, enable_reg, enable_reg_msk; + struct spi_device *spi = st->spi; + + /* Perform a software reset */ + ret = adrf6780_reset(st); + if (ret) + return ret; + + ret = __adrf6780_spi_read(st, ADRF6780_REG_CONTROL, &chip_id); + if (ret) + return ret; + + chip_id = FIELD_GET(ADRF6780_CHIP_ID_MSK, chip_id); + if (chip_id != ADRF6780_CHIP_ID) { + dev_err(&spi->dev, "ADRF6780 Invalid Chip ID.\n"); + return -EINVAL; + } + + enable_reg_msk = ADRF6780_VGA_BUFFER_EN_MSK | + ADRF6780_DETECTOR_EN_MSK | + ADRF6780_LO_BUFFER_EN_MSK | + ADRF6780_IF_MODE_EN_MSK | + ADRF6780_IQ_MODE_EN_MSK | + ADRF6780_LO_X2_EN_MSK | + ADRF6780_LO_PPF_EN_MSK | + ADRF6780_LO_EN_MSK | + ADRF6780_UC_BIAS_EN_MSK; + + enable_reg = FIELD_PREP(ADRF6780_VGA_BUFFER_EN_MSK, st->vga_buff_en) | + FIELD_PREP(ADRF6780_DETECTOR_EN_MSK, 1) | + FIELD_PREP(ADRF6780_LO_BUFFER_EN_MSK, st->lo_buff_en) | + FIELD_PREP(ADRF6780_IF_MODE_EN_MSK, st->if_mode_en) | + FIELD_PREP(ADRF6780_IQ_MODE_EN_MSK, st->iq_mode_en) | + FIELD_PREP(ADRF6780_LO_X2_EN_MSK, st->lo_x2_en) | + FIELD_PREP(ADRF6780_LO_PPF_EN_MSK, st->lo_ppf_en) | + FIELD_PREP(ADRF6780_LO_EN_MSK, st->lo_en) | + FIELD_PREP(ADRF6780_UC_BIAS_EN_MSK, st->uc_bias_en); + + ret = __adrf6780_spi_update_bits(st, ADRF6780_REG_ENABLE, + enable_reg_msk, enable_reg); + if (ret) + return ret; + + ret = __adrf6780_spi_update_bits(st, ADRF6780_REG_LO_PATH, + ADRF6780_LO_SIDEBAND_MSK, + FIELD_PREP(ADRF6780_LO_SIDEBAND_MSK, st->lo_sideband)); + if (ret) + return ret; + + return __adrf6780_spi_update_bits(st, ADRF6780_REG_ADC_CONTROL, + ADRF6780_VDET_OUTPUT_SELECT_MSK, + FIELD_PREP(ADRF6780_VDET_OUTPUT_SELECT_MSK, st->vdet_out_en)); +} + +static void adrf6780_properties_parse(struct adrf6780_state *st) +{ + struct spi_device *spi = st->spi; + + st->vga_buff_en = device_property_read_bool(&spi->dev, "adi,vga-buff-en"); + st->lo_buff_en = device_property_read_bool(&spi->dev, "adi,lo-buff-en"); + st->if_mode_en = device_property_read_bool(&spi->dev, "adi,if-mode-en"); + st->iq_mode_en = device_property_read_bool(&spi->dev, "adi,iq-mode-en"); + st->lo_x2_en = device_property_read_bool(&spi->dev, "adi,lo-x2-en"); + st->lo_ppf_en = device_property_read_bool(&spi->dev, "adi,lo-ppf-en"); + st->lo_en = device_property_read_bool(&spi->dev, "adi,lo-en"); + st->uc_bias_en = device_property_read_bool(&spi->dev, "adi,uc-bias-en"); + st->lo_sideband = device_property_read_bool(&spi->dev, "adi,lo-sideband"); + st->vdet_out_en = device_property_read_bool(&spi->dev, "adi,vdet-out-en"); +} + +static void adrf6780_clk_disable(void *data) +{ + clk_disable_unprepare(data); +} + +static void adrf6780_powerdown(void *data) +{ + /* Disable all components in the Enable Register */ + adrf6780_spi_write(data, ADRF6780_REG_ENABLE, 0x0); +} + +static int adrf6780_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct adrf6780_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + indio_dev->info = &adrf6780_info; + indio_dev->name = "adrf6780"; + indio_dev->channels = adrf6780_channels; + indio_dev->num_channels = ARRAY_SIZE(adrf6780_channels); + + st->spi = spi; + + adrf6780_properties_parse(st); + + st->clkin = devm_clk_get(&spi->dev, "lo_in"); + if (IS_ERR(st->clkin)) + return dev_err_probe(&spi->dev, PTR_ERR(st->clkin), + "failed to get the LO input clock\n"); + + ret = clk_prepare_enable(st->clkin); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, adrf6780_clk_disable, + st->clkin); + if (ret) + return ret; + + mutex_init(&st->lock); + + ret = adrf6780_init(st); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, adrf6780_powerdown, st); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id adrf6780_id[] = { + { "adrf6780", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, adrf6780_id); + +static const struct of_device_id adrf6780_of_match[] = { + { .compatible = "adi,adrf6780" }, + {} +}; +MODULE_DEVICE_TABLE(of, adrf6780_of_match); + +static struct spi_driver adrf6780_driver = { + .driver = { + .name = "adrf6780", + .of_match_table = adrf6780_of_match, + }, + .probe = adrf6780_probe, + .id_table = adrf6780_id, +}; +module_spi_driver(adrf6780_driver); + +MODULE_AUTHOR("Antoniu Miclaus Date: Thu, 21 Oct 2021 14:32:44 +0300 Subject: dt-bindings: iio: frequency: add adrf6780 doc Add device tree bindings for the ADRF6780 Upconverter. Reviewed-by: Rob Herring Signed-off-by: Antoniu Miclaus Link: https://lore.kernel.org/r/20211021113244.56936-2-antoniu.miclaus@analog.com Signed-off-by: Jonathan Cameron --- .../bindings/iio/frequency/adi,adrf6780.yaml | 131 +++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/frequency/adi,adrf6780.yaml diff --git a/Documentation/devicetree/bindings/iio/frequency/adi,adrf6780.yaml b/Documentation/devicetree/bindings/iio/frequency/adi,adrf6780.yaml new file mode 100644 index 000000000000..3a8ea93f4e0c --- /dev/null +++ b/Documentation/devicetree/bindings/iio/frequency/adi,adrf6780.yaml @@ -0,0 +1,131 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/frequency/adi,adrf6780.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ADRF6780 Microwave Upconverter + +maintainers: + - Antoniu Miclaus + +description: | + Wideband, microwave upconverter optimized for point to point microwave + radio designs operating in the 5.9 GHz to 23.6 GHz frequency range. + + https://www.analog.com/en/products/adrf6780.html + +properties: + compatible: + enum: + - adi,adrf6780 + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 1000000 + + clocks: + description: + Definition of the external clock. + minItems: 1 + + clock-names: + items: + - const: lo_in + + clock-output-names: + maxItems: 1 + + adi,vga-buff-en: + description: + RF Variable Gain Amplifier Buffer Enable. Gain is controlled by + the voltage on the VATT pin. + type: boolean + + adi,lo-buff-en: + description: + Local Oscillator Amplifier Enable. Disable to put the part in + a power down state. + type: boolean + + adi,if-mode-en: + description: + Intermediate Frequency Mode Enable. Either IF Mode or I/Q Mode + can be enabled at a time. + type: boolean + + adi,iq-mode-en: + description: + I/Q Mode Enable. Either IF Mode or I/Q Mode can be enabled at a + time. + type: boolean + + adi,lo-x2-en: + description: + Double the Local Oscillator output frequency from the Local + Oscillator Input Frequency. Either LOx1 or LOx2 can be enabled + at a time. + type: boolean + + adi,lo-ppf-en: + description: + Local Oscillator input frequency equal to the Local Oscillator + output frequency (LO x1). Either LOx1 or LOx2 can be enabled + at a time. + type: boolean + + adi,lo-en: + description: + Enable additional cirtuitry in the LO chain. Disable to put the + part in a power down state. + type: boolean + + adi,uc-bias-en: + description: + Enable all bias circuitry thourghout the entire part. + Disable to put the part in a power down state. + type: boolean + + adi,lo-sideband: + description: + Switch to the Lower LO Sideband. By default the Upper LO + sideband is enabled. + type: boolean + + adi,vdet-out-en: + description: + VDET Output Select Enable. Expose the RF detector output to the + VDET external pin. + type: boolean + + '#clock-cells': + const: 0 + +dependencies: + adi,lo-x2-en: [ "adi,lo-en" ] + adi,lo-ppf-en: [ "adi,lo-en" ] + +required: + - compatible + - reg + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + adrf6780@0 { + compatible = "adi,adrf6780"; + reg = <0>; + spi-max-frequency = <1000000>; + clocks = <&adrf6780_lo>; + clock-names = "lo_in"; + }; + }; +... -- cgit v1.2.3-70-g09d2 From 008d3825a805557464c5e75f9eb806a3aa2f5e6d Mon Sep 17 00:00:00 2001 From: Eddie James Date: Tue, 19 Oct 2021 15:53:04 -0500 Subject: fsi: occ: Use a large buffer for responses Allocate a large buffer for each OCC to handle response data. This removes memory allocation during an operation, and also allows for the maximum amount of SBE FFDC. Previously for the putsram and attn commands, only 32 words would have been available, and for getsram, only up to the size of the transfer. SBE FFDC might be up to 8Kb. The SBE interface expects data to be specified in units of words (4 bytes), defined as OCC_MAX_RESP_WORDS. This change allows the full FFDC capture to be implemented, where before it was not available. Signed-off-by: Eddie James Link: https://lore.kernel.org/r/20211019205307.36946-2-eajames@linux.ibm.com Signed-off-by: Joel Stanley --- drivers/fsi/fsi-occ.c | 110 +++++++++++++++++++----------------------------- include/linux/fsi-occ.h | 2 + 2 files changed, 46 insertions(+), 66 deletions(-) diff --git a/drivers/fsi/fsi-occ.c b/drivers/fsi/fsi-occ.c index ecf738411fe2..b4302776026d 100644 --- a/drivers/fsi/fsi-occ.c +++ b/drivers/fsi/fsi-occ.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -33,13 +34,6 @@ #define OCC_P10_SRAM_MODE 0x58 /* Normal mode, OCB channel 2 */ -/* - * Assume we don't have much FFDC, if we do we'll overflow and - * fail the command. This needs to be big enough for simple - * commands as well. - */ -#define OCC_SBE_STATUS_WORDS 32 - #define OCC_TIMEOUT_MS 1000 #define OCC_CMD_IN_PRG_WAIT_MS 50 @@ -51,6 +45,7 @@ struct occ { char name[32]; int idx; u8 sequence_number; + void *buffer; enum versions version; struct miscdevice mdev; struct mutex occ_lock; @@ -241,8 +236,10 @@ static int occ_verify_checksum(struct occ *occ, struct occ_response *resp, static int occ_getsram(struct occ *occ, u32 offset, void *data, ssize_t len) { u32 data_len = ((len + 7) / 8) * 8; /* must be multiples of 8 B */ - size_t cmd_len, resp_len, resp_data_len; - __be32 *resp, cmd[6]; + size_t cmd_len, resp_data_len; + size_t resp_len = OCC_MAX_RESP_WORDS; + __be32 *resp = occ->buffer; + __be32 cmd[6]; int idx = 0, rc; /* @@ -269,19 +266,19 @@ static int occ_getsram(struct occ *occ, u32 offset, void *data, ssize_t len) cmd[1] = cpu_to_be32(SBEFIFO_CMD_GET_OCC_SRAM); cmd[4 + idx] = cpu_to_be32(data_len); - resp_len = (data_len >> 2) + OCC_SBE_STATUS_WORDS; - resp = kzalloc(resp_len << 2, GFP_KERNEL); - if (!resp) - return -ENOMEM; - rc = sbefifo_submit(occ->sbefifo, cmd, cmd_len, resp, &resp_len); if (rc) - goto free; + return rc; rc = sbefifo_parse_status(occ->sbefifo, SBEFIFO_CMD_GET_OCC_SRAM, resp, resp_len, &resp_len); - if (rc) - goto free; + if (rc > 0) { + dev_err(occ->dev, "SRAM read returned failure status: %08x\n", + rc); + return -EBADMSG; + } else if (rc) { + return rc; + } resp_data_len = be32_to_cpu(resp[resp_len - 1]); if (resp_data_len != data_len) { @@ -292,39 +289,21 @@ static int occ_getsram(struct occ *occ, u32 offset, void *data, ssize_t len) memcpy(data, resp, len); } -free: - /* Convert positive SBEI status */ - if (rc > 0) { - dev_err(occ->dev, "SRAM read returned failure status: %08x\n", - rc); - rc = -EBADMSG; - } - - kfree(resp); return rc; } static int occ_putsram(struct occ *occ, const void *data, ssize_t len, u8 seq_no, u16 checksum) { - size_t cmd_len, buf_len, resp_len, resp_data_len; u32 data_len = ((len + 7) / 8) * 8; /* must be multiples of 8 B */ - __be32 *buf; + size_t cmd_len, resp_data_len; + size_t resp_len = OCC_MAX_RESP_WORDS; + __be32 *buf = occ->buffer; u8 *byte_buf; int idx = 0, rc; cmd_len = (occ->version == occ_p10) ? 6 : 5; - - /* - * We use the same buffer for command and response, make - * sure it's big enough - */ - resp_len = OCC_SBE_STATUS_WORDS; cmd_len += data_len >> 2; - buf_len = max(cmd_len, resp_len); - buf = kzalloc(buf_len << 2, GFP_KERNEL); - if (!buf) - return -ENOMEM; /* * Magic sequence to do SBE putsram command. SBE will transfer @@ -361,12 +340,17 @@ static int occ_putsram(struct occ *occ, const void *data, ssize_t len, rc = sbefifo_submit(occ->sbefifo, buf, cmd_len, buf, &resp_len); if (rc) - goto free; + return rc; rc = sbefifo_parse_status(occ->sbefifo, SBEFIFO_CMD_PUT_OCC_SRAM, buf, resp_len, &resp_len); - if (rc) - goto free; + if (rc > 0) { + dev_err(occ->dev, "SRAM write returned failure status: %08x\n", + rc); + return -EBADMSG; + } else if (rc) { + return rc; + } if (resp_len != 1) { dev_err(occ->dev, "SRAM write response length invalid: %zd\n", @@ -382,27 +366,16 @@ static int occ_putsram(struct occ *occ, const void *data, ssize_t len, } } -free: - /* Convert positive SBEI status */ - if (rc > 0) { - dev_err(occ->dev, "SRAM write returned failure status: %08x\n", - rc); - rc = -EBADMSG; - } - - kfree(buf); return rc; } static int occ_trigger_attn(struct occ *occ) { - __be32 buf[OCC_SBE_STATUS_WORDS]; - size_t cmd_len, resp_len, resp_data_len; + __be32 *buf = occ->buffer; + size_t cmd_len, resp_data_len; + size_t resp_len = OCC_MAX_RESP_WORDS; int idx = 0, rc; - BUILD_BUG_ON(OCC_SBE_STATUS_WORDS < 8); - resp_len = OCC_SBE_STATUS_WORDS; - switch (occ->version) { default: case occ_p9: @@ -427,12 +400,17 @@ static int occ_trigger_attn(struct occ *occ) rc = sbefifo_submit(occ->sbefifo, buf, cmd_len, buf, &resp_len); if (rc) - goto error; + return rc; rc = sbefifo_parse_status(occ->sbefifo, SBEFIFO_CMD_PUT_OCC_SRAM, buf, resp_len, &resp_len); - if (rc) - goto error; + if (rc > 0) { + dev_err(occ->dev, "SRAM attn returned failure status: %08x\n", + rc); + return -EBADMSG; + } else if (rc) { + return rc; + } if (resp_len != 1) { dev_err(occ->dev, "SRAM attn response length invalid: %zd\n", @@ -448,14 +426,6 @@ static int occ_trigger_attn(struct occ *occ) } } - error: - /* Convert positive SBEI status */ - if (rc > 0) { - dev_err(occ->dev, "SRAM attn returned failure status: %08x\n", - rc); - rc = -EBADMSG; - } - return rc; } @@ -590,6 +560,11 @@ static int occ_probe(struct platform_device *pdev) if (!occ) return -ENOMEM; + /* SBE words are always four bytes */ + occ->buffer = kvmalloc(OCC_MAX_RESP_WORDS * 4, GFP_KERNEL); + if (!occ->buffer) + return -ENOMEM; + occ->version = (uintptr_t)of_device_get_match_data(dev); occ->dev = dev; occ->sbefifo = dev->parent; @@ -625,6 +600,7 @@ static int occ_probe(struct platform_device *pdev) if (rc) { dev_err(dev, "failed to register miscdevice: %d\n", rc); ida_simple_remove(&occ_ida, occ->idx); + kvfree(occ->buffer); return rc; } @@ -640,6 +616,8 @@ static int occ_remove(struct platform_device *pdev) { struct occ *occ = platform_get_drvdata(pdev); + kvfree(occ->buffer); + misc_deregister(&occ->mdev); device_for_each_child(&pdev->dev, NULL, occ_unregister_child); diff --git a/include/linux/fsi-occ.h b/include/linux/fsi-occ.h index d4cdc2aa6e33..7ee3dbd7f4b3 100644 --- a/include/linux/fsi-occ.h +++ b/include/linux/fsi-occ.h @@ -19,6 +19,8 @@ struct device; #define OCC_RESP_CRIT_OCB 0xE3 #define OCC_RESP_CRIT_HW 0xE4 +#define OCC_MAX_RESP_WORDS 2048 + int fsi_occ_submit(struct device *dev, const void *request, size_t req_len, void *response, size_t *resp_len); -- cgit v1.2.3-70-g09d2 From 8ec3cc9fb51dc973ea6886cc6ba3dcea4eee98ec Mon Sep 17 00:00:00 2001 From: Eddie James Date: Tue, 19 Oct 2021 15:53:05 -0500 Subject: fsi: occ: Store the SBEFIFO FFDC in the user response buffer If the SBEFIFO response indicates an error, store the response in the user buffer and return an error. Previously, the user had no way of obtaining the SBEFIFO FFDC. The user's buffer now contains data in the event of a failure. No change in the event of a successful transfer. Signed-off-by: Eddie James Link: https://lore.kernel.org/r/20211019205307.36946-3-eajames@linux.ibm.com Signed-off-by: Joel Stanley --- drivers/fsi/fsi-occ.c | 66 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/drivers/fsi/fsi-occ.c b/drivers/fsi/fsi-occ.c index b4302776026d..7eaab1be0aa4 100644 --- a/drivers/fsi/fsi-occ.c +++ b/drivers/fsi/fsi-occ.c @@ -46,6 +46,9 @@ struct occ { int idx; u8 sequence_number; void *buffer; + void *client_buffer; + size_t client_buffer_size; + size_t client_response_size; enum versions version; struct miscdevice mdev; struct mutex occ_lock; @@ -208,6 +211,22 @@ static const struct file_operations occ_fops = { .release = occ_release, }; +static void occ_save_ffdc(struct occ *occ, __be32 *resp, size_t parsed_len, + size_t resp_len) +{ + if (resp_len > parsed_len) { + size_t dh = resp_len - parsed_len; + size_t ffdc_len = (dh - 1) * 4; /* SBE words are four bytes */ + __be32 *ffdc = &resp[parsed_len]; + + if (ffdc_len > occ->client_buffer_size) + ffdc_len = occ->client_buffer_size; + + memcpy(occ->client_buffer, ffdc, ffdc_len); + occ->client_response_size = ffdc_len; + } +} + static int occ_verify_checksum(struct occ *occ, struct occ_response *resp, u16 data_length) { @@ -236,7 +255,7 @@ static int occ_verify_checksum(struct occ *occ, struct occ_response *resp, static int occ_getsram(struct occ *occ, u32 offset, void *data, ssize_t len) { u32 data_len = ((len + 7) / 8) * 8; /* must be multiples of 8 B */ - size_t cmd_len, resp_data_len; + size_t cmd_len, parsed_len, resp_data_len; size_t resp_len = OCC_MAX_RESP_WORDS; __be32 *resp = occ->buffer; __be32 cmd[6]; @@ -271,16 +290,17 @@ static int occ_getsram(struct occ *occ, u32 offset, void *data, ssize_t len) return rc; rc = sbefifo_parse_status(occ->sbefifo, SBEFIFO_CMD_GET_OCC_SRAM, - resp, resp_len, &resp_len); + resp, resp_len, &parsed_len); if (rc > 0) { dev_err(occ->dev, "SRAM read returned failure status: %08x\n", rc); - return -EBADMSG; + occ_save_ffdc(occ, resp, parsed_len, resp_len); + return -ECOMM; } else if (rc) { return rc; } - resp_data_len = be32_to_cpu(resp[resp_len - 1]); + resp_data_len = be32_to_cpu(resp[parsed_len - 1]); if (resp_data_len != data_len) { dev_err(occ->dev, "SRAM read expected %d bytes got %zd\n", data_len, resp_data_len); @@ -296,7 +316,7 @@ static int occ_putsram(struct occ *occ, const void *data, ssize_t len, u8 seq_no, u16 checksum) { u32 data_len = ((len + 7) / 8) * 8; /* must be multiples of 8 B */ - size_t cmd_len, resp_data_len; + size_t cmd_len, parsed_len, resp_data_len; size_t resp_len = OCC_MAX_RESP_WORDS; __be32 *buf = occ->buffer; u8 *byte_buf; @@ -343,18 +363,19 @@ static int occ_putsram(struct occ *occ, const void *data, ssize_t len, return rc; rc = sbefifo_parse_status(occ->sbefifo, SBEFIFO_CMD_PUT_OCC_SRAM, - buf, resp_len, &resp_len); + buf, resp_len, &parsed_len); if (rc > 0) { dev_err(occ->dev, "SRAM write returned failure status: %08x\n", rc); - return -EBADMSG; + occ_save_ffdc(occ, buf, parsed_len, resp_len); + return -ECOMM; } else if (rc) { return rc; } - if (resp_len != 1) { + if (parsed_len != 1) { dev_err(occ->dev, "SRAM write response length invalid: %zd\n", - resp_len); + parsed_len); rc = -EBADMSG; } else { resp_data_len = be32_to_cpu(buf[0]); @@ -372,7 +393,7 @@ static int occ_putsram(struct occ *occ, const void *data, ssize_t len, static int occ_trigger_attn(struct occ *occ) { __be32 *buf = occ->buffer; - size_t cmd_len, resp_data_len; + size_t cmd_len, parsed_len, resp_data_len; size_t resp_len = OCC_MAX_RESP_WORDS; int idx = 0, rc; @@ -403,18 +424,19 @@ static int occ_trigger_attn(struct occ *occ) return rc; rc = sbefifo_parse_status(occ->sbefifo, SBEFIFO_CMD_PUT_OCC_SRAM, - buf, resp_len, &resp_len); + buf, resp_len, &parsed_len); if (rc > 0) { dev_err(occ->dev, "SRAM attn returned failure status: %08x\n", rc); - return -EBADMSG; + occ_save_ffdc(occ, buf, parsed_len, resp_len); + return -ECOMM; } else if (rc) { return rc; } - if (resp_len != 1) { + if (parsed_len != 1) { dev_err(occ->dev, "SRAM attn response length invalid: %zd\n", - resp_len); + parsed_len); rc = -EBADMSG; } else { resp_data_len = be32_to_cpu(buf[0]); @@ -437,6 +459,7 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len, msecs_to_jiffies(OCC_CMD_IN_PRG_WAIT_MS); struct occ *occ = dev_get_drvdata(dev); struct occ_response *resp = response; + size_t user_resp_len = *resp_len; u8 seq_no; u16 checksum = 0; u16 resp_data_length; @@ -445,11 +468,13 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len, int rc; size_t i; + *resp_len = 0; + if (!occ) return -ENODEV; - if (*resp_len < 7) { - dev_dbg(dev, "Bad resplen %zd\n", *resp_len); + if (user_resp_len < 7) { + dev_dbg(dev, "Bad resplen %zd\n", user_resp_len); return -EINVAL; } @@ -459,6 +484,10 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len, mutex_lock(&occ->occ_lock); + occ->client_buffer = response; + occ->client_buffer_size = user_resp_len; + occ->client_response_size = 0; + /* * Get a sequence number and update the counter. Avoid a sequence * number of 0 which would pass the response check below even if the @@ -509,7 +538,7 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len, resp_data_length = get_unaligned_be16(&resp->data_length); /* Message size is data length + 5 bytes header + 2 bytes checksum */ - if ((resp_data_length + 7) > *resp_len) { + if ((resp_data_length + 7) > user_resp_len) { rc = -EMSGSIZE; goto done; } @@ -525,10 +554,11 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len, goto done; } - *resp_len = resp_data_length + 7; + occ->client_response_size = resp_data_length + 7; rc = occ_verify_checksum(occ, resp, resp_data_length); done: + *resp_len = occ->client_response_size; mutex_unlock(&occ->occ_lock); return rc; -- cgit v1.2.3-70-g09d2 From 4cf400e120b303c25fd378fb4286fa682e4e0a33 Mon Sep 17 00:00:00 2001 From: Eddie James Date: Tue, 19 Oct 2021 15:53:06 -0500 Subject: docs: ABI: testing: Document the OCC hwmon FFDC binary interface Add documentation for the new binary sysfs that will dump the SBEFIFO FFDC. Signed-off-by: Eddie James Link: https://lore.kernel.org/r/20211019205307.36946-4-eajames@linux.ibm.com Signed-off-by: Joel Stanley --- .../ABI/testing/sysfs-bus-platform-devices-occ-hwmon | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-platform-devices-occ-hwmon diff --git a/Documentation/ABI/testing/sysfs-bus-platform-devices-occ-hwmon b/Documentation/ABI/testing/sysfs-bus-platform-devices-occ-hwmon new file mode 100644 index 000000000000..b24d7ab0278f --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-platform-devices-occ-hwmon @@ -0,0 +1,13 @@ +What: /sys/bus/platform/devices/occ-hwmon.X/ffdc +KernelVersion: 5.15 +Contact: eajames@linux.ibm.com +Description: + Contains the First Failure Data Capture from the SBEFIFO + hardware, if there is any from a previous transfer. Otherwise, + the file is empty. The data is cleared when it's been + completely read by a user. As the name suggests, only the data + from the first error is saved, until it's cleared upon read. The OCC hwmon driver, running on + a Baseboard Management Controller (BMC), communicates with + POWER9 and up processors over the Self-Boot Engine (SBE) FIFO. + In many error conditions, the SBEFIFO will return error data + indicating the type of error and system state, etc. -- cgit v1.2.3-70-g09d2 From 5027a34a575e79ac225f5f3e710491e4c372c44a Mon Sep 17 00:00:00 2001 From: Eddie James Date: Tue, 19 Oct 2021 15:53:07 -0500 Subject: hwmon: (occ) Provide the SBEFIFO FFDC in binary sysfs Save any FFDC provided by the OCC driver, and provide it to userspace through a binary sysfs entry. Notify userspace pollers when there is an error too. Signed-off-by: Eddie James Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20211019205307.36946-5-eajames@linux.ibm.com Signed-off-by: Joel Stanley --- drivers/hwmon/occ/p9_sbe.c | 86 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/occ/p9_sbe.c b/drivers/hwmon/occ/p9_sbe.c index 9709f2b9c052..e50243580269 100644 --- a/drivers/hwmon/occ/p9_sbe.c +++ b/drivers/hwmon/occ/p9_sbe.c @@ -4,18 +4,79 @@ #include #include #include +#include #include +#include #include +#include +#include #include "common.h" struct p9_sbe_occ { struct occ occ; + bool sbe_error; + void *ffdc; + size_t ffdc_len; + size_t ffdc_size; + struct mutex sbe_error_lock; /* lock access to ffdc data */ struct device *sbe; }; #define to_p9_sbe_occ(x) container_of((x), struct p9_sbe_occ, occ) +static ssize_t ffdc_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *battr, char *buf, loff_t pos, + size_t count) +{ + ssize_t rc = 0; + struct occ *occ = dev_get_drvdata(kobj_to_dev(kobj)); + struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ); + + mutex_lock(&ctx->sbe_error_lock); + if (ctx->sbe_error) { + rc = memory_read_from_buffer(buf, count, &pos, ctx->ffdc, + ctx->ffdc_len); + if (pos >= ctx->ffdc_len) + ctx->sbe_error = false; + } + mutex_unlock(&ctx->sbe_error_lock); + + return rc; +} +static BIN_ATTR_RO(ffdc, OCC_MAX_RESP_WORDS * 4); + +static bool p9_sbe_occ_save_ffdc(struct p9_sbe_occ *ctx, const void *resp, + size_t resp_len) +{ + bool notify = false; + + mutex_lock(&ctx->sbe_error_lock); + if (!ctx->sbe_error) { + if (resp_len > ctx->ffdc_size) { + if (ctx->ffdc) + kvfree(ctx->ffdc); + ctx->ffdc = kvmalloc(resp_len, GFP_KERNEL); + if (!ctx->ffdc) { + ctx->ffdc_len = 0; + ctx->ffdc_size = 0; + goto done; + } + + ctx->ffdc_size = resp_len; + } + + notify = true; + ctx->sbe_error = true; + ctx->ffdc_len = resp_len; + memcpy(ctx->ffdc, resp, resp_len); + } + +done: + mutex_unlock(&ctx->sbe_error_lock); + return notify; +} + static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd, size_t len) { struct occ_response *resp = &occ->resp; @@ -24,8 +85,15 @@ static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd, size_t len) int rc; rc = fsi_occ_submit(ctx->sbe, cmd, len, resp, &resp_len); - if (rc < 0) + if (rc < 0) { + if (resp_len) { + if (p9_sbe_occ_save_ffdc(ctx, resp, resp_len)) + sysfs_notify(&occ->bus_dev->kobj, NULL, + bin_attr_ffdc.attr.name); + } + return rc; + } switch (resp->return_status) { case OCC_RESP_CMD_IN_PRG: @@ -65,6 +133,8 @@ static int p9_sbe_occ_probe(struct platform_device *pdev) if (!ctx) return -ENOMEM; + mutex_init(&ctx->sbe_error_lock); + ctx->sbe = pdev->dev.parent; occ = &ctx->occ; occ->bus_dev = &pdev->dev; @@ -78,6 +148,15 @@ static int p9_sbe_occ_probe(struct platform_device *pdev) if (rc == -ESHUTDOWN) rc = -ENODEV; /* Host is shutdown, don't spew errors */ + if (!rc) { + rc = device_create_bin_file(occ->bus_dev, &bin_attr_ffdc); + if (rc) { + dev_warn(occ->bus_dev, + "failed to create SBE error ffdc file\n"); + rc = 0; + } + } + return rc; } @@ -86,9 +165,14 @@ static int p9_sbe_occ_remove(struct platform_device *pdev) struct occ *occ = platform_get_drvdata(pdev); struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ); + device_remove_bin_file(occ->bus_dev, &bin_attr_ffdc); + ctx->sbe = NULL; occ_shutdown(occ); + if (ctx->ffdc) + kvfree(ctx->ffdc); + return 0; } -- cgit v1.2.3-70-g09d2 From 9a93de620e0a113b5f18916f58e1c80aad2f612b Mon Sep 17 00:00:00 2001 From: Eddie James Date: Tue, 19 Oct 2021 16:17:48 -0500 Subject: docs: ABI: testing: Document the SBEFIFO timeout interface Add documentation for the new sysfs entry that indicates whether or not the SBE has timed out. Signed-off-by: Eddie James Link: https://lore.kernel.org/r/20211019211749.38059-2-eajames@linux.ibm.com Signed-off-by: Joel Stanley --- Documentation/ABI/testing/sysfs-bus-fsi-devices-sbefifo | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-fsi-devices-sbefifo diff --git a/Documentation/ABI/testing/sysfs-bus-fsi-devices-sbefifo b/Documentation/ABI/testing/sysfs-bus-fsi-devices-sbefifo new file mode 100644 index 000000000000..531fe9d6b40a --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-fsi-devices-sbefifo @@ -0,0 +1,10 @@ +What: /sys/bus/fsi/devices/XX.XX.00:06/sbefifoX/timeout +KernelVersion: 5.15 +Contact: eajames@linux.ibm.com +Description: + Indicates whether or not this SBE device has experienced a + timeout; i.e. the SBE did not respond within the time allotted + by the driver. A value of 1 indicates that a timeout has + ocurred and no transfers have completed since the timeout. A + value of 0 indicates that no timeout has ocurred, or if one + has, more recent transfers have completed successful. -- cgit v1.2.3-70-g09d2 From 826280348ec68cefeb7c3cc3689f6cafcd31c832 Mon Sep 17 00:00:00 2001 From: Eddie James Date: Tue, 19 Oct 2021 16:17:49 -0500 Subject: fsi: sbefifo: Add sysfs file indicating a timeout error The SBEFIFO timeout error requires special handling in userspace to do recovery operations. Add a sysfs file to indicate a timeout error, and notify pollers when a timeout occurs. This will be used by the openpower-occ-control application. Signed-off-by: Eddie James Reviewed-by: Joel Stanley Link: https://lore.kernel.org/r/20211019211749.38059-3-eajames@linux.ibm.com Signed-off-by: Joel Stanley --- drivers/fsi/fsi-sbefifo.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c index 84cb965bfed5..b414ab4431ef 100644 --- a/drivers/fsi/fsi-sbefifo.c +++ b/drivers/fsi/fsi-sbefifo.c @@ -124,6 +124,7 @@ struct sbefifo { bool broken; bool dead; bool async_ffdc; + bool timed_out; }; struct sbefifo_user { @@ -136,6 +137,14 @@ struct sbefifo_user { static DEFINE_MUTEX(sbefifo_ffdc_mutex); +static ssize_t timeout_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sbefifo *sbefifo = container_of(dev, struct sbefifo, dev); + + return sysfs_emit(buf, "%d\n", sbefifo->timed_out ? 1 : 0); +} +static DEVICE_ATTR_RO(timeout); static void __sbefifo_dump_ffdc(struct device *dev, const __be32 *ffdc, size_t ffdc_sz, bool internal) @@ -462,11 +471,14 @@ static int sbefifo_wait(struct sbefifo *sbefifo, bool up, break; } if (!ready) { + sysfs_notify(&sbefifo->dev.kobj, NULL, dev_attr_timeout.attr.name); + sbefifo->timed_out = true; dev_err(dev, "%s FIFO Timeout ! status=%08x\n", up ? "UP" : "DOWN", sts); return -ETIMEDOUT; } dev_vdbg(dev, "End of wait status: %08x\n", sts); + sbefifo->timed_out = false; *status = sts; return 0; @@ -993,6 +1005,8 @@ static int sbefifo_probe(struct device *dev) child_name); } + device_create_file(&sbefifo->dev, &dev_attr_timeout); + return 0; err_free_minor: fsi_free_minor(sbefifo->dev.devt); @@ -1018,6 +1032,8 @@ static int sbefifo_remove(struct device *dev) dev_dbg(dev, "Removing sbefifo device...\n"); + device_remove_file(&sbefifo->dev, &dev_attr_timeout); + mutex_lock(&sbefifo->lock); sbefifo->dead = true; mutex_unlock(&sbefifo->lock); -- cgit v1.2.3-70-g09d2 From 7cc2f34e1f4da07c791737cc6b3d965b31815ea0 Mon Sep 17 00:00:00 2001 From: Eddie James Date: Tue, 3 Aug 2021 16:30:16 -0500 Subject: fsi: sbefifo: Use interruptible mutex locking Some SBE operations have extremely large responses and can require several minutes to process the response. During this time, the device lock must be held. If another process attempts an operation, it will wait for the mutex for longer than the kernel hung task watchdog allows. Therefore, use the interruptible function to lock the mutex. Signed-off-by: Eddie James Reviewed-by: Joel Stanley Link: https://lore.kernel.org/r/20210803213016.44739-1-eajames@linux.ibm.com Signed-off-by: Joel Stanley --- drivers/fsi/fsi-sbefifo.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c index b414ab4431ef..52328adef643 100644 --- a/drivers/fsi/fsi-sbefifo.c +++ b/drivers/fsi/fsi-sbefifo.c @@ -752,7 +752,9 @@ int sbefifo_submit(struct device *dev, const __be32 *command, size_t cmd_len, iov_iter_kvec(&resp_iter, WRITE, &resp_iov, 1, rbytes); /* Perform the command */ - mutex_lock(&sbefifo->lock); + rc = mutex_lock_interruptible(&sbefifo->lock); + if (rc) + return rc; rc = __sbefifo_submit(sbefifo, command, cmd_len, &resp_iter); mutex_unlock(&sbefifo->lock); @@ -832,7 +834,9 @@ static ssize_t sbefifo_user_read(struct file *file, char __user *buf, iov_iter_init(&resp_iter, WRITE, &resp_iov, 1, len); /* Perform the command */ - mutex_lock(&sbefifo->lock); + rc = mutex_lock_interruptible(&sbefifo->lock); + if (rc) + goto bail; rc = __sbefifo_submit(sbefifo, user->pending_cmd, cmd_len, &resp_iter); mutex_unlock(&sbefifo->lock); if (rc < 0) @@ -887,7 +891,9 @@ static ssize_t sbefifo_user_write(struct file *file, const char __user *buf, user->pending_len = 0; /* Trigger reset request */ - mutex_lock(&sbefifo->lock); + rc = mutex_lock_interruptible(&sbefifo->lock); + if (rc) + goto bail; rc = sbefifo_request_reset(user->sbefifo); mutex_unlock(&sbefifo->lock); if (rc == 0) -- cgit v1.2.3-70-g09d2 From 714f1af14bb0c829a33ad68c4ef7d622618ce702 Mon Sep 17 00:00:00 2001 From: Ye Guojin Date: Fri, 22 Oct 2021 09:06:04 +0000 Subject: misc: enclosure: replace snprintf in show functions with sysfs_emit coccicheck complains about the use of snprintf() in sysfs show functions: WARNING use scnprintf or sprintf Use sysfs_emit instead of scnprintf or sprintf makes more sense. Reported-by: Zeal Robot Signed-off-by: Ye Guojin Link: https://lore.kernel.org/r/20211022090604.1065367-1-ye.guojin@zte.com.cn Signed-off-by: Greg Kroah-Hartman --- drivers/misc/enclosure.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index f950d0155876..1b010d9267c9 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -426,7 +426,7 @@ static ssize_t components_show(struct device *cdev, { struct enclosure_device *edev = to_enclosure_device(cdev); - return snprintf(buf, 40, "%d\n", edev->components); + return sysfs_emit(buf, "%d\n", edev->components); } static DEVICE_ATTR_RO(components); @@ -481,7 +481,7 @@ static ssize_t get_component_fault(struct device *cdev, if (edev->cb->get_fault) edev->cb->get_fault(edev, ecomp); - return snprintf(buf, 40, "%d\n", ecomp->fault); + return sysfs_emit(buf, "%d\n", ecomp->fault); } static ssize_t set_component_fault(struct device *cdev, @@ -505,7 +505,7 @@ static ssize_t get_component_status(struct device *cdev, if (edev->cb->get_status) edev->cb->get_status(edev, ecomp); - return snprintf(buf, 40, "%s\n", enclosure_status[ecomp->status]); + return sysfs_emit(buf, "%s\n", enclosure_status[ecomp->status]); } static ssize_t set_component_status(struct device *cdev, @@ -539,7 +539,7 @@ static ssize_t get_component_active(struct device *cdev, if (edev->cb->get_active) edev->cb->get_active(edev, ecomp); - return snprintf(buf, 40, "%d\n", ecomp->active); + return sysfs_emit(buf, "%d\n", ecomp->active); } static ssize_t set_component_active(struct device *cdev, @@ -563,7 +563,7 @@ static ssize_t get_component_locate(struct device *cdev, if (edev->cb->get_locate) edev->cb->get_locate(edev, ecomp); - return snprintf(buf, 40, "%d\n", ecomp->locate); + return sysfs_emit(buf, "%d\n", ecomp->locate); } static ssize_t set_component_locate(struct device *cdev, @@ -593,7 +593,7 @@ static ssize_t get_component_power_status(struct device *cdev, if (ecomp->power_status == -1) return (edev->cb->get_power_status) ? -EIO : -ENOTTY; - return snprintf(buf, 40, "%s\n", ecomp->power_status ? "on" : "off"); + return sysfs_emit(buf, "%s\n", ecomp->power_status ? "on" : "off"); } static ssize_t set_component_power_status(struct device *cdev, @@ -623,7 +623,7 @@ static ssize_t get_component_type(struct device *cdev, { struct enclosure_component *ecomp = to_enclosure_component(cdev); - return snprintf(buf, 40, "%s\n", enclosure_type[ecomp->type]); + return sysfs_emit(buf, "%s\n", enclosure_type[ecomp->type]); } static ssize_t get_component_slot(struct device *cdev, @@ -638,7 +638,7 @@ static ssize_t get_component_slot(struct device *cdev, else slot = ecomp->number; - return snprintf(buf, 40, "%d\n", slot); + return sysfs_emit(buf, "%d\n", slot); } static DEVICE_ATTR(fault, S_IRUGO | S_IWUSR, get_component_fault, -- cgit v1.2.3-70-g09d2 From 7b473ae754fec86cfa44f05f0882d1329d244e6c Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Fri, 22 Oct 2021 12:56:56 -0700 Subject: iio: frequency: adrf6780: Fix adrf6780_spi_{read,write}() Clang warns: drivers/iio/frequency/adrf6780.c:117:1: error: all paths through this function will call itself [-Werror,-Winfinite-recursion] { ^ drivers/iio/frequency/adrf6780.c:138:1: error: all paths through this function will call itself [-Werror,-Winfinite-recursion] { ^ 2 errors generated. The underscore variants should be used here. Link: https://github.com/ClangBuiltLinux/linux/issues/1490 Fixes: 63aaf6d06d87 ("iio: frequency: adrf6780: add support for ADRF6780") Acked-by: Jonathan Cameron Signed-off-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211022195656.1513147-1-nathan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/iio/frequency/adrf6780.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/frequency/adrf6780.c b/drivers/iio/frequency/adrf6780.c index abe8b30fceca..8255ffd174f6 100644 --- a/drivers/iio/frequency/adrf6780.c +++ b/drivers/iio/frequency/adrf6780.c @@ -118,7 +118,7 @@ static int adrf6780_spi_read(struct adrf6780_state *st, unsigned int reg, int ret; mutex_lock(&st->lock); - ret = adrf6780_spi_read(st, reg, val); + ret = __adrf6780_spi_read(st, reg, val); mutex_unlock(&st->lock); return ret; @@ -139,7 +139,7 @@ static int adrf6780_spi_write(struct adrf6780_state *st, unsigned int reg, int ret; mutex_lock(&st->lock); - ret = adrf6780_spi_write(st, reg, val); + ret = __adrf6780_spi_write(st, reg, val); mutex_unlock(&st->lock); return ret; -- cgit v1.2.3-70-g09d2 From 149538cd55cad138b6eaa4dc4735c28f53eb5d32 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 22 Oct 2021 15:38:25 -0700 Subject: selftests/lkdtm: Add way to repeat a test Some LKDTM tests need to be run more than once (usually to setup and then later trigger). Until now, the only case was the SOFT_LOCKUP test, which wasn't useful to run in the bulk selftests. The coming stack canary checking needs to run twice, so support this with a new test output prefix "repeat". Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20211022223826.330653-2-keescook@chromium.org Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/lkdtm/run.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/lkdtm/run.sh b/tools/testing/selftests/lkdtm/run.sh index e95e79bd3126..95e904959207 100755 --- a/tools/testing/selftests/lkdtm/run.sh +++ b/tools/testing/selftests/lkdtm/run.sh @@ -56,8 +56,14 @@ if echo "$test" | grep -q '^#' ; then fi # If no expected output given, assume an Oops with back trace is success. +repeat=1 if [ -z "$expect" ]; then expect="call trace:" +else + if echo "$expect" | grep -q '^repeat:' ; then + repeat=$(echo "$expect" | cut -d' ' -f1 | cut -d: -f2) + expect=$(echo "$expect" | cut -d' ' -f2-) + fi fi # Prepare log for report checking @@ -83,7 +89,9 @@ dmesg > "$DMESG" # the signal that killed the subprocess, we must ignore the failure and # continue. However we don't silence stderr since there might be other # useful details reported there in the case of other unexpected conditions. -echo "$test" | cat >"$TRIGGER" || true +for i in $(seq 1 $repeat); do + echo "$test" | cat >"$TRIGGER" || true +done # Record and dump the results dmesg | comm --nocheck-order -13 "$DMESG" - > "$LOG" || true -- cgit v1.2.3-70-g09d2 From d46e58ef776bc6548b17b563ec4a8ed30c6643da Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 22 Oct 2021 15:38:26 -0700 Subject: lkdtm/bugs: Check that a per-task stack canary exists Introduce REPORT_STACK_CANARY to check for differing stack canaries between two processes (i.e. that an architecture is correctly implementing per-task stack canaries), using the task_struct canary as the hint to locate in the stack. Requires that one of the processes being tested not be pid 1. Cc: Ard Biesheuvel Cc: Greg Kroah-Hartman Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20211022223826.330653-3-keescook@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/lkdtm/bugs.c | 77 +++++++++++++++++++++++++++++++++ drivers/misc/lkdtm/core.c | 1 + drivers/misc/lkdtm/lkdtm.h | 1 + tools/testing/selftests/lkdtm/config | 1 + tools/testing/selftests/lkdtm/tests.txt | 1 + 5 files changed, 81 insertions(+) diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c index 4282b625200f..f4cb94a9aa9c 100644 --- a/drivers/misc/lkdtm/bugs.c +++ b/drivers/misc/lkdtm/bugs.c @@ -151,6 +151,83 @@ void lkdtm_REPORT_STACK(void) pr_info("Stack offset: %d\n", (int)(stack_addr - (uintptr_t)&magic)); } +static pid_t stack_canary_pid; +static unsigned long stack_canary; +static unsigned long stack_canary_offset; + +static noinline void __lkdtm_REPORT_STACK_CANARY(void *stack) +{ + int i = 0; + pid_t pid = task_pid_nr(current); + unsigned long *canary = (unsigned long *)stack; + unsigned long current_offset = 0, init_offset = 0; + + /* Do our best to find the canary in a 16 word window ... */ + for (i = 1; i < 16; i++) { + canary = (unsigned long *)stack + i; +#ifdef CONFIG_STACKPROTECTOR + if (*canary == current->stack_canary) + current_offset = i; + if (*canary == init_task.stack_canary) + init_offset = i; +#endif + } + + if (current_offset == 0) { + /* + * If the canary doesn't match what's in the task_struct, + * we're either using a global canary or the stack frame + * layout changed. + */ + if (init_offset != 0) { + pr_err("FAIL: global stack canary found at offset %ld (canary for pid %d matches init_task's)!\n", + init_offset, pid); + } else { + pr_warn("FAIL: did not correctly locate stack canary :(\n"); + pr_expected_config(CONFIG_STACKPROTECTOR); + } + + return; + } else if (init_offset != 0) { + pr_warn("WARNING: found both current and init_task canaries nearby?!\n"); + } + + canary = (unsigned long *)stack + current_offset; + if (stack_canary_pid == 0) { + stack_canary = *canary; + stack_canary_pid = pid; + stack_canary_offset = current_offset; + pr_info("Recorded stack canary for pid %d at offset %ld\n", + stack_canary_pid, stack_canary_offset); + } else if (pid == stack_canary_pid) { + pr_warn("ERROR: saw pid %d again -- please use a new pid\n", pid); + } else { + if (current_offset != stack_canary_offset) { + pr_warn("ERROR: canary offset changed from %ld to %ld!?\n", + stack_canary_offset, current_offset); + return; + } + + if (*canary == stack_canary) { + pr_warn("FAIL: canary identical for pid %d and pid %d at offset %ld!\n", + stack_canary_pid, pid, current_offset); + } else { + pr_info("ok: stack canaries differ between pid %d and pid %d at offset %ld.\n", + stack_canary_pid, pid, current_offset); + /* Reset the test. */ + stack_canary_pid = 0; + } + } +} + +void lkdtm_REPORT_STACK_CANARY(void) +{ + /* Use default char array length that triggers stack protection. */ + char data[8] __aligned(sizeof(void *)) = { }; + + __lkdtm_REPORT_STACK_CANARY((void *)&data); +} + void lkdtm_UNALIGNED_LOAD_STORE_WRITE(void) { static u8 data[5] __attribute__((aligned(4))) = {1, 2, 3, 4, 5}; diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c index fe6fd34b8caf..609d9ee2acc0 100644 --- a/drivers/misc/lkdtm/core.c +++ b/drivers/misc/lkdtm/core.c @@ -111,6 +111,7 @@ static const struct crashtype crashtypes[] = { CRASHTYPE(CORRUPT_STACK), CRASHTYPE(CORRUPT_STACK_STRONG), CRASHTYPE(REPORT_STACK), + CRASHTYPE(REPORT_STACK_CANARY), CRASHTYPE(CORRUPT_LIST_ADD), CRASHTYPE(CORRUPT_LIST_DEL), CRASHTYPE(STACK_GUARD_PAGE_LEADING), diff --git a/drivers/misc/lkdtm/lkdtm.h b/drivers/misc/lkdtm/lkdtm.h index c212a253edde..d6137c70ebbe 100644 --- a/drivers/misc/lkdtm/lkdtm.h +++ b/drivers/misc/lkdtm/lkdtm.h @@ -69,6 +69,7 @@ void lkdtm_EXHAUST_STACK(void); void lkdtm_CORRUPT_STACK(void); void lkdtm_CORRUPT_STACK_STRONG(void); void lkdtm_REPORT_STACK(void); +void lkdtm_REPORT_STACK_CANARY(void); void lkdtm_UNALIGNED_LOAD_STORE_WRITE(void); void lkdtm_SOFTLOCKUP(void); void lkdtm_HARDLOCKUP(void); diff --git a/tools/testing/selftests/lkdtm/config b/tools/testing/selftests/lkdtm/config index 38edea25631b..a26a3fa9e925 100644 --- a/tools/testing/selftests/lkdtm/config +++ b/tools/testing/selftests/lkdtm/config @@ -8,3 +8,4 @@ CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT=y CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y CONFIG_UBSAN_BOUNDS=y CONFIG_UBSAN_TRAP=y +CONFIG_STACKPROTECTOR_STRONG=y diff --git a/tools/testing/selftests/lkdtm/tests.txt b/tools/testing/selftests/lkdtm/tests.txt index 09f7bfa383cc..6b36b7f5dcf9 100644 --- a/tools/testing/selftests/lkdtm/tests.txt +++ b/tools/testing/selftests/lkdtm/tests.txt @@ -12,6 +12,7 @@ CORRUPT_LIST_ADD list_add corruption CORRUPT_LIST_DEL list_del corruption STACK_GUARD_PAGE_LEADING STACK_GUARD_PAGE_TRAILING +REPORT_STACK_CANARY repeat:2 ok: stack canaries differ UNSET_SMEP pinned CR4 bits changed: DOUBLE_FAULT CORRUPT_PAC -- cgit v1.2.3-70-g09d2 From 16b0314aa746be6c84c0bc6eca9dde0dce2e99df Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 10 Oct 2021 14:46:28 +0200 Subject: dma-buf: move dma-buf symbols into the DMA_BUF module namespace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to better track where in the kernel the dma-buf code is used, put the symbols in the namespace DMA_BUF and modify all users of the symbols to properly import the namespace to not break the build at the same time. Now the output of modinfo shows the use of these symbols, making it easier to watch for users over time: $ modinfo drivers/misc/fastrpc.ko | grep import import_ns: DMA_BUF Cc: "Pan, Xinhui" Cc: David Airlie Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Thomas Zimmermann Cc: Mauro Carvalho Chehab Cc: dri-devel@lists.freedesktop.org Acked-by: Daniel Vetter Acked-by: Christian König Acked-by: Arnd Bergmann Acked-by: Sumit Semwal Acked-by: Alex Deucher Link: https://lore.kernel.org/r/20211010124628.17691-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/dma-buf/dma-buf.c | 34 +++++++++++----------- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 3 ++ drivers/gpu/drm/armada/armada_gem.c | 2 ++ drivers/gpu/drm/drm_gem_framebuffer_helper.c | 3 ++ drivers/gpu/drm/drm_prime.c | 3 ++ drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c | 3 ++ drivers/gpu/drm/exynos/exynos_drm_gem.c | 3 ++ drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c | 3 ++ drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c | 2 ++ drivers/gpu/drm/tegra/gem.c | 3 ++ drivers/gpu/drm/vmwgfx/ttm_object.c | 3 ++ drivers/infiniband/core/umem_dmabuf.c | 3 ++ drivers/media/common/videobuf2/videobuf2-core.c | 1 + .../media/common/videobuf2/videobuf2-dma-contig.c | 1 + drivers/media/common/videobuf2/videobuf2-dma-sg.c | 1 + drivers/media/common/videobuf2/videobuf2-vmalloc.c | 1 + drivers/misc/fastrpc.c | 1 + drivers/misc/habanalabs/common/memory.c | 2 ++ drivers/staging/media/tegra-vde/dmabuf-cache.c | 3 ++ drivers/tee/tee_shm.c | 3 ++ drivers/virtio/virtio_dma_buf.c | 1 + drivers/xen/gntdev-dmabuf.c | 3 ++ samples/vfio-mdev/mbochs.c | 1 + 23 files changed, 66 insertions(+), 17 deletions(-) diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 63d32261b63f..6c2b5ea828a6 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -610,7 +610,7 @@ err_module: module_put(exp_info->owner); return ERR_PTR(ret); } -EXPORT_SYMBOL_GPL(dma_buf_export); +EXPORT_SYMBOL_NS_GPL(dma_buf_export, DMA_BUF); /** * dma_buf_fd - returns a file descriptor for the given struct dma_buf @@ -634,7 +634,7 @@ int dma_buf_fd(struct dma_buf *dmabuf, int flags) return fd; } -EXPORT_SYMBOL_GPL(dma_buf_fd); +EXPORT_SYMBOL_NS_GPL(dma_buf_fd, DMA_BUF); /** * dma_buf_get - returns the struct dma_buf related to an fd @@ -660,7 +660,7 @@ struct dma_buf *dma_buf_get(int fd) return file->private_data; } -EXPORT_SYMBOL_GPL(dma_buf_get); +EXPORT_SYMBOL_NS_GPL(dma_buf_get, DMA_BUF); /** * dma_buf_put - decreases refcount of the buffer @@ -679,7 +679,7 @@ void dma_buf_put(struct dma_buf *dmabuf) fput(dmabuf->file); } -EXPORT_SYMBOL_GPL(dma_buf_put); +EXPORT_SYMBOL_NS_GPL(dma_buf_put, DMA_BUF); static void mangle_sg_table(struct sg_table *sg_table) { @@ -810,7 +810,7 @@ err_unlock: dma_buf_detach(dmabuf, attach); return ERR_PTR(ret); } -EXPORT_SYMBOL_GPL(dma_buf_dynamic_attach); +EXPORT_SYMBOL_NS_GPL(dma_buf_dynamic_attach, DMA_BUF); /** * dma_buf_attach - Wrapper for dma_buf_dynamic_attach @@ -825,7 +825,7 @@ struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf, { return dma_buf_dynamic_attach(dmabuf, dev, NULL, NULL); } -EXPORT_SYMBOL_GPL(dma_buf_attach); +EXPORT_SYMBOL_NS_GPL(dma_buf_attach, DMA_BUF); static void __unmap_dma_buf(struct dma_buf_attachment *attach, struct sg_table *sg_table, @@ -871,7 +871,7 @@ void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach) kfree(attach); } -EXPORT_SYMBOL_GPL(dma_buf_detach); +EXPORT_SYMBOL_NS_GPL(dma_buf_detach, DMA_BUF); /** * dma_buf_pin - Lock down the DMA-buf @@ -901,7 +901,7 @@ int dma_buf_pin(struct dma_buf_attachment *attach) return ret; } -EXPORT_SYMBOL_GPL(dma_buf_pin); +EXPORT_SYMBOL_NS_GPL(dma_buf_pin, DMA_BUF); /** * dma_buf_unpin - Unpin a DMA-buf @@ -922,7 +922,7 @@ void dma_buf_unpin(struct dma_buf_attachment *attach) if (dmabuf->ops->unpin) dmabuf->ops->unpin(attach); } -EXPORT_SYMBOL_GPL(dma_buf_unpin); +EXPORT_SYMBOL_NS_GPL(dma_buf_unpin, DMA_BUF); /** * dma_buf_map_attachment - Returns the scatterlist table of the attachment; @@ -1012,7 +1012,7 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach, #endif /* CONFIG_DMA_API_DEBUG */ return sg_table; } -EXPORT_SYMBOL_GPL(dma_buf_map_attachment); +EXPORT_SYMBOL_NS_GPL(dma_buf_map_attachment, DMA_BUF); /** * dma_buf_unmap_attachment - unmaps and decreases usecount of the buffer;might @@ -1048,7 +1048,7 @@ void dma_buf_unmap_attachment(struct dma_buf_attachment *attach, !IS_ENABLED(CONFIG_DMABUF_MOVE_NOTIFY)) dma_buf_unpin(attach); } -EXPORT_SYMBOL_GPL(dma_buf_unmap_attachment); +EXPORT_SYMBOL_NS_GPL(dma_buf_unmap_attachment, DMA_BUF); /** * dma_buf_move_notify - notify attachments that DMA-buf is moving @@ -1068,7 +1068,7 @@ void dma_buf_move_notify(struct dma_buf *dmabuf) if (attach->importer_ops) attach->importer_ops->move_notify(attach); } -EXPORT_SYMBOL_GPL(dma_buf_move_notify); +EXPORT_SYMBOL_NS_GPL(dma_buf_move_notify, DMA_BUF); /** * DOC: cpu access @@ -1212,7 +1212,7 @@ int dma_buf_begin_cpu_access(struct dma_buf *dmabuf, return ret; } -EXPORT_SYMBOL_GPL(dma_buf_begin_cpu_access); +EXPORT_SYMBOL_NS_GPL(dma_buf_begin_cpu_access, DMA_BUF); /** * dma_buf_end_cpu_access - Must be called after accessing a dma_buf from the @@ -1240,7 +1240,7 @@ int dma_buf_end_cpu_access(struct dma_buf *dmabuf, return ret; } -EXPORT_SYMBOL_GPL(dma_buf_end_cpu_access); +EXPORT_SYMBOL_NS_GPL(dma_buf_end_cpu_access, DMA_BUF); /** @@ -1282,7 +1282,7 @@ int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma, return dmabuf->ops->mmap(dmabuf, vma); } -EXPORT_SYMBOL_GPL(dma_buf_mmap); +EXPORT_SYMBOL_NS_GPL(dma_buf_mmap, DMA_BUF); /** * dma_buf_vmap - Create virtual mapping for the buffer object into kernel @@ -1336,7 +1336,7 @@ out_unlock: mutex_unlock(&dmabuf->lock); return ret; } -EXPORT_SYMBOL_GPL(dma_buf_vmap); +EXPORT_SYMBOL_NS_GPL(dma_buf_vmap, DMA_BUF); /** * dma_buf_vunmap - Unmap a vmap obtained by dma_buf_vmap. @@ -1360,7 +1360,7 @@ void dma_buf_vunmap(struct dma_buf *dmabuf, struct dma_buf_map *map) } mutex_unlock(&dmabuf->lock); } -EXPORT_SYMBOL_GPL(dma_buf_vunmap); +EXPORT_SYMBOL_NS_GPL(dma_buf_vunmap, DMA_BUF); #ifdef CONFIG_DEBUG_FS static int dma_buf_debug_show(struct seq_file *s, void *unused) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 94126dc39688..4f03e0a2953e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -59,6 +60,8 @@ #include "amdgpu_res_cursor.h" #include "bif/bif_4_1_d.h" +MODULE_IMPORT_NS(DMA_BUF); + #define AMDGPU_TTM_VRAM_MAX_DW_READ (size_t)128 static int amdgpu_ttm_backend_bind(struct ttm_device *bdev, diff --git a/drivers/gpu/drm/armada/armada_gem.c b/drivers/gpu/drm/armada/armada_gem.c index 21909642ee4c..711f0cca6f9c 100644 --- a/drivers/gpu/drm/armada/armada_gem.c +++ b/drivers/gpu/drm/armada/armada_gem.c @@ -15,6 +15,8 @@ #include "armada_gem.h" #include "armada_ioctlP.h" +MODULE_IMPORT_NS(DMA_BUF); + static vm_fault_t armada_gem_vm_fault(struct vm_fault *vmf) { struct drm_gem_object *gobj = vmf->vma->vm_private_data; diff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index 3c75d79dbb65..746fd8c73845 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -6,6 +6,7 @@ */ #include +#include #include #include @@ -17,6 +18,8 @@ #include "drm_internal.h" +MODULE_IMPORT_NS(DMA_BUF); + #define AFBC_HEADER_SIZE 16 #define AFBC_TH_LAYOUT_ALIGNMENT 8 #define AFBC_HDR_ALIGN 64 diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index deb23dbec8b5..d8ba95744410 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -39,6 +40,8 @@ #include "drm_internal.h" +MODULE_IMPORT_NS(DMA_BUF); + /** * DOC: overview and lifetime rules * diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c index 6d8bed9c739d..6788ea8490d1 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c @@ -5,10 +5,13 @@ #include #include +#include #include "etnaviv_drv.h" #include "etnaviv_gem.h" +MODULE_IMPORT_NS(DMA_BUF); + static struct lock_class_key etnaviv_prime_lock_class; struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj) diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 4396224227d1..0a0c042a3155 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -17,6 +18,8 @@ #include "exynos_drm_drv.h" #include "exynos_drm_gem.h" +MODULE_IMPORT_NS(DMA_BUF); + static int exynos_drm_alloc_buf(struct exynos_drm_gem *exynos_gem, bool kvmap) { struct drm_device *dev = exynos_gem->base.dev; diff --git a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c index afa34111de02..abb854281347 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c @@ -7,11 +7,14 @@ #include #include #include +#include #include "i915_drv.h" #include "i915_gem_object.h" #include "i915_scatterlist.h" +MODULE_IMPORT_NS(DMA_BUF); + I915_SELFTEST_DECLARE(static bool force_different_devices;) static struct drm_i915_gem_object *dma_buf_to_obj(struct dma_buf *buf) diff --git a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c index f4cde3a169d8..809f86cfc540 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c +++ b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c @@ -11,6 +11,8 @@ #include "omap_drv.h" +MODULE_IMPORT_NS(DMA_BUF); + /* ----------------------------------------------------------------------------- * DMABUF Export */ diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index 6ec598f5d5b3..d38fd7e12b57 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -20,6 +21,8 @@ #include "drm.h" #include "gem.h" +MODULE_IMPORT_NS(DMA_BUF); + static void tegra_bo_put(struct host1x_bo *bo) { struct tegra_bo *obj = host1x_to_tegra_bo(bo); diff --git a/drivers/gpu/drm/vmwgfx/ttm_object.c b/drivers/gpu/drm/vmwgfx/ttm_object.c index 04789b2bb2a2..899945f54dc7 100644 --- a/drivers/gpu/drm/vmwgfx/ttm_object.c +++ b/drivers/gpu/drm/vmwgfx/ttm_object.c @@ -48,8 +48,11 @@ #include #include #include +#include #include "ttm_object.h" +MODULE_IMPORT_NS(DMA_BUF); + /** * struct ttm_object_file * diff --git a/drivers/infiniband/core/umem_dmabuf.c b/drivers/infiniband/core/umem_dmabuf.c index e824baf4640d..2d14929543af 100644 --- a/drivers/infiniband/core/umem_dmabuf.c +++ b/drivers/infiniband/core/umem_dmabuf.c @@ -6,9 +6,12 @@ #include #include #include +#include #include "uverbs.h" +MODULE_IMPORT_NS(DMA_BUF); + int ib_umem_dmabuf_map_pages(struct ib_umem_dmabuf *umem_dmabuf) { struct sg_table *sgt; diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 508ac295eb06..773c68dcd158 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -2978,3 +2978,4 @@ EXPORT_SYMBOL_GPL(vb2_thread_stop); MODULE_DESCRIPTION("Media buffer core framework"); MODULE_AUTHOR("Pawel Osciak , Marek Szyprowski"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(DMA_BUF); diff --git a/drivers/media/common/videobuf2/videobuf2-dma-contig.c b/drivers/media/common/videobuf2/videobuf2-dma-contig.c index a7f61ba85440..9a1a9baca2e4 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-contig.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-contig.c @@ -755,3 +755,4 @@ EXPORT_SYMBOL_GPL(vb2_dma_contig_set_max_seg_size); MODULE_DESCRIPTION("DMA-contig memory handling routines for videobuf2"); MODULE_AUTHOR("Pawel Osciak "); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(DMA_BUF); diff --git a/drivers/media/common/videobuf2/videobuf2-dma-sg.c b/drivers/media/common/videobuf2/videobuf2-dma-sg.c index c5b06a509566..db90ebb8950f 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-sg.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-sg.c @@ -666,3 +666,4 @@ EXPORT_SYMBOL_GPL(vb2_dma_sg_memops); MODULE_DESCRIPTION("dma scatter/gather memory handling routines for videobuf2"); MODULE_AUTHOR("Andrzej Pietrasiewicz"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(DMA_BUF); diff --git a/drivers/media/common/videobuf2/videobuf2-vmalloc.c b/drivers/media/common/videobuf2/videobuf2-vmalloc.c index 83f95258ec8c..fa983897d0e9 100644 --- a/drivers/media/common/videobuf2/videobuf2-vmalloc.c +++ b/drivers/media/common/videobuf2/videobuf2-vmalloc.c @@ -444,3 +444,4 @@ EXPORT_SYMBOL_GPL(vb2_vmalloc_memops); MODULE_DESCRIPTION("vmalloc memory handling routines for videobuf2"); MODULE_AUTHOR("Pawel Osciak "); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(DMA_BUF); diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 3b82d8ebe780..39aca7753719 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -1767,3 +1767,4 @@ static void fastrpc_exit(void) module_exit(fastrpc_exit); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(DMA_BUF); diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c index 504973330e2e..9bd626a00de3 100644 --- a/drivers/misc/habanalabs/common/memory.c +++ b/drivers/misc/habanalabs/common/memory.c @@ -13,6 +13,8 @@ #include #include +MODULE_IMPORT_NS(DMA_BUF); + #define HL_MMU_DEBUG 0 /* use small pages for supporting non-pow2 (32M/40M/48M) DRAM phys page sizes */ diff --git a/drivers/staging/media/tegra-vde/dmabuf-cache.c b/drivers/staging/media/tegra-vde/dmabuf-cache.c index a93b317885bf..a98d03419b8f 100644 --- a/drivers/staging/media/tegra-vde/dmabuf-cache.c +++ b/drivers/staging/media/tegra-vde/dmabuf-cache.c @@ -12,9 +12,12 @@ #include #include #include +#include #include "vde.h" +MODULE_IMPORT_NS(DMA_BUF); + struct tegra_vde_cache_entry { enum dma_data_direction dma_dir; struct dma_buf_attachment *a; diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c index 8a9384a64f3e..8a8deb95e918 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -10,8 +10,11 @@ #include #include #include +#include #include "tee_private.h" +MODULE_IMPORT_NS(DMA_BUF); + static void release_registered_pages(struct tee_shm *shm) { if (shm->pages) { diff --git a/drivers/virtio/virtio_dma_buf.c b/drivers/virtio/virtio_dma_buf.c index 5127a2f0c986..2521a75009c3 100644 --- a/drivers/virtio/virtio_dma_buf.c +++ b/drivers/virtio/virtio_dma_buf.c @@ -86,3 +86,4 @@ int virtio_dma_buf_get_uuid(struct dma_buf *dma_buf, EXPORT_SYMBOL(virtio_dma_buf_get_uuid); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(DMA_BUF); diff --git a/drivers/xen/gntdev-dmabuf.c b/drivers/xen/gntdev-dmabuf.c index 4c13cbc99896..12e380db7f55 100644 --- a/drivers/xen/gntdev-dmabuf.c +++ b/drivers/xen/gntdev-dmabuf.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -21,6 +22,8 @@ #include "gntdev-common.h" #include "gntdev-dmabuf.h" +MODULE_IMPORT_NS(DMA_BUF); + #ifndef GRANT_INVALID_REF /* * Note on usage of grant reference 0 as invalid grant reference: diff --git a/samples/vfio-mdev/mbochs.c b/samples/vfio-mdev/mbochs.c index c313ab4d1f4e..a83be6cd162f 100644 --- a/samples/vfio-mdev/mbochs.c +++ b/samples/vfio-mdev/mbochs.c @@ -1493,5 +1493,6 @@ static void __exit mbochs_dev_exit(void) mbochs_class = NULL; } +MODULE_IMPORT_NS(DMA_BUF); module_init(mbochs_dev_init) module_exit(mbochs_dev_exit) -- cgit v1.2.3-70-g09d2 From 0b7c7ebe0f603dc4740685c23339e73519490ad8 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 27 Sep 2021 14:48:28 +0800 Subject: dt-bindings: phy: qcom,qmp: Add QCM2290 USB3 PHY Add support for USB3 PHY found on Qualcomm QCM2290 SoC. Signed-off-by: Shawn Guo Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20210927064829.5752-2-shawn.guo@linaro.org Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/qcom,qmp-phy.yaml | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml index a167b5c1ae17..647b45cfec84 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml @@ -27,6 +27,7 @@ properties: - qcom,msm8998-qmp-pcie-phy - qcom,msm8998-qmp-ufs-phy - qcom,msm8998-qmp-usb3-phy + - qcom,qcm2290-qmp-usb3-phy - qcom,sc7180-qmp-usb3-phy - qcom,sc8180x-qmp-pcie-phy - qcom,sc8180x-qmp-ufs-phy @@ -414,6 +415,32 @@ allOf: items: - const: phy - const: common + - if: + properties: + compatible: + contains: + enum: + - qcom,qcm2290-qmp-usb3-phy + then: + properties: + clocks: + items: + - description: Phy config clock. + - description: 19.2 MHz ref clk. + - description: Phy common block aux clock. + clock-names: + items: + - const: cfg_ahb + - const: ref + - const: com_aux + resets: + items: + - description: phy_phy reset. + - description: reset of phy block. + reset-names: + items: + - const: phy_phy + - const: phy examples: - | -- cgit v1.2.3-70-g09d2 From 8abe5e778b2c236c053d643435a847cf151231e9 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 27 Sep 2021 14:48:29 +0800 Subject: phy: qcom-qmp: Add QCM2290 USB3 PHY support Enable QCM2290 USB3 PHY support by adding the qmp_phy_cfg data which are taken from downstream kernel. Signed-off-by: Shawn Guo Link: https://lore.kernel.org/r/20210927064829.5752-3-shawn.guo@linaro.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp.c | 143 ++++++++++++++++++++++++++++++++++++ drivers/phy/qualcomm/phy-qcom-qmp.h | 2 + 2 files changed, 145 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 084e3d96264e..32123a60be97 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -135,6 +135,8 @@ enum qphy_reg_layout { QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR, QPHY_PCS_LFPS_RXTERM_IRQ_STATUS, QPHY_PCS_POWER_DOWN_CONTROL, + /* PCS_MISC registers */ + QPHY_PCS_MISC_TYPEC_CTRL, /* Keep last to ensure regs_layout arrays are properly initialized */ QPHY_LAYOUT_SIZE }; @@ -229,6 +231,16 @@ static const unsigned int sm8350_usb3_uniphy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x1014, }; +static const unsigned int qcm2290_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = 0x00, + [QPHY_PCS_POWER_DOWN_CONTROL] = 0x04, + [QPHY_START_CTRL] = 0x08, + [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0xd8, + [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0xdc, + [QPHY_PCS_STATUS] = 0x174, + [QPHY_PCS_MISC_TYPEC_CTRL] = 0x00, +}; + static const unsigned int sdm845_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_START_CTRL] = 0x00, [QPHY_PCS_READY_STATUS] = 0x160, @@ -2761,6 +2773,99 @@ static const struct qmp_phy_init_tbl sm8350_usb3_uniphy_pcs_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), }; +static const struct qmp_phy_init_tbl qcm2290_usb3_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x06), + QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0xde), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_INITVAL, 0x80), + QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CTRL_BY_PSM, 0x01), +}; + +static const struct qmp_phy_init_tbl qcm2290_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0xc6), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x00), +}; + +static const struct qmp_phy_init_tbl qcm2290_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FO_GAIN, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_VGA_CAL_CNTRL2, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_ENABLES, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x00), +}; + +static const struct qmp_phy_init_tbl qcm2290_usb3_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x17), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x85), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x88), +}; + struct qmp_phy; /* struct qmp_phy_cfg - per-PHY initialization config */ @@ -2995,6 +3100,10 @@ static const char * const qmp_v4_sdx55_usbphy_clk_l[] = { "aux", "cfg_ahb", "ref" }; +static const char * const qcm2290_usb3phy_clk_l[] = { + "cfg_ahb", "ref", "com_aux", +}; + /* list of resets */ static const char * const msm8996_pciephy_reset_l[] = { "phy", "common", "cfg", @@ -3008,6 +3117,10 @@ static const char * const sc7180_usb3phy_reset_l[] = { "phy", }; +static const char * const qcm2290_usb3phy_reset_l[] = { + "phy_phy", "phy", +}; + static const char * const sdm845_pciephy_reset_l[] = { "phy", }; @@ -3974,6 +4087,33 @@ static const struct qmp_phy_cfg sm8350_usb3_uniphy_cfg = { .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, }; +static const struct qmp_phy_cfg qcm2290_usb3phy_cfg = { + .type = PHY_TYPE_USB3, + .nlanes = 1, + + .serdes_tbl = qcm2290_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(qcm2290_usb3_serdes_tbl), + .tx_tbl = qcm2290_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(qcm2290_usb3_tx_tbl), + .rx_tbl = qcm2290_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(qcm2290_usb3_rx_tbl), + .pcs_tbl = qcm2290_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(qcm2290_usb3_pcs_tbl), + .clk_list = qcm2290_usb3phy_clk_l, + .num_clks = ARRAY_SIZE(qcm2290_usb3phy_clk_l), + .reset_list = qcm2290_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(qcm2290_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qcm2290_usb3phy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, + + .is_dual_lane_phy = true, +}; + static void qcom_qmp_phy_configure_lane(void __iomem *base, const unsigned int *regs, const struct qmp_phy_init_tbl tbl[], @@ -5605,6 +5745,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { }, { .compatible = "qcom,sm8350-qmp-usb3-uni-phy", .data = &sm8350_usb3_uniphy_cfg, + }, { + .compatible = "qcom,qcm2290-qmp-usb3-phy", + .data = &qcm2290_usb3phy_cfg, }, { }, }; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index bebeac2c091c..e15f461065bb 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -169,6 +169,7 @@ #define QSERDES_COM_BIAS_EN_CTRL_BY_PSM 0x0a8 #define QSERDES_COM_SYSCLK_EN_SEL 0x0ac #define QSERDES_COM_RESETSM_CNTRL 0x0b4 +#define QSERDES_COM_RESETSM_CNTRL2 0x0b8 #define QSERDES_COM_RESTRIM_CTRL 0x0bc #define QSERDES_COM_RESCODE_DIV_NUM 0x0c4 #define QSERDES_COM_LOCK_CMP_EN 0x0c8 @@ -181,6 +182,7 @@ #define QSERDES_COM_DIV_FRAC_START1_MODE1 0x0e8 #define QSERDES_COM_DIV_FRAC_START2_MODE1 0x0ec #define QSERDES_COM_DIV_FRAC_START3_MODE1 0x0f0 +#define QSERDES_COM_INTEGLOOP_INITVAL 0x100 #define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x108 #define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x10c #define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 0x110 -- cgit v1.2.3-70-g09d2 From bf7ffcd0069d30e2e7ba2b827f08c89f471cd1f3 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Thu, 23 Sep 2021 02:35:48 +0300 Subject: phy: qcom-qusb2: Fix a memory leak on probe On success nvmem_cell_read() returns a pointer to a dynamically allocated buffer, and therefore it shall be freed after usage. The issue is reported by kmemleak: # cat /sys/kernel/debug/kmemleak unreferenced object 0xffff3b3803e4b280 (size 128): comm "kworker/u16:1", pid 107, jiffies 4294892861 (age 94.120s) hex dump (first 32 bytes): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [<000000007739afdc>] __kmalloc+0x27c/0x41c [<0000000071c0fbf8>] nvmem_cell_read+0x40/0xe0 [<00000000e803ef1f>] qusb2_phy_init+0x258/0x5bc [<00000000fc81fcfa>] phy_init+0x70/0x110 [<00000000e3d48a57>] dwc3_core_soft_reset+0x4c/0x234 [<0000000027d1dbd4>] dwc3_core_init+0x68/0x990 [<000000001965faf9>] dwc3_probe+0x4f4/0x730 [<000000002f7617ca>] platform_probe+0x74/0xf0 [<00000000a2576cac>] really_probe+0xc4/0x470 [<00000000bc77f2c5>] __driver_probe_device+0x11c/0x190 [<00000000130db71f>] driver_probe_device+0x48/0x110 [<0000000019f36c2b>] __device_attach_driver+0xa4/0x140 [<00000000e5812ff7>] bus_for_each_drv+0x84/0xe0 [<00000000f4bac574>] __device_attach+0xe4/0x1c0 [<00000000d3beb631>] device_initial_probe+0x20/0x30 [<000000008019b9db>] bus_probe_device+0xa4/0xb0 Fixes: ca04d9d3e1b1 ("phy: qcom-qusb2: New driver for QUSB2 PHY on Qcom chips") Signed-off-by: Vladimir Zapolskiy Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20210922233548.2150244-1-vladimir.zapolskiy@linaro.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qusb2.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c index 7a8e4333babe..032d02bf50c5 100644 --- a/drivers/phy/qualcomm/phy-qcom-qusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c @@ -561,7 +561,7 @@ static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy) { struct device *dev = &qphy->phy->dev; const struct qusb2_phy_cfg *cfg = qphy->cfg; - u8 *val; + u8 *val, hstx_trim; /* efuse register is optional */ if (!qphy->cell) @@ -575,7 +575,13 @@ static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy) * set while configuring the phy. */ val = nvmem_cell_read(qphy->cell, NULL); - if (IS_ERR(val) || !val[0]) { + if (IS_ERR(val)) { + dev_dbg(dev, "failed to read a valid hs-tx trim value\n"); + return; + } + hstx_trim = val[0]; + kfree(val); + if (!hstx_trim) { dev_dbg(dev, "failed to read a valid hs-tx trim value\n"); return; } @@ -583,12 +589,10 @@ static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy) /* Fused TUNE1/2 value is the higher nibble only */ if (cfg->update_tune1_with_efuse) qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE1], - val[0] << HSTX_TRIM_SHIFT, - HSTX_TRIM_MASK); + hstx_trim << HSTX_TRIM_SHIFT, HSTX_TRIM_MASK); else qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE2], - val[0] << HSTX_TRIM_SHIFT, - HSTX_TRIM_MASK); + hstx_trim << HSTX_TRIM_SHIFT, HSTX_TRIM_MASK); } static int qusb2_phy_set_mode(struct phy *phy, -- cgit v1.2.3-70-g09d2 From 4ff6b676ba53c02a4ab82181265678f64272641c Mon Sep 17 00:00:00 2001 From: Johan Jonker Date: Sat, 28 Aug 2021 13:12:17 +0200 Subject: dt-bindings: phy: rockchip: remove usb-phy fallback string for rk3066a/rk3188 With the conversion of rockchip-usb-phy.yaml a long time used fallback string for rk3066a/rk3188 was added. The linux driver doesn't do much with the GRF phy address range, however the u-boot driver rockchip_usb2_phy.c does. The bits in GRF_UOC0_CON2 for rk3066a/rk3188 and rk3288 for example don't match. Remove the usb-phy fallback string for rk3066a/rk3188 to prevent possible strange side effects. Signed-off-by: Johan Jonker Acked-by: Rob Herring Link: https://lore.kernel.org/r/20210828111218.10026-1-jbx6244@gmail.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/rockchip-usb-phy.yaml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/rockchip-usb-phy.yaml b/Documentation/devicetree/bindings/phy/rockchip-usb-phy.yaml index f0fc8275dcd0..499d55131aa8 100644 --- a/Documentation/devicetree/bindings/phy/rockchip-usb-phy.yaml +++ b/Documentation/devicetree/bindings/phy/rockchip-usb-phy.yaml @@ -11,13 +11,10 @@ maintainers: properties: compatible: - oneOf: - - const: rockchip,rk3288-usb-phy - - items: - - enum: - - rockchip,rk3066a-usb-phy - - rockchip,rk3188-usb-phy - - const: rockchip,rk3288-usb-phy + enum: + - rockchip,rk3066a-usb-phy + - rockchip,rk3188-usb-phy + - rockchip,rk3288-usb-phy "#address-cells": const: 1 -- cgit v1.2.3-70-g09d2 From 95e38c17d9971cd6639ce899a24e581800e7113a Mon Sep 17 00:00:00 2001 From: Amelie Delaunay Date: Fri, 15 Oct 2021 18:14:25 +0200 Subject: phy: stm32: restore utmi switch on resume UTMI switch value can be lost during suspend/resume, depending on the power state reached. This patch adds resume function to usbphyc, to reconfigure utmi switch after suspend. Signed-off-by: Amelie Delaunay Link: https://lore.kernel.org/r/20211015161427.220784-2-amelie.delaunay@foss.st.com Signed-off-by: Vinod Koul --- drivers/phy/st/phy-stm32-usbphyc.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/phy/st/phy-stm32-usbphyc.c b/drivers/phy/st/phy-stm32-usbphyc.c index 937a14fa7448..083593aea53a 100644 --- a/drivers/phy/st/phy-stm32-usbphyc.c +++ b/drivers/phy/st/phy-stm32-usbphyc.c @@ -598,6 +598,18 @@ static int stm32_usbphyc_remove(struct platform_device *pdev) return 0; } +static int __maybe_unused stm32_usbphyc_resume(struct device *dev) +{ + struct stm32_usbphyc *usbphyc = dev_get_drvdata(dev); + + if (usbphyc->switch_setup >= 0) + stm32_usbphyc_switch_setup(usbphyc, usbphyc->switch_setup); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(stm32_usbphyc_pm_ops, NULL, stm32_usbphyc_resume); + static const struct of_device_id stm32_usbphyc_of_match[] = { { .compatible = "st,stm32mp1-usbphyc", }, { }, @@ -610,6 +622,7 @@ static struct platform_driver stm32_usbphyc_driver = { .driver = { .of_match_table = stm32_usbphyc_of_match, .name = "stm32-usbphyc", + .pm = &stm32_usbphyc_pm_ops, } }; module_platform_driver(stm32_usbphyc_driver); -- cgit v1.2.3-70-g09d2 From 6e59b5aea2f8b55e1c629ac35c147dfc14c418da Mon Sep 17 00:00:00 2001 From: Amelie Delaunay Date: Fri, 15 Oct 2021 18:14:26 +0200 Subject: dt-bindings: phy: phy-stm32-usbphyc: add optional phy tuning properties This patch adds the description of new optional phy tuning properties for usbphyc phy sub nodes. Signed-off-by: Amelie Delaunay Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211015161427.220784-3-amelie.delaunay@foss.st.com Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/phy-stm32-usbphyc.yaml | 129 +++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/phy-stm32-usbphyc.yaml b/Documentation/devicetree/bindings/phy/phy-stm32-usbphyc.yaml index 3329f1d33a4f..225128364a63 100644 --- a/Documentation/devicetree/bindings/phy/phy-stm32-usbphyc.yaml +++ b/Documentation/devicetree/bindings/phy/phy-stm32-usbphyc.yaml @@ -81,6 +81,119 @@ patternProperties: properties: vbus-supply: true + # It can be necessary to adjust the PHY settings to compensate parasitics, which can be due + # to USB connector/receptacle, routing, ESD protection component,... Here is the list of + # all optional parameters to tune the interface of the PHY (HS for High-Speed, FS for Full- + # Speed, LS for Low-Speed) + + st,current-boost-microamp: + description: Current boosting in uA + enum: [ 1000, 2000 ] + + st,no-lsfs-fb-cap: + description: Disables the LS/FS feedback capacitor + type: boolean + + st,decrease-hs-slew-rate: + description: Decreases the HS driver slew rate by 10% + type: boolean + + st,tune-hs-dc-level: + description: | + Tunes the HS driver DC level + - <0> normal level + - <1> increases the level by 5 to 7 mV + - <2> increases the level by 10 to 14 mV + - <3> decreases the level by 5 to 7 mV + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 3 + default: 0 + + st,enable-fs-rftime-tuning: + description: Enables the FS rise/fall tuning option + type: boolean + + st,enable-hs-rftime-reduction: + description: Enables the HS rise/fall reduction feature + type: boolean + + st,trim-hs-current: + description: | + Controls HS driver current trimming for choke compensation + - <0> = 18.87 mA target current / nominal + 0% + - <1> = 19.165 mA target current / nominal + 1.56% + - <2> = 19.46 mA target current / nominal + 3.12% + - <3> = 19.755 mA target current / nominal + 4.68% + - <4> = 20.05 mA target current / nominal + 6.24% + - <5> = 20.345 mA target current / nominal + 7.8% + - <6> = 20.64 mA target current / nominal + 9.36% + - <7> = 20.935 mA target current / nominal + 10.92% + - <8> = 21.23 mA target current / nominal + 12.48% + - <9> = 21.525 mA target current / nominal + 14.04% + - <10> = 21.82 mA target current / nominal + 15.6% + - <11> = 22.115 mA target current / nominal + 17.16% + - <12> = 22.458 mA target current / nominal + 19.01% + - <13> = 22.755 mA target current / nominal + 20.58% + - <14> = 23.052 mA target current / nominal + 22.16% + - <15> = 23.348 mA target current / nominal + 23.73% + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 15 + default: 0 + + st,trim-hs-impedance: + description: | + Controls HS driver impedance tuning for choke compensation + - <0> = no impedance offset + - <1> = reduce the impedance by 2 ohms + - <2> = reduce the impedance by 4 ohms + - <3> = reduce the impedance by 6 ohms + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 3 + default: 0 + + st,tune-squelch-level: + description: | + Tunes the squelch DC threshold value + - <0> = no shift in threshold + - <1> = threshold shift by +7 mV + - <2> = threshold shift by -5 mV + - <3> = threshold shift by +14 mV + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 3 + default: 0 + + st,enable-hs-rx-gain-eq: + description: Enables the HS Rx gain equalizer + type: boolean + + st,tune-hs-rx-offset: + description: | + Adjusts the HS Rx offset + - <0> = no offset + - <1> = offset of +5 mV + - <2> = offset of +10 mV + - <3> = offset of -5 mV + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 3 + default: 0 + + st,no-hs-ftime-ctrl: + description: Disables the HS fall time control of single ended signals during pre-emphasis + type: boolean + + st,no-lsfs-sc: + description: Disables the short circuit protection in LS/FS driver + type: boolean + + st,enable-hs-tx-staggering: + description: Enables the basic staggering in HS Tx mode + type: boolean + allOf: - if: properties: @@ -137,6 +250,14 @@ examples: reg = <0>; phy-supply = <&vdd_usb>; #phy-cells = <0>; + st,tune-hs-dc-level = <2>; + st,enable-fs-rftime-tuning; + st,enable-hs-rftime-reduction; + st,trim-hs-current = <15>; + st,trim-hs-impedance = <1>; + st,tune-squelch-level = <3>; + st,tune-hs-rx-offset = <2>; + st,no-lsfs-sc; connector { compatible = "usb-a-connector"; vbus-supply = <&vbus_sw>; @@ -147,6 +268,14 @@ examples: reg = <1>; phy-supply = <&vdd_usb>; #phy-cells = <1>; + st,tune-hs-dc-level = <2>; + st,enable-fs-rftime-tuning; + st,enable-hs-rftime-reduction; + st,trim-hs-current = <15>; + st,trim-hs-impedance = <1>; + st,tune-squelch-level = <3>; + st,tune-hs-rx-offset = <2>; + st,no-lsfs-sc; }; }; ... -- cgit v1.2.3-70-g09d2 From 2f5e9f815a2f9d581b0998710949400fc6e7a51f Mon Sep 17 00:00:00 2001 From: Amelie Delaunay Date: Fri, 15 Oct 2021 18:14:27 +0200 Subject: phy: stm32: add phy tuning support It can be necessary to adjust the phys settings to compensate parasitics. This patch adds support of new optional properties to configure the tune interface of the phys of stm32-usbphyc. Properties are optional, that's why each property is skipped if not found (-EINVAL). Phy tuning is restored on resume because if deep low power state is achieved, phy tuning configuration is reset. Signed-off-by: Amelie Delaunay Link: https://lore.kernel.org/r/20211015161427.220784-4-amelie.delaunay@foss.st.com Signed-off-by: Vinod Koul --- drivers/phy/st/phy-stm32-usbphyc.c | 190 +++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) diff --git a/drivers/phy/st/phy-stm32-usbphyc.c b/drivers/phy/st/phy-stm32-usbphyc.c index 083593aea53a..7df6a63ad37b 100644 --- a/drivers/phy/st/phy-stm32-usbphyc.c +++ b/drivers/phy/st/phy-stm32-usbphyc.c @@ -20,6 +20,7 @@ #define STM32_USBPHYC_PLL 0x0 #define STM32_USBPHYC_MISC 0x8 #define STM32_USBPHYC_MONITOR(X) (0x108 + ((X) * 0x100)) +#define STM32_USBPHYC_TUNE(X) (0x10C + ((X) * 0x100)) #define STM32_USBPHYC_VERSION 0x3F4 /* STM32_USBPHYC_PLL bit fields */ @@ -41,6 +42,83 @@ #define STM32_USBPHYC_MON_SEL_LOCKP 0x1F #define STM32_USBPHYC_MON_OUT_LOCKP BIT(3) +/* STM32_USBPHYC_TUNE bit fields */ +#define INCURREN BIT(0) +#define INCURRINT BIT(1) +#define LFSCAPEN BIT(2) +#define HSDRVSLEW BIT(3) +#define HSDRVDCCUR BIT(4) +#define HSDRVDCLEV BIT(5) +#define HSDRVCURINCR BIT(6) +#define FSDRVRFADJ BIT(7) +#define HSDRVRFRED BIT(8) +#define HSDRVCHKITRM GENMASK(12, 9) +#define HSDRVCHKZTRM GENMASK(14, 13) +#define OTPCOMP GENMASK(19, 15) +#define SQLCHCTL GENMASK(21, 20) +#define HDRXGNEQEN BIT(22) +#define HSRXOFF GENMASK(24, 23) +#define HSFALLPREEM BIT(25) +#define SHTCCTCTLPROT BIT(26) +#define STAGSEL BIT(27) + +enum boosting_vals { + BOOST_1000_UA = 1000, + BOOST_2000_UA = 2000, +}; + +enum dc_level_vals { + DC_NOMINAL, + DC_PLUS_5_TO_7_MV, + DC_PLUS_10_TO_14_MV, + DC_MINUS_5_TO_7_MV, + DC_MAX, +}; + +enum current_trim { + CUR_NOMINAL, + CUR_PLUS_1_56_PCT, + CUR_PLUS_3_12_PCT, + CUR_PLUS_4_68_PCT, + CUR_PLUS_6_24_PCT, + CUR_PLUS_7_8_PCT, + CUR_PLUS_9_36_PCT, + CUR_PLUS_10_92_PCT, + CUR_PLUS_12_48_PCT, + CUR_PLUS_14_04_PCT, + CUR_PLUS_15_6_PCT, + CUR_PLUS_17_16_PCT, + CUR_PLUS_19_01_PCT, + CUR_PLUS_20_58_PCT, + CUR_PLUS_22_16_PCT, + CUR_PLUS_23_73_PCT, + CUR_MAX, +}; + +enum impedance_trim { + IMP_NOMINAL, + IMP_MINUS_2_OHMS, + IMP_MINUS_4_OMHS, + IMP_MINUS_6_OHMS, + IMP_MAX, +}; + +enum squelch_level { + SQLCH_NOMINAL, + SQLCH_PLUS_7_MV, + SQLCH_MINUS_5_MV, + SQLCH_PLUS_14_MV, + SQLCH_MAX, +}; + +enum rx_offset { + NO_RX_OFFSET, + RX_OFFSET_PLUS_5_MV, + RX_OFFSET_PLUS_10_MV, + RX_OFFSET_MINUS_5_MV, + RX_OFFSET_MAX, +}; + /* STM32_USBPHYC_VERSION bit fields */ #define MINREV GENMASK(3, 0) #define MAJREV GENMASK(7, 4) @@ -60,6 +138,7 @@ struct stm32_usbphyc_phy { struct regulator *vbus; u32 index; bool active; + u32 tune; }; struct stm32_usbphyc { @@ -375,6 +454,107 @@ static int stm32_usbphyc_clk48_register(struct stm32_usbphyc *usbphyc) return ret; } +static void stm32_usbphyc_phy_tuning(struct stm32_usbphyc *usbphyc, + struct device_node *np, u32 index) +{ + struct stm32_usbphyc_phy *usbphyc_phy = usbphyc->phys[index]; + u32 reg = STM32_USBPHYC_TUNE(index); + u32 otpcomp, val; + int ret; + + /* Backup OTP compensation code */ + otpcomp = FIELD_GET(OTPCOMP, readl_relaxed(usbphyc->base + reg)); + + ret = of_property_read_u32(np, "st,current-boost-microamp", &val); + if (ret != -EINVAL) { + if (!ret && (val == BOOST_1000_UA || val == BOOST_2000_UA)) { + val = (val == BOOST_2000_UA) ? 1 : 0; + usbphyc_phy->tune |= INCURREN | FIELD_PREP(INCURRINT, val); + } else { + dev_warn(usbphyc->dev, "phy%d: invalid st,current-boost-microamp\n", index); + } + } + + if (!of_property_read_bool(np, "st,no-lsfs-fb-cap")) + usbphyc_phy->tune |= LFSCAPEN; + + if (of_property_read_bool(np, "st,slow-hs-slew-rate")) + usbphyc_phy->tune |= HSDRVSLEW; + + ret = of_property_read_u32(np, "st,tune-hs-dc-level", &val); + if (ret != -EINVAL) { + if (!ret && val < DC_MAX) { + if (val == DC_MINUS_5_TO_7_MV) {/* Decreases HS driver DC level */ + usbphyc_phy->tune |= HSDRVDCCUR; + } else if (val > 0) { /* Increases HS driver DC level */ + val = (val == DC_PLUS_10_TO_14_MV) ? 1 : 0; + usbphyc_phy->tune |= HSDRVCURINCR | FIELD_PREP(HSDRVDCLEV, val); + } + } else { + dev_warn(usbphyc->dev, "phy%d: invalid st,tune-hs-dc-level\n", index); + } + } + + if (of_property_read_bool(np, "st,enable-fs-rftime-tuning")) + usbphyc_phy->tune |= FSDRVRFADJ; + + if (of_property_read_bool(np, "st,enable-hs-rftime-reduction")) + usbphyc_phy->tune |= HSDRVRFRED; + + ret = of_property_read_u32(np, "st,trim-hs-current", &val); + if (ret != -EINVAL) { + if (!ret && val < CUR_MAX) + usbphyc_phy->tune |= FIELD_PREP(HSDRVCHKITRM, val); + else + dev_warn(usbphyc->dev, "phy%d: invalid st,trim-hs-current\n", index); + } + + ret = of_property_read_u32(np, "st,trim-hs-impedance", &val); + if (ret != -EINVAL) { + if (!ret && val < IMP_MAX) + usbphyc_phy->tune |= FIELD_PREP(HSDRVCHKZTRM, val); + else + dev_warn(usbphyc->dev, "phy%d: invalid st,trim-hs-impedance\n", index); + } + + ret = of_property_read_u32(np, "st,tune-squelch-level", &val); + if (ret != -EINVAL) { + if (!ret && val < SQLCH_MAX) + usbphyc_phy->tune |= FIELD_PREP(SQLCHCTL, val); + else + dev_warn(usbphyc->dev, "phy%d: invalid st,tune-squelch\n", index); + } + + if (of_property_read_bool(np, "st,enable-hs-rx-gain-eq")) + usbphyc_phy->tune |= HDRXGNEQEN; + + ret = of_property_read_u32(np, "st,tune-hs-rx-offset", &val); + if (ret != -EINVAL) { + if (!ret && val < RX_OFFSET_MAX) + usbphyc_phy->tune |= FIELD_PREP(HSRXOFF, val); + else + dev_warn(usbphyc->dev, "phy%d: invalid st,tune-hs-rx-offset\n", index); + } + + if (of_property_read_bool(np, "st,no-hs-ftime-ctrl")) + usbphyc_phy->tune |= HSFALLPREEM; + + if (!of_property_read_bool(np, "st,no-lsfs-sc")) + usbphyc_phy->tune |= SHTCCTCTLPROT; + + if (of_property_read_bool(np, "st,enable-hs-tx-staggering")) + usbphyc_phy->tune |= STAGSEL; + + /* Restore OTP compensation code */ + usbphyc_phy->tune |= FIELD_PREP(OTPCOMP, otpcomp); + + /* + * By default, if no st,xxx tuning property is used, usbphyc_phy->tune is equal to + * STM32_USBPHYC_TUNE reset value (LFSCAPEN | SHTCCTCTLPROT | OTPCOMP). + */ + writel_relaxed(usbphyc_phy->tune, usbphyc->base + reg); +} + static void stm32_usbphyc_switch_setup(struct stm32_usbphyc *usbphyc, u32 utmi_switch) { @@ -550,6 +730,9 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) usbphyc->phys[port]->vbus = NULL; } + /* Configure phy tuning */ + stm32_usbphyc_phy_tuning(usbphyc, child, index); + port++; } @@ -601,10 +784,17 @@ static int stm32_usbphyc_remove(struct platform_device *pdev) static int __maybe_unused stm32_usbphyc_resume(struct device *dev) { struct stm32_usbphyc *usbphyc = dev_get_drvdata(dev); + struct stm32_usbphyc_phy *usbphyc_phy; + int port; if (usbphyc->switch_setup >= 0) stm32_usbphyc_switch_setup(usbphyc, usbphyc->switch_setup); + for (port = 0; port < usbphyc->nphys; port++) { + usbphyc_phy = usbphyc->phys[port]; + writel_relaxed(usbphyc_phy->tune, usbphyc->base + STM32_USBPHYC_TUNE(port)); + } + return 0; } -- cgit v1.2.3-70-g09d2 From d8b951abd7ff7b126398c51d33671a6909541c68 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 20 Oct 2021 21:06:34 +0800 Subject: dt-bindings: phy: qcom,qmp: IPQ6018 and IPQ8074 PCIe PHY require no supply The qmp-phy driver suggests that 'vdda-phy-supply' and 'vdda-pll-supply' are not required for IPQ6018 and IPQ8074 QMP PCIe PHY. Update the bindings to reflect it. While at it, also correct the clock properties for IPQ8074 QMP PCIe PHY. And as the result, 'qcom,ipq8074-qmp-pcie-phy' and 'qcom,ipq6018-qmp-pcie-phy' share the same clock, reset and supply bindings. Signed-off-by: Shawn Guo Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211020130634.26194-1-shawn.guo@linaro.org Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/qcom,qmp-phy.yaml | 55 ++++++++++++---------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml index 647b45cfec84..630ceaf915e2 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml @@ -117,8 +117,6 @@ required: - clock-names - resets - reset-names - - vdda-phy-supply - - vdda-pll-supply additionalProperties: false @@ -151,6 +149,9 @@ allOf: items: - const: phy - const: common + required: + - vdda-phy-supply + - vdda-pll-supply - if: properties: compatible: @@ -177,6 +178,9 @@ allOf: items: - const: phy - const: common + required: + - vdda-phy-supply + - vdda-pll-supply - if: properties: compatible: @@ -205,6 +209,9 @@ allOf: - const: phy - const: common - const: cfg + required: + - vdda-phy-supply + - vdda-pll-supply - if: properties: compatible: @@ -234,6 +241,9 @@ allOf: items: - const: phy - const: common + required: + - vdda-phy-supply + - vdda-pll-supply - if: properties: compatible: @@ -254,6 +264,9 @@ allOf: reset-names: items: - const: ufsphy + required: + - vdda-phy-supply + - vdda-pll-supply - if: properties: compatible: @@ -279,34 +292,16 @@ allOf: reset-names: items: - const: ufsphy - - if: - properties: - compatible: - contains: - enum: - - qcom,ipq8074-qmp-pcie-phy - then: - properties: - clocks: - items: - - description: pipe clk. - clock-names: - items: - - const: pipe_clk - resets: - items: - - description: reset of phy block. - - description: phy common block reset. - reset-names: - items: - - const: phy - - const: common + required: + - vdda-phy-supply + - vdda-pll-supply - if: properties: compatible: contains: enum: - qcom,ipq6018-qmp-pcie-phy + - qcom,ipq8074-qmp-pcie-phy then: properties: clocks: @@ -357,6 +352,9 @@ allOf: reset-names: items: - const: phy + required: + - vdda-phy-supply + - vdda-pll-supply - if: properties: compatible: @@ -388,6 +386,9 @@ allOf: items: - const: phy - const: common + required: + - vdda-phy-supply + - vdda-pll-supply - if: properties: compatible: @@ -415,6 +416,9 @@ allOf: items: - const: phy - const: common + required: + - vdda-phy-supply + - vdda-pll-supply - if: properties: compatible: @@ -441,6 +445,9 @@ allOf: items: - const: phy_phy - const: phy + required: + - vdda-phy-supply + - vdda-pll-supply examples: - | -- cgit v1.2.3-70-g09d2 From 8d55027f4e2c04146a75fb63371ab96ccc887f2c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 14 Sep 2021 14:00:38 +0300 Subject: phy: ti: gmii-sel: check of_get_address() for failure Smatch complains that if of_get_address() returns NULL, then "size" isn't initialized. Also it would lead to an Oops. Fixes: 7f78322cdd67 ("phy: ti: gmii-sel: retrieve ports number and base offset from dt") Signed-off-by: Dan Carpenter Reviewed-by: Grygorii Strashko Link: https://lore.kernel.org/r/20210914110038.GB11657@kili Signed-off-by: Vinod Koul --- drivers/phy/ti/phy-gmii-sel.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/phy/ti/phy-gmii-sel.c b/drivers/phy/ti/phy-gmii-sel.c index 5fd2e8a08bfc..d0ab69750c6b 100644 --- a/drivers/phy/ti/phy-gmii-sel.c +++ b/drivers/phy/ti/phy-gmii-sel.c @@ -320,6 +320,8 @@ static int phy_gmii_sel_init_ports(struct phy_gmii_sel_priv *priv) u64 size; offset = of_get_address(dev->of_node, 0, &size, NULL); + if (!offset) + return -EINVAL; priv->num_ports = size / sizeof(u32); if (!priv->num_ports) return -EINVAL; -- cgit v1.2.3-70-g09d2 From aef096dbf514a9f70a1d81824d3b21d5266e0d6e Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Wed, 22 Sep 2021 14:37:32 +0200 Subject: phy: cadence-torrent: Migrate to clk_hw based registration and OF APIs Use clk_hw based provider APIs to register clks to remove the usage of deprecated APIs. Signed-off-by: Swapnil Jakhade Link: https://lore.kernel.org/r/20210922123735.21927-2-sjakhade@cadence.com Signed-off-by: Vinod Koul --- drivers/phy/cadence/phy-cadence-torrent.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/drivers/phy/cadence/phy-cadence-torrent.c b/drivers/phy/cadence/phy-cadence-torrent.c index 415ace64adc5..ecb1aa883c05 100644 --- a/drivers/phy/cadence/phy-cadence-torrent.c +++ b/drivers/phy/cadence/phy-cadence-torrent.c @@ -235,6 +235,8 @@ #define PHY_PMA_CMN_CTRL2 0x0001U #define PHY_PMA_PLL_RAW_CTRL 0x0003U +#define CDNS_TORRENT_OUTPUT_CLOCKS 1 + static const char * const clk_names[] = { [CDNS_TORRENT_REFCLK_DRIVER] = "refclk-driver", }; @@ -333,8 +335,7 @@ struct cdns_torrent_phy { struct regmap_field *phy_pma_pll_raw_ctrl; struct regmap_field *phy_reset_ctrl; struct regmap_field *phy_pcs_iso_link_ctrl_1[MAX_NUM_LANES]; - struct clk *clks[CDNS_TORRENT_REFCLK_DRIVER + 1]; - struct clk_onecell_data clk_data; + struct clk_hw_onecell_data *clk_hw_data; }; enum phy_powerstate { @@ -1659,8 +1660,9 @@ static int cdns_torrent_derived_refclk_register(struct cdns_torrent_phy *cdns_ph const char *parent_name; struct regmap *regmap; char clk_name[100]; + struct clk_hw *hw; struct clk *clk; - int i; + int i, ret; derived_refclk = devm_kzalloc(dev, sizeof(*derived_refclk), GFP_KERNEL); if (!derived_refclk) @@ -1706,11 +1708,12 @@ static int cdns_torrent_derived_refclk_register(struct cdns_torrent_phy *cdns_ph derived_refclk->hw.init = init; - clk = devm_clk_register(dev, &derived_refclk->hw); - if (IS_ERR(clk)) - return PTR_ERR(clk); + hw = &derived_refclk->hw; + ret = devm_clk_hw_register(dev, hw); + if (ret) + return ret; - cdns_phy->clks[CDNS_TORRENT_REFCLK_DRIVER] = clk; + cdns_phy->clk_hw_data->hws[CDNS_TORRENT_REFCLK_DRIVER] = hw; return 0; } @@ -2188,18 +2191,23 @@ static int cdns_torrent_clk_register(struct cdns_torrent_phy *cdns_phy) { struct device *dev = cdns_phy->dev; struct device_node *node = dev->of_node; + struct clk_hw_onecell_data *data; int ret; + data = devm_kzalloc(dev, struct_size(data, hws, CDNS_TORRENT_OUTPUT_CLOCKS), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->num = CDNS_TORRENT_OUTPUT_CLOCKS; + cdns_phy->clk_hw_data = data; + ret = cdns_torrent_derived_refclk_register(cdns_phy); if (ret) { dev_err(dev, "failed to register derived refclk\n"); return ret; } - cdns_phy->clk_data.clks = cdns_phy->clks; - cdns_phy->clk_data.clk_num = CDNS_TORRENT_REFCLK_DRIVER + 1; - - ret = of_clk_add_provider(node, of_clk_src_onecell_get, &cdns_phy->clk_data); + ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, data); if (ret) { dev_err(dev, "Failed to add clock provider: %s\n", node->name); return ret; -- cgit v1.2.3-70-g09d2 From f9aec1648df09d55436a0e3a94acff1df507751f Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Wed, 22 Sep 2021 14:37:33 +0200 Subject: dt-bindings: phy: cadence-torrent: Add clock IDs for derived and received refclk Add clock IDs for derived and received reference clock output. Signed-off-by: Swapnil Jakhade Acked-by: Rob Herring Link: https://lore.kernel.org/r/20210922123735.21927-3-sjakhade@cadence.com Signed-off-by: Vinod Koul --- include/dt-bindings/phy/phy-cadence.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/dt-bindings/phy/phy-cadence.h b/include/dt-bindings/phy/phy-cadence.h index 4652bcb86265..24fdc9e11bd6 100644 --- a/include/dt-bindings/phy/phy-cadence.h +++ b/include/dt-bindings/phy/phy-cadence.h @@ -12,6 +12,8 @@ #define TORRENT_SERDES_INTERNAL_SSC 2 #define CDNS_TORRENT_REFCLK_DRIVER 0 +#define CDNS_TORRENT_DERIVED_REFCLK 1 +#define CDNS_TORRENT_RECEIVED_REFCLK 2 /* Sierra */ #define CDNS_SIERRA_PLL_CMNLC 0 -- cgit v1.2.3-70-g09d2 From 235bde4f440a32a528bcedc48dbfa0ec4a039060 Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Wed, 22 Sep 2021 14:37:34 +0200 Subject: phy: cadence-torrent: Model reference clock driver as a clock to enable derived refclk When reference clock driver is enabled, either derived or received refclk is output on cmn_refclk_p/m. Update the reference clock driver implementation by modelling reference clock driver as a "clock" with derived reference clock set as its default parent. The support for received reference clock will be added in a separate patch. Signed-off-by: Swapnil Jakhade Link: https://lore.kernel.org/r/20210922123735.21927-4-sjakhade@cadence.com Signed-off-by: Vinod Koul --- drivers/phy/cadence/phy-cadence-torrent.c | 157 +++++++++++++++++++++++++----- 1 file changed, 132 insertions(+), 25 deletions(-) diff --git a/drivers/phy/cadence/phy-cadence-torrent.c b/drivers/phy/cadence/phy-cadence-torrent.c index ecb1aa883c05..615aca6bd52b 100644 --- a/drivers/phy/cadence/phy-cadence-torrent.c +++ b/drivers/phy/cadence/phy-cadence-torrent.c @@ -235,10 +235,11 @@ #define PHY_PMA_CMN_CTRL2 0x0001U #define PHY_PMA_PLL_RAW_CTRL 0x0003U -#define CDNS_TORRENT_OUTPUT_CLOCKS 1 +#define CDNS_TORRENT_OUTPUT_CLOCKS 2 static const char * const clk_names[] = { [CDNS_TORRENT_REFCLK_DRIVER] = "refclk-driver", + [CDNS_TORRENT_DERIVED_REFCLK] = "refclk-der", }; static const struct reg_field phy_pll_cfg = @@ -261,10 +262,12 @@ static const struct reg_field phy_pcs_iso_link_ctrl_1 = static const struct reg_field phy_pipe_cmn_ctrl1_0 = REG_FIELD(PHY_PIPE_CMN_CTRL1, 0, 0); -#define REFCLK_OUT_NUM_CMN_CONFIG 5 +static const struct reg_field cmn_cdiag_refclk_ovrd_4 = + REG_FIELD(CMN_CDIAG_REFCLK_OVRD, 4, 4); + +#define REFCLK_OUT_NUM_CMN_CONFIG 4 enum cdns_torrent_refclk_out_cmn { - CMN_CDIAG_REFCLK_OVRD_4, CMN_CDIAG_REFCLK_DRV0_CTRL_1, CMN_CDIAG_REFCLK_DRV0_CTRL_4, CMN_CDIAG_REFCLK_DRV0_CTRL_5, @@ -272,7 +275,6 @@ enum cdns_torrent_refclk_out_cmn { }; static const struct reg_field refclk_out_cmn_cfg[] = { - [CMN_CDIAG_REFCLK_OVRD_4] = REG_FIELD(CMN_CDIAG_REFCLK_OVRD, 4, 4), [CMN_CDIAG_REFCLK_DRV0_CTRL_1] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 1, 1), [CMN_CDIAG_REFCLK_DRV0_CTRL_4] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 4, 4), [CMN_CDIAG_REFCLK_DRV0_CTRL_5] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 5, 5), @@ -330,6 +332,8 @@ struct cdns_torrent_phy { struct regmap *regmap_phy_pcs_lane_cdb[MAX_NUM_LANES]; struct regmap *regmap_dptx_phy_reg; struct regmap_field *phy_pll_cfg; + struct regmap_field *phy_pipe_cmn_ctrl1_0; + struct regmap_field *cmn_cdiag_refclk_ovrd_4; struct regmap_field *phy_pma_cmn_ctrl_1; struct regmap_field *phy_pma_cmn_ctrl_2; struct regmap_field *phy_pma_pll_raw_ctrl; @@ -345,10 +349,19 @@ enum phy_powerstate { POWERSTATE_A3 = 3, }; +struct cdns_torrent_refclk_driver { + struct clk_hw hw; + struct regmap_field *cmn_fields[REFCLK_OUT_NUM_CMN_CONFIG]; + struct clk_init_data clk_data; +}; + +#define to_cdns_torrent_refclk_driver(_hw) \ + container_of(_hw, struct cdns_torrent_refclk_driver, hw) + struct cdns_torrent_derived_refclk { struct clk_hw hw; struct regmap_field *phy_pipe_cmn_ctrl1_0; - struct regmap_field *cmn_fields[REFCLK_OUT_NUM_CMN_CONFIG]; + struct regmap_field *cmn_cdiag_refclk_ovrd_4; struct clk_init_data clk_data; }; @@ -1618,11 +1631,7 @@ static int cdns_torrent_derived_refclk_enable(struct clk_hw *hw) { struct cdns_torrent_derived_refclk *derived_refclk = to_cdns_torrent_derived_refclk(hw); - regmap_field_write(derived_refclk->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_6], 0); - regmap_field_write(derived_refclk->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], 1); - regmap_field_write(derived_refclk->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_5], 1); - regmap_field_write(derived_refclk->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_1], 0); - regmap_field_write(derived_refclk->cmn_fields[CMN_CDIAG_REFCLK_OVRD_4], 1); + regmap_field_write(derived_refclk->cmn_cdiag_refclk_ovrd_4, 1); regmap_field_write(derived_refclk->phy_pipe_cmn_ctrl1_0, 1); return 0; @@ -1633,6 +1642,7 @@ static void cdns_torrent_derived_refclk_disable(struct clk_hw *hw) struct cdns_torrent_derived_refclk *derived_refclk = to_cdns_torrent_derived_refclk(hw); regmap_field_write(derived_refclk->phy_pipe_cmn_ctrl1_0, 0); + regmap_field_write(derived_refclk->cmn_cdiag_refclk_ovrd_4, 0); } static int cdns_torrent_derived_refclk_is_enabled(struct clk_hw *hw) @@ -1640,7 +1650,7 @@ static int cdns_torrent_derived_refclk_is_enabled(struct clk_hw *hw) struct cdns_torrent_derived_refclk *derived_refclk = to_cdns_torrent_derived_refclk(hw); int val; - regmap_field_read(derived_refclk->phy_pipe_cmn_ctrl1_0, &val); + regmap_field_read(derived_refclk->cmn_cdiag_refclk_ovrd_4, &val); return !!val; } @@ -1655,21 +1665,19 @@ static int cdns_torrent_derived_refclk_register(struct cdns_torrent_phy *cdns_ph { struct cdns_torrent_derived_refclk *derived_refclk; struct device *dev = cdns_phy->dev; - struct regmap_field *field; struct clk_init_data *init; const char *parent_name; - struct regmap *regmap; char clk_name[100]; struct clk_hw *hw; struct clk *clk; - int i, ret; + int ret; derived_refclk = devm_kzalloc(dev, sizeof(*derived_refclk), GFP_KERNEL); if (!derived_refclk) return -ENOMEM; snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), - clk_names[CDNS_TORRENT_REFCLK_DRIVER]); + clk_names[CDNS_TORRENT_DERIVED_REFCLK]); clk = devm_clk_get_optional(dev, "phy_en_refclk"); if (IS_ERR(clk)) { @@ -1688,27 +1696,104 @@ static int cdns_torrent_derived_refclk_register(struct cdns_torrent_phy *cdns_ph init->flags = 0; init->name = clk_name; - regmap = cdns_phy->regmap_phy_pcs_common_cdb; - field = devm_regmap_field_alloc(dev, regmap, phy_pipe_cmn_ctrl1_0); - if (IS_ERR(field)) { - dev_err(dev, "phy_pipe_cmn_ctrl1_0 reg field init failed\n"); - return PTR_ERR(field); + derived_refclk->phy_pipe_cmn_ctrl1_0 = cdns_phy->phy_pipe_cmn_ctrl1_0; + derived_refclk->cmn_cdiag_refclk_ovrd_4 = cdns_phy->cmn_cdiag_refclk_ovrd_4; + + derived_refclk->hw.init = init; + + hw = &derived_refclk->hw; + ret = devm_clk_hw_register(dev, hw); + if (ret) + return ret; + + cdns_phy->clk_hw_data->hws[CDNS_TORRENT_DERIVED_REFCLK] = hw; + + return 0; +} + +static int cdns_torrent_refclk_driver_enable(struct clk_hw *hw) +{ + struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw); + + regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_6], 0); + regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], 1); + regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_5], 1); + regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_1], 0); + + return 0; +} + +static void cdns_torrent_refclk_driver_disable(struct clk_hw *hw) +{ + struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw); + + regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_1], 1); +} + +static int cdns_torrent_refclk_driver_is_enabled(struct clk_hw *hw) +{ + struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw); + int val; + + regmap_field_read(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_1], &val); + + return !val; +} + +static const struct clk_ops cdns_torrent_refclk_driver_ops = { + .enable = cdns_torrent_refclk_driver_enable, + .disable = cdns_torrent_refclk_driver_disable, + .is_enabled = cdns_torrent_refclk_driver_is_enabled, +}; + +static int cdns_torrent_refclk_driver_register(struct cdns_torrent_phy *cdns_phy) +{ + struct cdns_torrent_refclk_driver *refclk_driver; + struct device *dev = cdns_phy->dev; + struct regmap_field *field; + struct clk_init_data *init; + const char *parent_name; + struct regmap *regmap; + char clk_name[100]; + struct clk_hw *hw; + int i, ret; + + refclk_driver = devm_kzalloc(dev, sizeof(*refclk_driver), GFP_KERNEL); + if (!refclk_driver) + return -ENOMEM; + + hw = cdns_phy->clk_hw_data->hws[CDNS_TORRENT_DERIVED_REFCLK]; + if (IS_ERR_OR_NULL(hw)) { + dev_err(dev, "No parent clock for refclk driver clock\n"); + return IS_ERR(hw) ? PTR_ERR(hw) : -ENOENT; } - derived_refclk->phy_pipe_cmn_ctrl1_0 = field; + parent_name = clk_hw_get_name(hw); + + snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), + clk_names[CDNS_TORRENT_REFCLK_DRIVER]); + + init = &refclk_driver->clk_data; + + init->ops = &cdns_torrent_refclk_driver_ops; + init->flags = 0; + init->parent_names = &parent_name; + init->num_parents = 1; + init->name = clk_name; regmap = cdns_phy->regmap_common_cdb; + for (i = 0; i < REFCLK_OUT_NUM_CMN_CONFIG; i++) { field = devm_regmap_field_alloc(dev, regmap, refclk_out_cmn_cfg[i]); if (IS_ERR(field)) { - dev_err(dev, "CMN reg field init failed\n"); + dev_err(dev, "Refclk driver CMN reg field init failed\n"); return PTR_ERR(field); } - derived_refclk->cmn_fields[i] = field; + refclk_driver->cmn_fields[i] = field; } - derived_refclk->hw.init = init; + refclk_driver->hw.init = init; - hw = &derived_refclk->hw; + hw = &refclk_driver->hw; ret = devm_clk_hw_register(dev, hw); if (ret) return ret; @@ -1768,6 +1853,22 @@ static int cdns_torrent_regfield_init(struct cdns_torrent_phy *cdns_phy) } cdns_phy->phy_pll_cfg = field; + regmap = cdns_phy->regmap_phy_pcs_common_cdb; + field = devm_regmap_field_alloc(dev, regmap, phy_pipe_cmn_ctrl1_0); + if (IS_ERR(field)) { + dev_err(dev, "phy_pipe_cmn_ctrl1_0 reg field init failed\n"); + return PTR_ERR(field); + } + cdns_phy->phy_pipe_cmn_ctrl1_0 = field; + + regmap = cdns_phy->regmap_common_cdb; + field = devm_regmap_field_alloc(dev, regmap, cmn_cdiag_refclk_ovrd_4); + if (IS_ERR(field)) { + dev_err(dev, "cmn_cdiag_refclk_ovrd_4 reg field init failed\n"); + return PTR_ERR(field); + } + cdns_phy->cmn_cdiag_refclk_ovrd_4 = field; + regmap = cdns_phy->regmap_phy_pma_common_cdb; field = devm_regmap_field_alloc(dev, regmap, phy_pma_cmn_ctrl_1); if (IS_ERR(field)) { @@ -2207,6 +2308,12 @@ static int cdns_torrent_clk_register(struct cdns_torrent_phy *cdns_phy) return ret; } + ret = cdns_torrent_refclk_driver_register(cdns_phy); + if (ret) { + dev_err(dev, "failed to register refclk driver\n"); + return ret; + } + ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, data); if (ret) { dev_err(dev, "Failed to add clock provider: %s\n", node->name); -- cgit v1.2.3-70-g09d2 From 785a4e688cd29adfe9a5f7a7d20f877f5b8ac4fa Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Wed, 22 Sep 2021 14:37:35 +0200 Subject: phy: cadence-torrent: Add support to output received reference clock Add support to output received reference clock. Model the received reference clock as an alternate parent of reference clock driver clock. When received refclk is selected to output on cmn_refclk_p/m, this is the internal reference clock driven on the pma_cmn_refclk_int. Signed-off-by: Swapnil Jakhade Link: https://lore.kernel.org/r/20210922123735.21927-5-sjakhade@cadence.com Signed-off-by: Vinod Koul --- drivers/phy/cadence/phy-cadence-torrent.c | 159 +++++++++++++++++++++++++++--- 1 file changed, 148 insertions(+), 11 deletions(-) diff --git a/drivers/phy/cadence/phy-cadence-torrent.c b/drivers/phy/cadence/phy-cadence-torrent.c index 615aca6bd52b..5786166133d3 100644 --- a/drivers/phy/cadence/phy-cadence-torrent.c +++ b/drivers/phy/cadence/phy-cadence-torrent.c @@ -235,11 +235,12 @@ #define PHY_PMA_CMN_CTRL2 0x0001U #define PHY_PMA_PLL_RAW_CTRL 0x0003U -#define CDNS_TORRENT_OUTPUT_CLOCKS 2 +#define CDNS_TORRENT_OUTPUT_CLOCKS 3 static const char * const clk_names[] = { [CDNS_TORRENT_REFCLK_DRIVER] = "refclk-driver", [CDNS_TORRENT_DERIVED_REFCLK] = "refclk-der", + [CDNS_TORRENT_RECEIVED_REFCLK] = "refclk-rec", }; static const struct reg_field phy_pll_cfg = @@ -281,6 +282,13 @@ static const struct reg_field refclk_out_cmn_cfg[] = { [CMN_CDIAG_REFCLK_DRV0_CTRL_6] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 6, 6), }; +static const int refclk_driver_parent_index[] = { + CDNS_TORRENT_DERIVED_REFCLK, + CDNS_TORRENT_RECEIVED_REFCLK +}; + +static u32 cdns_torrent_refclk_driver_mux_table[] = { 1, 0 }; + enum cdns_torrent_phy_type { TYPE_NONE, TYPE_DP, @@ -368,6 +376,16 @@ struct cdns_torrent_derived_refclk { #define to_cdns_torrent_derived_refclk(_hw) \ container_of(_hw, struct cdns_torrent_derived_refclk, hw) +struct cdns_torrent_received_refclk { + struct clk_hw hw; + struct regmap_field *phy_pipe_cmn_ctrl1_0; + struct regmap_field *cmn_cdiag_refclk_ovrd_4; + struct clk_init_data clk_data; +}; + +#define to_cdns_torrent_received_refclk(_hw) \ + container_of(_hw, struct cdns_torrent_received_refclk, hw) + struct cdns_reg_pairs { u32 val; u32 off; @@ -1711,12 +1729,94 @@ static int cdns_torrent_derived_refclk_register(struct cdns_torrent_phy *cdns_ph return 0; } +static int cdns_torrent_received_refclk_enable(struct clk_hw *hw) +{ + struct cdns_torrent_received_refclk *received_refclk = to_cdns_torrent_received_refclk(hw); + + regmap_field_write(received_refclk->phy_pipe_cmn_ctrl1_0, 1); + + return 0; +} + +static void cdns_torrent_received_refclk_disable(struct clk_hw *hw) +{ + struct cdns_torrent_received_refclk *received_refclk = to_cdns_torrent_received_refclk(hw); + + regmap_field_write(received_refclk->phy_pipe_cmn_ctrl1_0, 0); +} + +static int cdns_torrent_received_refclk_is_enabled(struct clk_hw *hw) +{ + struct cdns_torrent_received_refclk *received_refclk = to_cdns_torrent_received_refclk(hw); + int val, cmn_val; + + regmap_field_read(received_refclk->phy_pipe_cmn_ctrl1_0, &val); + regmap_field_read(received_refclk->cmn_cdiag_refclk_ovrd_4, &cmn_val); + + return val && !cmn_val; +} + +static const struct clk_ops cdns_torrent_received_refclk_ops = { + .enable = cdns_torrent_received_refclk_enable, + .disable = cdns_torrent_received_refclk_disable, + .is_enabled = cdns_torrent_received_refclk_is_enabled, +}; + +static int cdns_torrent_received_refclk_register(struct cdns_torrent_phy *cdns_phy) +{ + struct cdns_torrent_received_refclk *received_refclk; + struct device *dev = cdns_phy->dev; + struct clk_init_data *init; + const char *parent_name; + char clk_name[100]; + struct clk_hw *hw; + struct clk *clk; + int ret; + + received_refclk = devm_kzalloc(dev, sizeof(*received_refclk), GFP_KERNEL); + if (!received_refclk) + return -ENOMEM; + + snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), + clk_names[CDNS_TORRENT_RECEIVED_REFCLK]); + + clk = devm_clk_get_optional(dev, "phy_en_refclk"); + if (IS_ERR(clk)) { + dev_err(dev, "No parent clock for received_refclk\n"); + return PTR_ERR(clk); + } + + init = &received_refclk->clk_data; + + if (clk) { + parent_name = __clk_get_name(clk); + init->parent_names = &parent_name; + init->num_parents = 1; + } + init->ops = &cdns_torrent_received_refclk_ops; + init->flags = 0; + init->name = clk_name; + + received_refclk->phy_pipe_cmn_ctrl1_0 = cdns_phy->phy_pipe_cmn_ctrl1_0; + received_refclk->cmn_cdiag_refclk_ovrd_4 = cdns_phy->cmn_cdiag_refclk_ovrd_4; + + received_refclk->hw.init = init; + + hw = &received_refclk->hw; + ret = devm_clk_hw_register(dev, hw); + if (ret) + return ret; + + cdns_phy->clk_hw_data->hws[CDNS_TORRENT_RECEIVED_REFCLK] = hw; + + return 0; +} + static int cdns_torrent_refclk_driver_enable(struct clk_hw *hw) { struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw); regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_6], 0); - regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], 1); regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_5], 1); regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_1], 0); @@ -1740,10 +1840,30 @@ static int cdns_torrent_refclk_driver_is_enabled(struct clk_hw *hw) return !val; } +static u8 cdns_torrent_refclk_driver_get_parent(struct clk_hw *hw) +{ + struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw); + unsigned int val; + + regmap_field_read(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], &val); + return clk_mux_val_to_index(hw, cdns_torrent_refclk_driver_mux_table, 0, val); +} + +static int cdns_torrent_refclk_driver_set_parent(struct clk_hw *hw, u8 index) +{ + struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw); + unsigned int val; + + val = cdns_torrent_refclk_driver_mux_table[index]; + return regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], val); +} + static const struct clk_ops cdns_torrent_refclk_driver_ops = { .enable = cdns_torrent_refclk_driver_enable, .disable = cdns_torrent_refclk_driver_disable, .is_enabled = cdns_torrent_refclk_driver_is_enabled, + .set_parent = cdns_torrent_refclk_driver_set_parent, + .get_parent = cdns_torrent_refclk_driver_get_parent, }; static int cdns_torrent_refclk_driver_register(struct cdns_torrent_phy *cdns_phy) @@ -1752,7 +1872,8 @@ static int cdns_torrent_refclk_driver_register(struct cdns_torrent_phy *cdns_phy struct device *dev = cdns_phy->dev; struct regmap_field *field; struct clk_init_data *init; - const char *parent_name; + const char **parent_names; + unsigned int num_parents; struct regmap *regmap; char clk_name[100]; struct clk_hw *hw; @@ -1762,12 +1883,19 @@ static int cdns_torrent_refclk_driver_register(struct cdns_torrent_phy *cdns_phy if (!refclk_driver) return -ENOMEM; - hw = cdns_phy->clk_hw_data->hws[CDNS_TORRENT_DERIVED_REFCLK]; - if (IS_ERR_OR_NULL(hw)) { - dev_err(dev, "No parent clock for refclk driver clock\n"); - return IS_ERR(hw) ? PTR_ERR(hw) : -ENOENT; + num_parents = ARRAY_SIZE(refclk_driver_parent_index); + parent_names = devm_kzalloc(dev, (sizeof(char *) * num_parents), GFP_KERNEL); + if (!parent_names) + return -ENOMEM; + + for (i = 0; i < num_parents; i++) { + hw = cdns_phy->clk_hw_data->hws[refclk_driver_parent_index[i]]; + if (IS_ERR_OR_NULL(hw)) { + dev_err(dev, "No parent clock for refclk driver clock\n"); + return IS_ERR(hw) ? PTR_ERR(hw) : -ENOENT; + } + parent_names[i] = clk_hw_get_name(hw); } - parent_name = clk_hw_get_name(hw); snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), clk_names[CDNS_TORRENT_REFCLK_DRIVER]); @@ -1775,9 +1903,9 @@ static int cdns_torrent_refclk_driver_register(struct cdns_torrent_phy *cdns_phy init = &refclk_driver->clk_data; init->ops = &cdns_torrent_refclk_driver_ops; - init->flags = 0; - init->parent_names = &parent_name; - init->num_parents = 1; + init->flags = CLK_SET_RATE_NO_REPARENT; + init->parent_names = parent_names; + init->num_parents = num_parents; init->name = clk_name; regmap = cdns_phy->regmap_common_cdb; @@ -1791,6 +1919,9 @@ static int cdns_torrent_refclk_driver_register(struct cdns_torrent_phy *cdns_phy refclk_driver->cmn_fields[i] = field; } + /* Enable Derived reference clock as default */ + regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], 1); + refclk_driver->hw.init = init; hw = &refclk_driver->hw; @@ -2308,6 +2439,12 @@ static int cdns_torrent_clk_register(struct cdns_torrent_phy *cdns_phy) return ret; } + ret = cdns_torrent_received_refclk_register(cdns_phy); + if (ret) { + dev_err(dev, "failed to register received refclk\n"); + return ret; + } + ret = cdns_torrent_refclk_driver_register(cdns_phy); if (ret) { dev_err(dev, "failed to register refclk driver\n"); -- cgit v1.2.3-70-g09d2 From 26f71abef580537d978f6299330689f029ee1e6c Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 Oct 2021 18:56:04 +0300 Subject: phy: qcom-qmp: another fix for the sc8180x PCIe definition Commit f839f14e24f2 ("phy: qcom-qmp: Add sc8180x PCIe support") added SC8180X PCIe tables, but used sm8250_qmp_pcie_serdes_tbl as a serdes table because of the copy paste error. Commit bfccd9a71a08 ("phy: qcom-qmp: Fix sc8180x PCIe definition") corrected part of this mistake by pointing serdes_tbl to sc8180x_qmp_pcie_serdes_tbl, however the serdes_tbl_num field was not updated to use sc8180x table. So let's now fix the serdes_tbl_num field too. Fixes: bfccd9a71a08 ("phy: qcom-qmp: Fix sc8180x PCIe definition") Signed-off-by: Dmitry Baryshkov Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211020155604.1374530-1-dmitry.baryshkov@linaro.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 32123a60be97..456a59d8c7d0 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -3745,7 +3745,7 @@ static const struct qmp_phy_cfg sc8180x_pciephy_cfg = { .nlanes = 1, .serdes_tbl = sc8180x_qmp_pcie_serdes_tbl, - .serdes_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_serdes_tbl), + .serdes_tbl_num = ARRAY_SIZE(sc8180x_qmp_pcie_serdes_tbl), .tx_tbl = sc8180x_qmp_pcie_tx_tbl, .tx_tbl_num = ARRAY_SIZE(sc8180x_qmp_pcie_tx_tbl), .rx_tbl = sc8180x_qmp_pcie_rx_tbl, -- cgit v1.2.3-70-g09d2 From 21b89120be87b3fd2454e354bf466e5c4fea9624 Mon Sep 17 00:00:00 2001 From: Wan Jiabing Date: Fri, 15 Oct 2021 04:01:51 -0400 Subject: phy: hisilicon: Add of_node_put() in phy-hisi-inno-usb2 Fix following coccicheck warning: ./drivers/phy/hisilicon/phy-hisi-inno-usb2.c:138:1-23: WARNING: Function for_each_child_of_node should have of_node_put() before break Early exits from for_each_child_of_node should decrement the node reference counter. Signed-off-by: Wan Jiabing Link: https://lore.kernel.org/r/20211015080154.16016-1-wanjiabing@vivo.com Signed-off-by: Vinod Koul --- drivers/phy/hisilicon/phy-hisi-inno-usb2.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/phy/hisilicon/phy-hisi-inno-usb2.c b/drivers/phy/hisilicon/phy-hisi-inno-usb2.c index 34a6a9a1ceb2..b133ae06757a 100644 --- a/drivers/phy/hisilicon/phy-hisi-inno-usb2.c +++ b/drivers/phy/hisilicon/phy-hisi-inno-usb2.c @@ -140,14 +140,19 @@ static int hisi_inno_phy_probe(struct platform_device *pdev) struct phy *phy; rst = of_reset_control_get_exclusive(child, NULL); - if (IS_ERR(rst)) + if (IS_ERR(rst)) { + of_node_put(child); return PTR_ERR(rst); + } + priv->ports[i].utmi_rst = rst; priv->ports[i].priv = priv; phy = devm_phy_create(dev, child, &hisi_inno_phy_ops); - if (IS_ERR(phy)) + if (IS_ERR(phy)) { + of_node_put(child); return PTR_ERR(phy); + } phy_set_bus_width(phy, 8); phy_set_drvdata(phy, &priv->ports[i]); @@ -155,6 +160,7 @@ static int hisi_inno_phy_probe(struct platform_device *pdev) if (i > INNO_PHY_PORT_NUM) { dev_warn(dev, "Support %d ports in maximum\n", i); + of_node_put(child); break; } } -- cgit v1.2.3-70-g09d2 From b475bf0ec40a2b13fb32ef62f5706576d5858460 Mon Sep 17 00:00:00 2001 From: Sandeep Maheswaram Date: Mon, 25 Oct 2021 09:49:35 +0530 Subject: phy: qcom-snps: Correct the FSEL_MASK The FSEL_MASK which selects the refclock is defined incorrectly. It should be [4:6] not [5:7]. Due to this incorrect definition, the BIT(7) in USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0 is reset which keeps PHY analog blocks ON during suspend. Fix this issue by correctly defining the FSEL_MASK. Fixes: 51e8114f80d0 ("phy: qcom-snps: Add SNPS USB PHY driver for QCOM based SOCs") Signed-off-by: Sandeep Maheswaram Link: https://lore.kernel.org/r/1635135575-5668-1-git-send-email-quic_c_sanm@quicinc.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c index ae4bac024c7b..7e61202aa234 100644 --- a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c +++ b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c @@ -33,7 +33,7 @@ #define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0 (0x54) #define RETENABLEN BIT(3) -#define FSEL_MASK GENMASK(7, 5) +#define FSEL_MASK GENMASK(6, 4) #define FSEL_DEFAULT (0x3 << 4) #define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1 (0x58) -- cgit v1.2.3-70-g09d2 From b4dc97ab0a629eda8bda20d96ef47dac08a505d9 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Thu, 9 Sep 2021 15:21:49 +0800 Subject: phy: Sparx5 Eth SerDes: Fix return value check in sparx5_serdes_probe() In case of error, the function devm_ioremap() returns NULL pointer not ERR_PTR(). The IS_ERR() test in the return value check should be replaced with NULL test. Fixes: 2ff8a1eeb5aa ("phy: Add Sparx5 ethernet serdes PHY driver") Reported-by: Hulk Robot Signed-off-by: Yang Yingliang Link: https://lore.kernel.org/r/20210909072149.2934047-1-yangyingliang@huawei.com Signed-off-by: Vinod Koul --- drivers/phy/microchip/sparx5_serdes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/phy/microchip/sparx5_serdes.c b/drivers/phy/microchip/sparx5_serdes.c index 4076580fc2cd..ab1b0986aa67 100644 --- a/drivers/phy/microchip/sparx5_serdes.c +++ b/drivers/phy/microchip/sparx5_serdes.c @@ -2475,10 +2475,10 @@ static int sparx5_serdes_probe(struct platform_device *pdev) return -EINVAL; } iomem = devm_ioremap(priv->dev, iores->start, resource_size(iores)); - if (IS_ERR(iomem)) { + if (!iomem) { dev_err(priv->dev, "Unable to get serdes registers: %s\n", iores->name); - return PTR_ERR(iomem); + return -ENOMEM; } for (idx = 0; idx < ARRAY_SIZE(sparx5_serdes_iomap); idx++) { struct sparx5_serdes_io_resource *iomap = &sparx5_serdes_iomap[idx]; -- cgit v1.2.3-70-g09d2 From a23461c47482fc232ffc9b819539d1f837adf2b1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 25 Oct 2021 13:45:30 +0200 Subject: comedi: vmk80xx: fix transfer-buffer overflows The driver uses endpoint-sized USB transfer buffers but up until recently had no sanity checks on the sizes. Commit e1f13c879a7c ("staging: comedi: check validity of wMaxPacketSize of usb endpoints found") inadvertently fixed NULL-pointer dereferences when accessing the transfer buffers in case a malicious device has a zero wMaxPacketSize. Make sure to allocate buffers large enough to handle also the other accesses that are done without a size check (e.g. byte 18 in vmk80xx_cnt_insn_read() for the VMK8061_MODEL) to avoid writing beyond the buffers, for example, when doing descriptor fuzzing. The original driver was for a low-speed device with 8-byte buffers. Support was later added for a device that uses bulk transfers and is presumably a full-speed device with a maximum 64-byte wMaxPacketSize. Fixes: 985cafccbf9b ("Staging: Comedi: vmk80xx: Add k8061 support") Cc: stable@vger.kernel.org # 2.6.31 Signed-off-by: Johan Hovold Reviewed-by: Ian Abbott Link: https://lore.kernel.org/r/20211025114532.4599-4-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/vmk80xx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/comedi/drivers/vmk80xx.c b/drivers/comedi/drivers/vmk80xx.c index 9f920819cd74..f2c1572d0cd7 100644 --- a/drivers/comedi/drivers/vmk80xx.c +++ b/drivers/comedi/drivers/vmk80xx.c @@ -90,6 +90,8 @@ enum { #define IC3_VERSION BIT(0) #define IC6_VERSION BIT(1) +#define MIN_BUF_SIZE 64 + enum vmk80xx_model { VMK8055_MODEL, VMK8061_MODEL @@ -678,12 +680,12 @@ static int vmk80xx_alloc_usb_buffers(struct comedi_device *dev) struct vmk80xx_private *devpriv = dev->private; size_t size; - size = usb_endpoint_maxp(devpriv->ep_rx); + size = max(usb_endpoint_maxp(devpriv->ep_rx), MIN_BUF_SIZE); devpriv->usb_rx_buf = kzalloc(size, GFP_KERNEL); if (!devpriv->usb_rx_buf) return -ENOMEM; - size = usb_endpoint_maxp(devpriv->ep_tx); + size = max(usb_endpoint_maxp(devpriv->ep_rx), MIN_BUF_SIZE); devpriv->usb_tx_buf = kzalloc(size, GFP_KERNEL); if (!devpriv->usb_tx_buf) return -ENOMEM; -- cgit v1.2.3-70-g09d2 From 78cdfd62bd54af615fba9e3ca1ba35de39d3871d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 25 Oct 2021 13:45:31 +0200 Subject: comedi: vmk80xx: fix bulk-buffer overflow The driver is using endpoint-sized buffers but must not assume that the tx and rx buffers are of equal size or a malicious device could overflow the slab-allocated receive buffer when doing bulk transfers. Fixes: 985cafccbf9b ("Staging: Comedi: vmk80xx: Add k8061 support") Cc: stable@vger.kernel.org # 2.6.31 Signed-off-by: Johan Hovold Reviewed-by: Ian Abbott Link: https://lore.kernel.org/r/20211025114532.4599-5-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/vmk80xx.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/comedi/drivers/vmk80xx.c b/drivers/comedi/drivers/vmk80xx.c index f2c1572d0cd7..9c56918e3b76 100644 --- a/drivers/comedi/drivers/vmk80xx.c +++ b/drivers/comedi/drivers/vmk80xx.c @@ -159,22 +159,20 @@ static void vmk80xx_do_bulk_msg(struct comedi_device *dev) __u8 rx_addr; unsigned int tx_pipe; unsigned int rx_pipe; - size_t size; + size_t tx_size; + size_t rx_size; tx_addr = devpriv->ep_tx->bEndpointAddress; rx_addr = devpriv->ep_rx->bEndpointAddress; tx_pipe = usb_sndbulkpipe(usb, tx_addr); rx_pipe = usb_rcvbulkpipe(usb, rx_addr); - - /* - * The max packet size attributes of the K8061 - * input/output endpoints are identical - */ - size = usb_endpoint_maxp(devpriv->ep_tx); + tx_size = usb_endpoint_maxp(devpriv->ep_tx); + rx_size = usb_endpoint_maxp(devpriv->ep_rx); usb_bulk_msg(usb, tx_pipe, devpriv->usb_tx_buf, - size, NULL, devpriv->ep_tx->bInterval); - usb_bulk_msg(usb, rx_pipe, devpriv->usb_rx_buf, size, NULL, HZ * 10); + tx_size, NULL, devpriv->ep_tx->bInterval); + + usb_bulk_msg(usb, rx_pipe, devpriv->usb_rx_buf, rx_size, NULL, HZ * 10); } static int vmk80xx_read_packet(struct comedi_device *dev) -- cgit v1.2.3-70-g09d2 From a56d3e40bda460edf3f8d6aac00ec0b322b4ab83 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 25 Oct 2021 13:45:32 +0200 Subject: comedi: vmk80xx: fix bulk and interrupt message timeouts USB bulk and interrupt message timeouts are specified in milliseconds and should specifically not vary with CONFIG_HZ. Note that the bulk-out transfer timeout was set to the endpoint bInterval value, which should be ignored for bulk endpoints and is typically set to zero. This meant that a failing bulk-out transfer would never time out. Assume that the 10 second timeout used for all other transfers is more than enough also for the bulk-out endpoint. Fixes: 985cafccbf9b ("Staging: Comedi: vmk80xx: Add k8061 support") Fixes: 951348b37738 ("staging: comedi: vmk80xx: wait for URBs to complete") Cc: stable@vger.kernel.org # 2.6.31 Signed-off-by: Johan Hovold Reviewed-by: Ian Abbott Link: https://lore.kernel.org/r/20211025114532.4599-6-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/vmk80xx.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/comedi/drivers/vmk80xx.c b/drivers/comedi/drivers/vmk80xx.c index 9c56918e3b76..4b00a9ea611a 100644 --- a/drivers/comedi/drivers/vmk80xx.c +++ b/drivers/comedi/drivers/vmk80xx.c @@ -91,6 +91,7 @@ enum { #define IC6_VERSION BIT(1) #define MIN_BUF_SIZE 64 +#define PACKET_TIMEOUT 10000 /* ms */ enum vmk80xx_model { VMK8055_MODEL, @@ -169,10 +170,11 @@ static void vmk80xx_do_bulk_msg(struct comedi_device *dev) tx_size = usb_endpoint_maxp(devpriv->ep_tx); rx_size = usb_endpoint_maxp(devpriv->ep_rx); - usb_bulk_msg(usb, tx_pipe, devpriv->usb_tx_buf, - tx_size, NULL, devpriv->ep_tx->bInterval); + usb_bulk_msg(usb, tx_pipe, devpriv->usb_tx_buf, tx_size, NULL, + PACKET_TIMEOUT); - usb_bulk_msg(usb, rx_pipe, devpriv->usb_rx_buf, rx_size, NULL, HZ * 10); + usb_bulk_msg(usb, rx_pipe, devpriv->usb_rx_buf, rx_size, NULL, + PACKET_TIMEOUT); } static int vmk80xx_read_packet(struct comedi_device *dev) @@ -191,7 +193,7 @@ static int vmk80xx_read_packet(struct comedi_device *dev) pipe = usb_rcvintpipe(usb, ep->bEndpointAddress); return usb_interrupt_msg(usb, pipe, devpriv->usb_rx_buf, usb_endpoint_maxp(ep), NULL, - HZ * 10); + PACKET_TIMEOUT); } static int vmk80xx_write_packet(struct comedi_device *dev, int cmd) @@ -212,7 +214,7 @@ static int vmk80xx_write_packet(struct comedi_device *dev, int cmd) pipe = usb_sndintpipe(usb, ep->bEndpointAddress); return usb_interrupt_msg(usb, pipe, devpriv->usb_tx_buf, usb_endpoint_maxp(ep), NULL, - HZ * 10); + PACKET_TIMEOUT); } static int vmk80xx_reset_device(struct comedi_device *dev) -- cgit v1.2.3-70-g09d2 From 63b3e810eff65fb8587fcb26fa0b56802be12dcf Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 25 Oct 2021 13:58:11 +0200 Subject: most: fix control-message timeouts USB control-message timeouts are specified in milliseconds and should specifically not vary with CONFIG_HZ. Use the common control-message timeout defines for the five-second timeouts. Fixes: 97a6f772f36b ("drivers: most: add USB adapter driver") Cc: stable@vger.kernel.org # 5.9 Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20211025115811.5410-1-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/most/most_usb.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/most/most_usb.c b/drivers/most/most_usb.c index 2640c5b326a4..acabb7715b42 100644 --- a/drivers/most/most_usb.c +++ b/drivers/most/most_usb.c @@ -149,7 +149,8 @@ static inline int drci_rd_reg(struct usb_device *dev, u16 reg, u16 *buf) retval = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), DRCI_READ_REQ, req_type, 0x0000, - reg, dma_buf, sizeof(*dma_buf), 5 * HZ); + reg, dma_buf, sizeof(*dma_buf), + USB_CTRL_GET_TIMEOUT); *buf = le16_to_cpu(*dma_buf); kfree(dma_buf); @@ -176,7 +177,7 @@ static inline int drci_wr_reg(struct usb_device *dev, u16 reg, u16 data) reg, NULL, 0, - 5 * HZ); + USB_CTRL_SET_TIMEOUT); } static inline int start_sync_ep(struct usb_device *usb_dev, u16 ep) -- cgit v1.2.3-70-g09d2 From 57869c1174287c7092226a80ca1b1c9f9363d9a5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 15 Jul 2021 21:06:06 +0200 Subject: extcon: usb-gpio: Use the right includes The USB GPIO extcon driver does not use any of the legacy includes or but exploits the fact that this brings in . Fix this up by using the right includes. Signed-off-by: Linus Walleij Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-usb-gpio.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c index f06be6d4e2a9..0cb440bdd5cb 100644 --- a/drivers/extcon/extcon-usb-gpio.c +++ b/drivers/extcon/extcon-usb-gpio.c @@ -7,18 +7,17 @@ */ #include -#include #include #include #include #include #include #include -#include #include #include #include #include +#include #define USB_GPIO_DEBOUNCE_MS 20 /* ms */ -- cgit v1.2.3-70-g09d2 From 3177308a9421f7735482fd56091bc09ef521b933 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 15 Jul 2021 17:26:57 +0200 Subject: extcon: max3355: Drop unused include This driver includes the legacy header but does not use it. Drop this include. Cc: Sergei Shtylyov Signed-off-by: Linus Walleij Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-max3355.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/extcon/extcon-max3355.c b/drivers/extcon/extcon-max3355.c index fa01926c09f1..d7795607f693 100644 --- a/drivers/extcon/extcon-max3355.c +++ b/drivers/extcon/extcon-max3355.c @@ -7,7 +7,6 @@ */ #include -#include #include #include #include -- cgit v1.2.3-70-g09d2 From 968bd3f0388b8eaf6746336856348dc3ddf2ed39 Mon Sep 17 00:00:00 2001 From: Fabio Aiuto Date: Thu, 16 Sep 2021 09:12:55 +0200 Subject: extcon: extcon-axp288: Use P-Unit semaphore lock for register accesses use low level P-Unit semaphore lock for axp288 register accesses directly and for more than one access a time, to reduce the number of times this semaphore is locked and released which is an expensive operation. i2c-bus to the XPower is shared between the kernel and the SoCs P-Unit. The P-Unit has a semaphore wich the kernel must lock for axp288 register accesses. When the P-Unit semaphore is locked CPU and GPU power states cannot change or the system will freeze. The P-Unit semaphore lock is already managed inside the regmap access logic, but for each access the semaphore is locked and released. So use directly iosf_mbi_(un)block_punit_i2c_access(), we are safe in doing so because nested calls to the same semaphore are turned to nops. Suggested-by: Hans de Goede Reviewed-by: Hans de Goede Tested-by: Hans de Goede Signed-off-by: Fabio Aiuto Signed-off-by: Chanwoo Choi --- drivers/extcon/Kconfig | 2 +- drivers/extcon/extcon-axp288.c | 31 +++++++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index c69d40ae5619..aab87c9b35c8 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -23,7 +23,7 @@ config EXTCON_ADC_JACK config EXTCON_AXP288 tristate "X-Power AXP288 EXTCON support" - depends on MFD_AXP20X && USB_SUPPORT && X86 && ACPI + depends on MFD_AXP20X && USB_SUPPORT && X86 && ACPI && IOSF_MBI select USB_ROLE_SWITCH help Say Y here to enable support for USB peripheral detection diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c index fdb31954cf2b..7c6d5857ff25 100644 --- a/drivers/extcon/extcon-axp288.c +++ b/drivers/extcon/extcon-axp288.c @@ -24,6 +24,7 @@ #include #include +#include /* Power source status register */ #define PS_STAT_VBUS_TRIGGER BIT(0) @@ -215,6 +216,10 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info) unsigned int cable = info->previous_cable; bool vbus_attach = false; + ret = iosf_mbi_block_punit_i2c_access(); + if (ret < 0) + return ret; + vbus_attach = axp288_get_vbus_attach(info); if (!vbus_attach) goto no_vbus; @@ -253,6 +258,8 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info) } no_vbus: + iosf_mbi_unblock_punit_i2c_access(); + extcon_set_state_sync(info->edev, info->previous_cable, false); if (info->previous_cable == EXTCON_CHG_USB_SDP) extcon_set_state_sync(info->edev, EXTCON_USB, false); @@ -275,6 +282,8 @@ no_vbus: return 0; dev_det_ret: + iosf_mbi_unblock_punit_i2c_access(); + if (ret < 0) dev_err(info->dev, "failed to detect BC Mod\n"); @@ -305,13 +314,23 @@ static irqreturn_t axp288_extcon_isr(int irq, void *data) return IRQ_HANDLED; } -static void axp288_extcon_enable(struct axp288_extcon_info *info) +static int axp288_extcon_enable(struct axp288_extcon_info *info) { + int ret = 0; + + ret = iosf_mbi_block_punit_i2c_access(); + if (ret < 0) + return ret; + regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG, BC_GLOBAL_RUN, 0); /* Enable the charger detection logic */ regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG, BC_GLOBAL_RUN, BC_GLOBAL_RUN); + + iosf_mbi_unblock_punit_i2c_access(); + + return ret; } static void axp288_put_role_sw(void *data) @@ -384,10 +403,16 @@ static int axp288_extcon_probe(struct platform_device *pdev) } } + ret = iosf_mbi_block_punit_i2c_access(); + if (ret < 0) + return ret; + info->vbus_attach = axp288_get_vbus_attach(info); axp288_extcon_log_rsi(info); + iosf_mbi_unblock_punit_i2c_access(); + /* Initialize extcon device */ info->edev = devm_extcon_dev_allocate(&pdev->dev, axp288_extcon_cables); @@ -441,7 +466,9 @@ static int axp288_extcon_probe(struct platform_device *pdev) } /* Start charger cable type detection */ - axp288_extcon_enable(info); + ret = axp288_extcon_enable(info); + if (ret < 0) + return ret; device_init_wakeup(dev, true); platform_set_drvdata(pdev, info); -- cgit v1.2.3-70-g09d2 From 70c55d6be634e5f9894169340f3fe5c73f53ac2d Mon Sep 17 00:00:00 2001 From: Yassine Oudjana Date: Sat, 25 Sep 2021 05:45:39 +0000 Subject: extcon: usbc-tusb320: Add support for mode setting and reset Reset the chip and set its mode to default (maintain mode set by PORT pin) during probe to make sure it comes up in the default state. Signed-off-by: Yassine Oudjana Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-usbc-tusb320.c | 85 ++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 4 deletions(-) diff --git a/drivers/extcon/extcon-usbc-tusb320.c b/drivers/extcon/extcon-usbc-tusb320.c index 805af73b4152..1ed1dfe54206 100644 --- a/drivers/extcon/extcon-usbc-tusb320.c +++ b/drivers/extcon/extcon-usbc-tusb320.c @@ -19,15 +19,32 @@ #define TUSB320_REG9_ATTACHED_STATE_MASK 0x3 #define TUSB320_REG9_CABLE_DIRECTION BIT(5) #define TUSB320_REG9_INTERRUPT_STATUS BIT(4) -#define TUSB320_ATTACHED_STATE_NONE 0x0 -#define TUSB320_ATTACHED_STATE_DFP 0x1 -#define TUSB320_ATTACHED_STATE_UFP 0x2 -#define TUSB320_ATTACHED_STATE_ACC 0x3 + +#define TUSB320_REGA 0xa +#define TUSB320_REGA_I2C_SOFT_RESET BIT(3) +#define TUSB320_REGA_MODE_SELECT_SHIFT 4 +#define TUSB320_REGA_MODE_SELECT_MASK 0x3 + +enum tusb320_attached_state { + TUSB320_ATTACHED_STATE_NONE, + TUSB320_ATTACHED_STATE_DFP, + TUSB320_ATTACHED_STATE_UFP, + TUSB320_ATTACHED_STATE_ACC, +}; + +enum tusb320_mode { + TUSB320_MODE_PORT, + TUSB320_MODE_UFP, + TUSB320_MODE_DFP, + TUSB320_MODE_DRP, +}; struct tusb320_priv { struct device *dev; struct regmap *regmap; struct extcon_dev *edev; + + enum tusb320_attached_state state; }; static const char * const tusb_attached_states[] = { @@ -62,6 +79,53 @@ static int tusb320_check_signature(struct tusb320_priv *priv) return 0; } +static int tusb320_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode) +{ + int ret; + + /* Mode cannot be changed while cable is attached */ + if (priv->state != TUSB320_ATTACHED_STATE_NONE) + return -EBUSY; + + /* Write mode */ + ret = regmap_write_bits(priv->regmap, TUSB320_REGA, + TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT, + mode << TUSB320_REGA_MODE_SELECT_SHIFT); + if (ret) { + dev_err(priv->dev, "failed to write mode: %d\n", ret); + return ret; + } + + return 0; +} + +static int tusb320_reset(struct tusb320_priv *priv) +{ + int ret; + + /* Set mode to default (follow PORT pin) */ + ret = tusb320_set_mode(priv, TUSB320_MODE_PORT); + if (ret && ret != -EBUSY) { + dev_err(priv->dev, + "failed to set mode to PORT: %d\n", ret); + return ret; + } + + /* Perform soft reset */ + ret = regmap_write_bits(priv->regmap, TUSB320_REGA, + TUSB320_REGA_I2C_SOFT_RESET, 1); + if (ret) { + dev_err(priv->dev, + "failed to write soft reset bit: %d\n", ret); + return ret; + } + + /* Wait for chip to go through reset */ + msleep(95); + + return 0; +} + static irqreturn_t tusb320_irq_handler(int irq, void *dev_id) { struct tusb320_priv *priv = dev_id; @@ -96,6 +160,8 @@ static irqreturn_t tusb320_irq_handler(int irq, void *dev_id) extcon_sync(priv->edev, EXTCON_USB); extcon_sync(priv->edev, EXTCON_USB_HOST); + priv->state = state; + regmap_write(priv->regmap, TUSB320_REG9, reg); return IRQ_HANDLED; @@ -145,6 +211,17 @@ static int tusb320_extcon_probe(struct i2c_client *client, /* update initial state */ tusb320_irq_handler(client->irq, priv); + /* Reset chip to its default state */ + ret = tusb320_reset(priv); + if (ret) + dev_warn(priv->dev, "failed to reset chip: %d\n", ret); + else + /* + * State and polarity might change after a reset, so update + * them again and make sure the interrupt status bit is cleared. + */ + tusb320_irq_handler(client->irq, priv); + ret = devm_request_threaded_irq(priv->dev, client->irq, NULL, tusb320_irq_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, -- cgit v1.2.3-70-g09d2 From ce0320bd3872038569be360870e2d5251b975692 Mon Sep 17 00:00:00 2001 From: Yassine Oudjana Date: Sat, 25 Sep 2021 05:45:54 +0000 Subject: extcon: usbc-tusb320: Add support for TUSB320L TUSB320L is a newer chip with additional features, and it has additional steps in its mode changing sequence: - Disable CC state machine, - Write to mode register, - Wait for 5 ms, - Re-enable CC state machine. It also has an additional register that a revision number can be read from. Add support for the mode changing sequence, and read the revision number during probe and print it as info. Signed-off-by: Yassine Oudjana Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-usbc-tusb320.c | 82 ++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 3 deletions(-) diff --git a/drivers/extcon/extcon-usbc-tusb320.c b/drivers/extcon/extcon-usbc-tusb320.c index 1ed1dfe54206..6ba3d89b106d 100644 --- a/drivers/extcon/extcon-usbc-tusb320.c +++ b/drivers/extcon/extcon-usbc-tusb320.c @@ -21,10 +21,13 @@ #define TUSB320_REG9_INTERRUPT_STATUS BIT(4) #define TUSB320_REGA 0xa +#define TUSB320L_REGA_DISABLE_TERM BIT(0) #define TUSB320_REGA_I2C_SOFT_RESET BIT(3) #define TUSB320_REGA_MODE_SELECT_SHIFT 4 #define TUSB320_REGA_MODE_SELECT_MASK 0x3 +#define TUSB320L_REGA0_REVISION 0xa0 + enum tusb320_attached_state { TUSB320_ATTACHED_STATE_NONE, TUSB320_ATTACHED_STATE_DFP, @@ -39,11 +42,18 @@ enum tusb320_mode { TUSB320_MODE_DRP, }; +struct tusb320_priv; + +struct tusb320_ops { + int (*set_mode)(struct tusb320_priv *priv, enum tusb320_mode mode); + int (*get_revision)(struct tusb320_priv *priv, unsigned int *revision); +}; + struct tusb320_priv { struct device *dev; struct regmap *regmap; struct extcon_dev *edev; - + struct tusb320_ops *ops; enum tusb320_attached_state state; }; @@ -99,12 +109,46 @@ static int tusb320_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode) return 0; } +static int tusb320l_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode) +{ + int ret; + + /* Disable CC state machine */ + ret = regmap_write_bits(priv->regmap, TUSB320_REGA, + TUSB320L_REGA_DISABLE_TERM, 1); + if (ret) { + dev_err(priv->dev, + "failed to disable CC state machine: %d\n", ret); + return ret; + } + + /* Write mode */ + ret = regmap_write_bits(priv->regmap, TUSB320_REGA, + TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT, + mode << TUSB320_REGA_MODE_SELECT_SHIFT); + if (ret) { + dev_err(priv->dev, "failed to write mode: %d\n", ret); + goto err; + } + + msleep(5); +err: + /* Re-enable CC state machine */ + ret = regmap_write_bits(priv->regmap, TUSB320_REGA, + TUSB320L_REGA_DISABLE_TERM, 0); + if (ret) + dev_err(priv->dev, + "failed to re-enable CC state machine: %d\n", ret); + + return ret; +} + static int tusb320_reset(struct tusb320_priv *priv) { int ret; /* Set mode to default (follow PORT pin) */ - ret = tusb320_set_mode(priv, TUSB320_MODE_PORT); + ret = priv->ops->set_mode(priv, TUSB320_MODE_PORT); if (ret && ret != -EBUSY) { dev_err(priv->dev, "failed to set mode to PORT: %d\n", ret); @@ -126,6 +170,20 @@ static int tusb320_reset(struct tusb320_priv *priv) return 0; } +static int tusb320l_get_revision(struct tusb320_priv *priv, unsigned int *revision) +{ + return regmap_read(priv->regmap, TUSB320L_REGA0_REVISION, revision); +} + +static struct tusb320_ops tusb320_ops = { + .set_mode = tusb320_set_mode, +}; + +static struct tusb320_ops tusb320l_ops = { + .set_mode = tusb320l_set_mode, + .get_revision = tusb320l_get_revision, +}; + static irqreturn_t tusb320_irq_handler(int irq, void *dev_id) { struct tusb320_priv *priv = dev_id; @@ -176,6 +234,8 @@ static int tusb320_extcon_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct tusb320_priv *priv; + const void *match_data; + unsigned int revision; int ret; priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); @@ -191,12 +251,27 @@ static int tusb320_extcon_probe(struct i2c_client *client, if (ret) return ret; + match_data = device_get_match_data(&client->dev); + if (!match_data) + return -EINVAL; + + priv->ops = (struct tusb320_ops*)match_data; + priv->edev = devm_extcon_dev_allocate(priv->dev, tusb320_extcon_cable); if (IS_ERR(priv->edev)) { dev_err(priv->dev, "failed to allocate extcon device\n"); return PTR_ERR(priv->edev); } + if (priv->ops->get_revision) { + ret = priv->ops->get_revision(priv, &revision); + if (ret) + dev_warn(priv->dev, + "failed to read revision register: %d\n", ret); + else + dev_info(priv->dev, "chip revision %d\n", revision); + } + ret = devm_extcon_dev_register(priv->dev, priv->edev); if (ret < 0) { dev_err(priv->dev, "failed to register extcon device\n"); @@ -231,7 +306,8 @@ static int tusb320_extcon_probe(struct i2c_client *client, } static const struct of_device_id tusb320_extcon_dt_match[] = { - { .compatible = "ti,tusb320", }, + { .compatible = "ti,tusb320", .data = &tusb320_ops, }, + { .compatible = "ti,tusb320l", .data = &tusb320l_ops, }, { } }; MODULE_DEVICE_TABLE(of, tusb320_extcon_dt_match); -- cgit v1.2.3-70-g09d2 From 9e6ef3a25e5e13899381282dbd8d8d157a899262 Mon Sep 17 00:00:00 2001 From: Yassine Oudjana Date: Sat, 25 Sep 2021 05:46:09 +0000 Subject: dt-bindings: extcon: usbc-tusb320: Add TUSB320L compatible string Add a compatible string for TUSB320L. Signed-off-by: Yassine Oudjana Acked-by: Rob Herring Acked-by: Chanwoo Choi Signed-off-by: Chanwoo Choi --- Documentation/devicetree/bindings/extcon/extcon-usbc-tusb320.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/extcon/extcon-usbc-tusb320.yaml b/Documentation/devicetree/bindings/extcon/extcon-usbc-tusb320.yaml index 9875b4d5c356..71a9f2e5d0dc 100644 --- a/Documentation/devicetree/bindings/extcon/extcon-usbc-tusb320.yaml +++ b/Documentation/devicetree/bindings/extcon/extcon-usbc-tusb320.yaml @@ -11,7 +11,9 @@ maintainers: properties: compatible: - const: ti,tusb320 + enum: + - ti,tusb320 + - ti,tusb320l reg: maxItems: 1 -- cgit v1.2.3-70-g09d2 From 08e438e6296c566062a2b0627706b5967ceaf183 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Wed, 27 Oct 2021 15:48:43 +1100 Subject: fix for "dma-buf: move dma-buf symbols into the DMA_BUF module namespace" Adds the proper MODULE_IMPORT_NS(DMA_BUF) line to the file to get it to build properly. Signed-off-by: Stephen Rothwell Link: https://lore.kernel.org/r/20211027154843.622961fd@canb.auug.org.au Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/drm_gem_shmem_helper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c index a61946374c82..b25736baba49 100644 --- a/drivers/gpu/drm/drm_gem_shmem_helper.c +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c @@ -17,6 +17,8 @@ #include #include +MODULE_IMPORT_NS(DMA_BUF); + /** * DOC: overview * -- cgit v1.2.3-70-g09d2 From 692c9a499b286ea478f41b23a91fe3873b9e1326 Mon Sep 17 00:00:00 2001 From: Tao Zhang Date: Thu, 19 Aug 2021 17:29:37 +0800 Subject: coresight: cti: Correct the parameter for pm_runtime_put The input parameter of the function pm_runtime_put should be the same in the function cti_enable_hw and cti_disable_hw. The correct parameter to use here should be dev->parent. Signed-off-by: Tao Zhang Reviewed-by: Leo Yan Fixes: 835d722ba10a ("coresight: cti: Initial CoreSight CTI Driver") Cc: stable Link: https://lore.kernel.org/r/1629365377-5937-1-git-send-email-quic_taozha@quicinc.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-cti-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-cti-core.c b/drivers/hwtracing/coresight/coresight-cti-core.c index e2a3620cbf48..8988b2ed2ea6 100644 --- a/drivers/hwtracing/coresight/coresight-cti-core.c +++ b/drivers/hwtracing/coresight/coresight-cti-core.c @@ -175,7 +175,7 @@ static int cti_disable_hw(struct cti_drvdata *drvdata) coresight_disclaim_device_unlocked(csdev); CS_LOCK(drvdata->base); spin_unlock(&drvdata->spinlock); - pm_runtime_put(dev); + pm_runtime_put(dev->parent); return 0; /* not disabled this call */ -- cgit v1.2.3-70-g09d2 From 204879e6990d2a57d7a6e26792cec34f97a63c0e Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 3 Sep 2021 18:28:54 -0700 Subject: coresight: cpu-debug: Control default behavior via Kconfig Debugfs is nice and so are module parameters, but * debugfs doesn't take effect early (e.g., if drivers are locking up before user space gets anywhere) * module parameters either add a lot to the kernel command line, or else take effect late as well (if you build =m and configure in /etc/modprobe.d/) So in the same spirit as these CONFIG_PANIC_ON_OOPS (also available via cmdline or modparam) CONFIG_INTEL_IOMMU_DEFAULT_ON (also available via cmdline) add a new Kconfig option. Module parameters and debugfs can still override. Signed-off-by: Brian Norris Reviewed-by: Leo Yan [Fixed missing double quote in Kconfig title] Link: https://lore.kernel.org/r/20210903182839.1.I20856983f2841b78936134dcf9cdf6ecafe632b9@changeid Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/Kconfig | 13 +++++++++++++ drivers/hwtracing/coresight/coresight-cpu-debug.c | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index f026e5c0e777..514a9b8086e3 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -150,6 +150,19 @@ config CORESIGHT_CPU_DEBUG To compile this driver as a module, choose M here: the module will be called coresight-cpu-debug. +config CORESIGHT_CPU_DEBUG_DEFAULT_ON + bool "Enable CoreSight CPU Debug by default" + depends on CORESIGHT_CPU_DEBUG + help + Say Y here to enable the CoreSight Debug panic-debug by default. This + can also be enabled via debugfs, but this ensures the debug feature + is enabled as early as possible. + + Has the same effect as setting coresight_cpu_debug.enable=1 on the + kernel command line. + + Say N if unsure. + config CORESIGHT_CTI tristate "CoreSight Cross Trigger Interface (CTI) driver" depends on ARM || ARM64 diff --git a/drivers/hwtracing/coresight/coresight-cpu-debug.c b/drivers/hwtracing/coresight/coresight-cpu-debug.c index 00de46565bc4..8845ec4b4402 100644 --- a/drivers/hwtracing/coresight/coresight-cpu-debug.c +++ b/drivers/hwtracing/coresight/coresight-cpu-debug.c @@ -105,7 +105,7 @@ static DEFINE_PER_CPU(struct debug_drvdata *, debug_drvdata); static int debug_count; static struct dentry *debug_debugfs_dir; -static bool debug_enable; +static bool debug_enable = IS_ENABLED(CONFIG_CORESIGHT_CPU_DEBUG_DEFAULT_ON); module_param_named(enable, debug_enable, bool, 0600); MODULE_PARM_DESC(enable, "Control to enable coresight CPU debug functionality"); -- cgit v1.2.3-70-g09d2 From 0ab47f8079f27edc44ea0bcc67078561bcfdf542 Mon Sep 17 00:00:00 2001 From: Tanmay Jagdale Date: Wed, 1 Sep 2021 18:40:48 +0530 Subject: dt-bindings: coresight: Add burst size for TMC Add "arm,max-burst-size" optional property for TMC ETR. If specified, this value indicates the maximum burst size that can be initiated by TMC on the AXI bus. Signed-off-by: Tanmay Jagdale Reviewed-by: Mike Leach Acked-by: Rob Herring Link: https://lore.kernel.org/r/20210901131049.1365367-2-tanmay@marvell.com Signed-off-by: Mathieu Poirier --- Documentation/devicetree/bindings/arm/coresight.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt index 7f9c1ca87487..c68d93a35b6c 100644 --- a/Documentation/devicetree/bindings/arm/coresight.txt +++ b/Documentation/devicetree/bindings/arm/coresight.txt @@ -127,6 +127,11 @@ its hardware characteristcs. * arm,scatter-gather: boolean. Indicates that the TMC-ETR can safely use the SG mode on this system. + * arm,max-burst-size: The maximum burst size initiated by TMC on the + AXI master interface. The burst size can be in the range [0..15], + the setting supports one data transfer per burst up to a maximum of + 16 data transfers per burst. + * Optional property for CATU : * interrupts : Exactly one SPI may be listed for reporting the address error -- cgit v1.2.3-70-g09d2 From 4d5d88baa6c838bf92ed6a63c50dd3167c5a4956 Mon Sep 17 00:00:00 2001 From: Tanmay Jagdale Date: Wed, 1 Sep 2021 18:40:49 +0530 Subject: coresight: tmc: Configure AXI write burst size The current driver sets the write burst size initiated by TMC-ETR on AXI bus to a fixed value of 16. Make this configurable by reading the value specified in fwnode. If not specified, then default to 16. Introduced a "max_burst_size" variable in tmc_drvdata structure to facilitate this change. Signed-off-by: Tanmay Jagdale Reviewed-by: Mike Leach Link: https://lore.kernel.org/r/20210901131049.1365367-3-tanmay@marvell.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-tmc-core.c | 21 +++++++++++++++++++-- drivers/hwtracing/coresight/coresight-tmc-etr.c | 3 ++- drivers/hwtracing/coresight/coresight-tmc.h | 6 +++++- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c index 74c6323d4d6a..d0276af82494 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-core.c +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c @@ -432,6 +432,21 @@ static u32 tmc_etr_get_default_buffer_size(struct device *dev) return size; } +static u32 tmc_etr_get_max_burst_size(struct device *dev) +{ + u32 burst_size; + + if (fwnode_property_read_u32(dev->fwnode, "arm,max-burst-size", + &burst_size)) + return TMC_AXICTL_WR_BURST_16; + + /* Only permissible values are 0 to 15 */ + if (burst_size > 0xF) + burst_size = TMC_AXICTL_WR_BURST_16; + + return burst_size; +} + static int tmc_probe(struct amba_device *adev, const struct amba_id *id) { int ret = 0; @@ -469,10 +484,12 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) /* This device is not associated with a session */ drvdata->pid = -1; - if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) + if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { drvdata->size = tmc_etr_get_default_buffer_size(dev); - else + drvdata->max_burst_size = tmc_etr_get_max_burst_size(dev); + } else { drvdata->size = readl_relaxed(drvdata->base + TMC_RSZ) * 4; + } desc.dev = dev; desc.groups = coresight_tmc_groups; diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index acdb59e0e661..0ac2a611110b 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -982,7 +982,8 @@ static void __tmc_etr_enable_hw(struct tmc_drvdata *drvdata) axictl = readl_relaxed(drvdata->base + TMC_AXICTL); axictl &= ~TMC_AXICTL_CLEAR_MASK; - axictl |= (TMC_AXICTL_PROT_CTL_B1 | TMC_AXICTL_WR_BURST_16); + axictl |= TMC_AXICTL_PROT_CTL_B1; + axictl |= TMC_AXICTL_WR_BURST(drvdata->max_burst_size); axictl |= TMC_AXICTL_AXCACHE_OS; if (tmc_etr_has_cap(drvdata, TMC_ETR_AXI_ARCACHE)) { diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index b91ec7dde7bc..6bec20a392b3 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -70,7 +70,8 @@ #define TMC_AXICTL_PROT_CTL_B0 BIT(0) #define TMC_AXICTL_PROT_CTL_B1 BIT(1) #define TMC_AXICTL_SCT_GAT_MODE BIT(7) -#define TMC_AXICTL_WR_BURST_16 0xF00 +#define TMC_AXICTL_WR_BURST(v) (((v) & 0xf) << 8) +#define TMC_AXICTL_WR_BURST_16 0xf /* Write-back Read and Write-allocate */ #define TMC_AXICTL_AXCACHE_OS (0xf << 2) #define TMC_AXICTL_ARCACHE_OS (0xf << 16) @@ -174,6 +175,8 @@ struct etr_buf { * @etr_buf: details of buffer used in TMC-ETR * @len: size of the available trace for ETF/ETB. * @size: trace buffer size for this TMC (common for all modes). + * @max_burst_size: The maximum burst size that can be initiated by + * TMC-ETR on AXI bus. * @mode: how this TMC is being used. * @config_type: TMC variant, must be of type @tmc_config_type. * @memwidth: width of the memory interface databus, in bytes. @@ -198,6 +201,7 @@ struct tmc_drvdata { }; u32 len; u32 size; + u32 max_burst_size; u32 mode; enum tmc_config_type config_type; enum tmc_mem_intf_width memwidth; -- cgit v1.2.3-70-g09d2 From 26701ceb4c2c6bfa5f8c8984fa1a1ea08fa0f02c Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Mon, 9 Aug 2021 19:14:00 +0800 Subject: coresight: tmc-etr: Add barrier after updating AUX ring buffer Since a memory barrier is required between AUX trace data store and aux_head store, and the AUX trace data is filled with memcpy(), it's sufficient to use smp_wmb() so can ensure the trace data is visible prior to updating aux_head. Signed-off-by: Leo Yan Reviewed-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20210809111407.596077-3-leo.yan@linaro.org Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-tmc-etr.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 0ac2a611110b..7f4654b72314 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -1564,6 +1564,14 @@ tmc_update_etr_buffer(struct coresight_device *csdev, */ if (etr_perf->snapshot) handle->head += size; + + /* + * Ensure that the AUX trace data is visible before the aux_head + * is updated via perf_aux_output_end(), as expected by the + * perf ring buffer. + */ + smp_wmb(); + out: /* * Don't set the TRUNCATED flag in snapshot mode because 1) the -- cgit v1.2.3-70-g09d2 From bd8d06886d0a9b59d020fdb2496c76db77816768 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Mon, 9 Aug 2021 19:14:01 +0800 Subject: coresight: tmc-etf: Add comment for store ordering Since the function CS_LOCK() has contained memory barrier mb(), it ensures the visibility of the AUX trace data before updating the aux_head, thus it's needless to add any explicit barrier anymore. Add comment to make clear for the barrier usage for ETF. Signed-off-by: Leo Yan Link: https://lore.kernel.org/r/20210809111407.596077-4-leo.yan@linaro.org Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-tmc-etf.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index cd0fb7bfba68..8debd4f40f06 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -553,6 +553,11 @@ static unsigned long tmc_update_etf_buffer(struct coresight_device *csdev, if (buf->snapshot) handle->head += to_read; + /* + * CS_LOCK() contains mb() so it can ensure visibility of the AUX trace + * data before the aux_head is updated via perf_aux_output_end(), which + * is expected by the perf ring buffer. + */ CS_LOCK(drvdata->base); out: spin_unlock_irqrestore(&drvdata->spinlock, flags); -- cgit v1.2.3-70-g09d2 From f36dec8da1a4a083c51e4d2297985d3648b6740f Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Sun, 12 Sep 2021 20:57:47 +0800 Subject: coresight: tmc-etr: Use perf_output_handle::head for AUX ring buffer When enable the Arm CoreSight PMU event, the context for AUX ring buffer is prepared in the structure perf_output_handle, and its field "head" points the head of the AUX ring buffer and it is updated after filling AUX trace data into buffer. Current code uses an extra field etr_perf_buffer::head to maintain the header for the AUX ring buffer which is not necessary; alternatively, it's better to directly use perf_output_handle::head. This patch removes the field etr_perf_buffer::head and directly uses perf_output_handle::head for the head of AUX ring buffer. Signed-off-by: Leo Yan Reviewed-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20210912125748.2816606-2-leo.yan@linaro.org Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-tmc-etr.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 7f4654b72314..14a86a232a18 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -32,7 +32,6 @@ struct etr_flat_buf { * @etr_buf - Actual buffer used by the ETR * @pid - The PID this etr_perf_buffer belongs to. * @snaphost - Perf session mode - * @head - handle->head at the beginning of the session. * @nr_pages - Number of pages in the ring buffer. * @pages - Array of Pages in the ring buffer. */ @@ -41,7 +40,6 @@ struct etr_perf_buffer { struct etr_buf *etr_buf; pid_t pid; bool snapshot; - unsigned long head; int nr_pages; void **pages; }; @@ -1438,16 +1436,16 @@ free_etr_perf_buffer: * buffer to the perf ring buffer. */ static void tmc_etr_sync_perf_buffer(struct etr_perf_buffer *etr_perf, + unsigned long head, unsigned long src_offset, unsigned long to_copy) { long bytes; long pg_idx, pg_offset; - unsigned long head = etr_perf->head; char **dst_pages, *src_buf; struct etr_buf *etr_buf = etr_perf->etr_buf; - head = etr_perf->head; + head = PERF_IDX2OFF(head, etr_perf); pg_idx = head >> PAGE_SHIFT; pg_offset = head & (PAGE_SIZE - 1); dst_pages = (char **)etr_perf->pages; @@ -1554,7 +1552,7 @@ tmc_update_etr_buffer(struct coresight_device *csdev, /* Insert barrier packets at the beginning, if there was an overflow */ if (lost) tmc_etr_buf_insert_barrier_packet(etr_buf, offset); - tmc_etr_sync_perf_buffer(etr_perf, offset, size); + tmc_etr_sync_perf_buffer(etr_perf, handle->head, offset, size); /* * In snapshot mode we simply increment the head by the number of byte @@ -1614,8 +1612,6 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) goto unlock_out; } - etr_perf->head = PERF_IDX2OFF(handle->head, etr_perf); - /* * No HW configuration is needed if the sink is already in * use for this session. -- cgit v1.2.3-70-g09d2 From 7ba7ae1d5a4737ebea582526ada9d79c74dfd75d Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Sun, 12 Sep 2021 20:57:48 +0800 Subject: coresight: Update comments for removing cs_etm_find_snapshot() Commit 2f01c200d440 ("perf cs-etm: Remove callback cs_etm_find_snapshot()") has removed the function cs_etm_find_snapshot() from the perf tool in the user space, now CoreSight trace directly uses the perf common function __auxtrace_mmap__read() to calcualte the head and size for AUX trace data in snapshot mode. This patch updates the comments in drivers to make them generic and not stick to any specific function from perf tool. Signed-off-by: Leo Yan Link: https://lore.kernel.org/r/20210912125748.2816606-3-leo.yan@linaro.org Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-etb10.c | 5 ++--- drivers/hwtracing/coresight/coresight-tmc-etf.c | 5 ++--- drivers/hwtracing/coresight/coresight-tmc-etr.c | 5 ++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index f775cbee12b8..efa39820acec 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -557,9 +557,8 @@ static unsigned long etb_update_buffer(struct coresight_device *csdev, /* * In snapshot mode we simply increment the head by the number of byte - * that were written. User space function cs_etm_find_snapshot() will - * figure out how many bytes to get from the AUX buffer based on the - * position of the head. + * that were written. User space will figure out how many bytes to get + * from the AUX buffer based on the position of the head. */ if (buf->snapshot) handle->head += to_read; diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 8debd4f40f06..4c4cbd1f7258 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -546,9 +546,8 @@ static unsigned long tmc_update_etf_buffer(struct coresight_device *csdev, /* * In snapshot mode we simply increment the head by the number of byte - * that were written. User space function cs_etm_find_snapshot() will - * figure out how many bytes to get from the AUX buffer based on the - * position of the head. + * that were written. User space will figure out how many bytes to get + * from the AUX buffer based on the position of the head. */ if (buf->snapshot) handle->head += to_read; diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 14a86a232a18..d6da38e7c682 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -1556,9 +1556,8 @@ tmc_update_etr_buffer(struct coresight_device *csdev, /* * In snapshot mode we simply increment the head by the number of byte - * that were written. User space function cs_etm_find_snapshot() will - * figure out how many bytes to get from the AUX buffer based on the - * position of the head. + * that were written. User space will figure out how many bytes to get + * from the AUX buffer based on the position of the head. */ if (etr_perf->snapshot) handle->head += size; -- cgit v1.2.3-70-g09d2 From 0abd076217a39c4abc47dcd84d0c8f491f87cbe7 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Sun, 5 Sep 2021 11:21:44 +0800 Subject: coresight: tmc-etr: Speed up for bounce buffer in flat mode The AUX bounce buffer is allocated with API dma_alloc_coherent(), in the low level's architecture code, e.g. for Arm64, it maps the memory with the attribution "Normal non-cacheable"; this can be concluded from the definition for pgprot_dmacoherent() in arch/arm64/include/asm/pgtable.h. Later when access the AUX bounce buffer, since the memory mapping is non-cacheable, it's low efficiency due to every load instruction must reach out DRAM. This patch changes to allocate pages with dma_alloc_noncoherent(), the driver can access the memory via cacheable mapping; therefore, load instructions can fetch data from cache lines rather than always read data from DRAM, the driver can boost memory performance. After using the cacheable mapping, the driver uses dma_sync_single_for_cpu() to invalidate cacheline prior to read bounce buffer so can avoid read stale trace data. By measurement the duration for function tmc_update_etr_buffer() with ftrace function_graph tracer, it shows the performance significant improvement for copying 4MiB data from bounce buffer: # echo tmc_etr_get_data_flat_buf > set_graph_notrace // avoid noise # echo tmc_update_etr_buffer > set_graph_function # echo function_graph > current_tracer before: # CPU DURATION FUNCTION CALLS # | | | | | | | 2) | tmc_update_etr_buffer() { ... 2) # 8148.320 us | } after: # CPU DURATION FUNCTION CALLS # | | | | | | | 2) | tmc_update_etr_buffer() { ... 2) # 2525.420 us | } Signed-off-by: Leo Yan Reviewed-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20210905032144.966766-1-leo.yan@linaro.org Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-tmc-etr.c | 26 +++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index d6da38e7c682..867ad8bb9b0c 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -607,8 +607,9 @@ static int tmc_etr_alloc_flat_buf(struct tmc_drvdata *drvdata, if (!flat_buf) return -ENOMEM; - flat_buf->vaddr = dma_alloc_coherent(real_dev, etr_buf->size, - &flat_buf->daddr, GFP_KERNEL); + flat_buf->vaddr = dma_alloc_noncoherent(real_dev, etr_buf->size, + &flat_buf->daddr, + DMA_FROM_DEVICE, GFP_KERNEL); if (!flat_buf->vaddr) { kfree(flat_buf); return -ENOMEM; @@ -629,14 +630,18 @@ static void tmc_etr_free_flat_buf(struct etr_buf *etr_buf) if (flat_buf && flat_buf->daddr) { struct device *real_dev = flat_buf->dev->parent; - dma_free_coherent(real_dev, flat_buf->size, - flat_buf->vaddr, flat_buf->daddr); + dma_free_noncoherent(real_dev, etr_buf->size, + flat_buf->vaddr, flat_buf->daddr, + DMA_FROM_DEVICE); } kfree(flat_buf); } static void tmc_etr_sync_flat_buf(struct etr_buf *etr_buf, u64 rrp, u64 rwp) { + struct etr_flat_buf *flat_buf = etr_buf->private; + struct device *real_dev = flat_buf->dev->parent; + /* * Adjust the buffer to point to the beginning of the trace data * and update the available trace data. @@ -646,6 +651,19 @@ static void tmc_etr_sync_flat_buf(struct etr_buf *etr_buf, u64 rrp, u64 rwp) etr_buf->len = etr_buf->size; else etr_buf->len = rwp - rrp; + + /* + * The driver always starts tracing at the beginning of the buffer, + * the only reason why we would get a wrap around is when the buffer + * is full. Sync the entire buffer in one go for this case. + */ + if (etr_buf->offset + etr_buf->len > etr_buf->size) + dma_sync_single_for_cpu(real_dev, flat_buf->daddr, + etr_buf->size, DMA_FROM_DEVICE); + else + dma_sync_single_for_cpu(real_dev, + flat_buf->daddr + etr_buf->offset, + etr_buf->len, DMA_FROM_DEVICE); } static ssize_t tmc_etr_get_data_flat_buf(struct etr_buf *etr_buf, -- cgit v1.2.3-70-g09d2 From 8c60acbcb982bfff14d1e85094474671c4f3d006 Mon Sep 17 00:00:00 2001 From: James Clark Date: Wed, 22 Sep 2021 13:51:44 +0100 Subject: coresight: Don't immediately close events that are run on invalid CPU/sink combos When a traced process runs on a CPU that can't reach the selected sink, the event will be stopped with PERF_HES_STOPPED. This means that even if the process migrates to a valid CPU, tracing will not resume. This can be reproduced (on N1SDP) by using taskset to start the process on CPU 0, and then switching it to CPU 2 (ETF 1 is only reachable from CPU 2): taskset --cpu-list 0 ./perf record -e cs_etm/@tmc_etf1/ --per-thread -- taskset --cpu-list 2 ls This produces a single 0 length AUX record, and then no more trace: 0x3c8 [0x30]: PERF_RECORD_AUX offset: 0 size: 0 flags: 0x1 [T] After the fix, the same command produces normal AUX records. The perf self test "89: Check Arm CoreSight trace data recording and synthesized samples" no longer fails intermittently. This was because the taskset in the test is after the fork, so there is a period where the task is scheduled on a random CPU rather than forced to a valid one. Specifically selecting an invalid CPU will still result in a failure to open the event because it will never produce trace: ./perf record -C 2 -e cs_etm/@tmc_etf0/ failed to mmap with 12 (Cannot allocate memory) The only scenario that has changed is if the CPU mask has a valid CPU sink combo in it. Testing ======= * Coresight self test passes consistently: ./perf test Coresight * CPU wide mode still produces trace: ./perf record -e cs_etm// -a * Invalid -C options still fail to open: ./perf record -C 2,3 -e cs_etm/@tmc_etf0/ failed to mmap with 12 (Cannot allocate memory) * Migrating a task to a valid sink/CPU now produces trace: taskset --cpu-list 0 ./perf record -e cs_etm/@tmc_etf1/ --per-thread -- taskset --cpu-list 2 ls * If the task remains on an invalid CPU, no trace is emitted: taskset --cpu-list 0 ./perf record -e cs_etm/@tmc_etf1/ --per-thread -- ls Reviewed-by: Suzuki K Poulose Signed-off-by: James Clark Link: https://lore.kernel.org/r/20210922125144.133872-2-james.clark@arm.com Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-etm-perf.c | 29 +++++++++++++++++++----- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 8ebd728d3a80..3bce1ec4c4bf 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -452,9 +452,14 @@ static void etm_event_start(struct perf_event *event, int flags) * sink from this ETM. We can't do much in this case if * the sink was specified or hinted to the driver. For * now, simply don't record anything on this ETM. + * + * As such we pretend that everything is fine, and let + * it continue without actually tracing. The event could + * continue tracing when it moves to a CPU where it is + * reachable to a sink. */ if (!cpumask_test_cpu(cpu, &event_data->mask)) - goto fail_end_stop; + goto out; path = etm_event_cpu_path(event_data, cpu); /* We need a sink, no need to continue without one */ @@ -466,16 +471,15 @@ static void etm_event_start(struct perf_event *event, int flags) if (coresight_enable_path(path, CS_MODE_PERF, handle)) goto fail_end_stop; - /* Tell the perf core the event is alive */ - event->hw.state = 0; - /* Finally enable the tracer */ if (source_ops(csdev)->enable(csdev, event, CS_MODE_PERF)) goto fail_disable_path; +out: + /* Tell the perf core the event is alive */ + event->hw.state = 0; /* Save the event_data for this ETM */ ctxt->event_data = event_data; -out: return; fail_disable_path: @@ -485,7 +489,7 @@ fail_end_stop: perf_aux_output_end(handle, 0); fail: event->hw.state = PERF_HES_STOPPED; - goto out; + return; } static void etm_event_stop(struct perf_event *event, int mode) @@ -517,6 +521,19 @@ static void etm_event_stop(struct perf_event *event, int mode) if (WARN_ON(!event_data)) return; + /* + * Check if this ETM was allowed to trace, as decided at + * etm_setup_aux(). If it wasn't allowed to trace, then + * nothing needs to be torn down other than outputting a + * zero sized record. + */ + if (handle->event && (mode & PERF_EF_UPDATE) && + !cpumask_test_cpu(cpu, &event_data->mask)) { + event->hw.state = PERF_HES_STOPPED; + perf_aux_output_end(handle, 0); + return; + } + if (!csdev) return; -- cgit v1.2.3-70-g09d2 From 937d3f58cacf377cab7c32e475e1ffa91d611dce Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Tue, 14 Sep 2021 11:26:32 +0100 Subject: coresight: etm4x: Save restore TRFCR_EL1 When the CPU enters a low power mode, the TRFCR_EL1 contents could be reset. Thus we need to save/restore the TRFCR_EL1 along with the ETM4x registers to allow the tracing. The TRFCR related helpers are in a new header file, as we need to use them for TRBE in the later patches. Cc: Mathieu Poirier Cc: Anshuman Khandual Cc: Mike Leach Cc: Leo Yan Reviewed-by: Anshuman Khandual Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20210914102641.1852544-2-suzuki.poulose@arm.com [Fixed cosmetic details] Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-etm4x-core.c | 43 ++++++++++++++++------ drivers/hwtracing/coresight/coresight-etm4x.h | 2 + .../coresight/coresight-self-hosted-trace.h | 24 ++++++++++++ 3 files changed, 57 insertions(+), 12 deletions(-) create mode 100644 drivers/hwtracing/coresight/coresight-self-hosted-trace.h diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index e24252eaf8e4..537c0d7ee1ed 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -40,6 +40,7 @@ #include "coresight-etm4x.h" #include "coresight-etm-perf.h" #include "coresight-etm4x-cfg.h" +#include "coresight-self-hosted-trace.h" #include "coresight-syscfg.h" static int boot_enable; @@ -1011,7 +1012,7 @@ static void cpu_enable_tracing(struct etmv4_drvdata *drvdata) if (is_kernel_in_hyp_mode()) trfcr |= TRFCR_EL2_CX; - write_sysreg_s(trfcr, SYS_TRFCR_EL1); + write_trfcr(trfcr); } static void etm4_init_arch_data(void *info) @@ -1554,7 +1555,7 @@ static void etm4_init_trace_id(struct etmv4_drvdata *drvdata) drvdata->trcid = coresight_get_trace_id(drvdata->cpu); } -static int etm4_cpu_save(struct etmv4_drvdata *drvdata) +static int __etm4_cpu_save(struct etmv4_drvdata *drvdata) { int i, ret = 0; struct etmv4_save_state *state; @@ -1693,7 +1694,23 @@ out: return ret; } -static void etm4_cpu_restore(struct etmv4_drvdata *drvdata) +static int etm4_cpu_save(struct etmv4_drvdata *drvdata) +{ + int ret = 0; + + /* Save the TRFCR irrespective of whether the ETM is ON */ + if (drvdata->trfc) + drvdata->save_trfcr = read_trfcr(); + /* + * Save and restore the ETM Trace registers only if + * the ETM is active. + */ + if (local_read(&drvdata->mode) && drvdata->save_state) + ret = __etm4_cpu_save(drvdata); + return ret; +} + +static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata) { int i; struct etmv4_save_state *state = drvdata->save_state; @@ -1789,6 +1806,14 @@ static void etm4_cpu_restore(struct etmv4_drvdata *drvdata) etm4_cs_lock(drvdata, csa); } +static void etm4_cpu_restore(struct etmv4_drvdata *drvdata) +{ + if (drvdata->trfc) + write_trfcr(drvdata->save_trfcr); + if (drvdata->state_needs_restore) + __etm4_cpu_restore(drvdata); +} + static int etm4_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd, void *v) { @@ -1800,23 +1825,17 @@ static int etm4_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd, drvdata = etmdrvdata[cpu]; - if (!drvdata->save_state) - return NOTIFY_OK; - if (WARN_ON_ONCE(drvdata->cpu != cpu)) return NOTIFY_BAD; switch (cmd) { case CPU_PM_ENTER: - /* save the state if self-hosted coresight is in use */ - if (local_read(&drvdata->mode)) - if (etm4_cpu_save(drvdata)) - return NOTIFY_BAD; + if (etm4_cpu_save(drvdata)) + return NOTIFY_BAD; break; case CPU_PM_EXIT: case CPU_PM_ENTER_FAILED: - if (drvdata->state_needs_restore) - etm4_cpu_restore(drvdata); + etm4_cpu_restore(drvdata); break; default: return NOTIFY_DONE; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index e5b79bdb9851..82cba16b73a6 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -921,6 +921,7 @@ struct etmv4_save_state { * @lpoverride: If the implementation can support low-power state over. * @trfc: If the implementation supports Arm v8.4 trace filter controls. * @config: structure holding configuration parameters. + * @save_trfcr: Saved TRFCR_EL1 register during a CPU PM event. * @save_state: State to be preserved across power loss * @state_needs_restore: True when there is context to restore after PM exit * @skip_power_up: Indicates if an implementation can skip powering up @@ -973,6 +974,7 @@ struct etmv4_drvdata { bool lpoverride; bool trfc; struct etmv4_config config; + u64 save_trfcr; struct etmv4_save_state *save_state; bool state_needs_restore; bool skip_power_up; diff --git a/drivers/hwtracing/coresight/coresight-self-hosted-trace.h b/drivers/hwtracing/coresight/coresight-self-hosted-trace.h new file mode 100644 index 000000000000..303d71911870 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-self-hosted-trace.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Arm v8 Self-Hosted trace support. + * + * Copyright (C) 2021 ARM Ltd. + */ + +#ifndef __CORESIGHT_SELF_HOSTED_TRACE_H +#define __CORESIGHT_SELF_HOSTED_TRACE_H + +#include + +static inline u64 read_trfcr(void) +{ + return read_sysreg_s(SYS_TRFCR_EL1); +} + +static inline void write_trfcr(u64 val) +{ + write_sysreg_s(val, SYS_TRFCR_EL1); + isb(); +} + +#endif /* __CORESIGHT_SELF_HOSTED_TRACE_H */ -- cgit v1.2.3-70-g09d2 From 5f6fd1aa8cc147b111af1a833574487a87237dc0 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Tue, 14 Sep 2021 11:26:33 +0100 Subject: coresight: etm4x: Use Trace Filtering controls dynamically The Trace Filtering support (FEAT_TRF) ensures that the ETM can be prohibited from generating any trace for a given EL. This is much stricter knob, than the TRCVICTLR exception level masks, which doesn't prevent the ETM from generating Context packets for an "excluded" EL. At the moment, we do a onetime enable trace at user and kernel and leave it untouched for the kernel life time. This implies that the ETM could potentially generate trace packets containing the kernel addresses, and thus leaking the kernel virtual address in the trace. This patch makes the switch dynamic, by honoring the filters set by the user and enforcing them in the TRFCR controls. We also rename the cpu_enable_tracing() appropriately to cpu_detect_trace_filtering() and the drvdata member trfc => trfcr to indicate the "value" of the TRFCR_EL1. Cc: Mathieu Poirier Cc: Al Grant Cc: Mike Leach Cc: Leo Yan Signed-off-by: Suzuki K Poulose Reviewed-by: Anshuman Khandual Link: https://lore.kernel.org/r/20210914102641.1852544-3-suzuki.poulose@arm.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-etm4x-core.c | 63 ++++++++++++++++------ drivers/hwtracing/coresight/coresight-etm4x.h | 7 ++- .../coresight/coresight-self-hosted-trace.h | 7 +++ 3 files changed, 59 insertions(+), 18 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 537c0d7ee1ed..d9a85ca25b60 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -239,6 +239,45 @@ struct etm4_enable_arg { int rc; }; +/* + * etm4x_prohibit_trace - Prohibit the CPU from tracing at all ELs. + * When the CPU supports FEAT_TRF, we could move the ETM to a trace + * prohibited state by filtering the Exception levels via TRFCR_EL1. + */ +static void etm4x_prohibit_trace(struct etmv4_drvdata *drvdata) +{ + /* If the CPU doesn't support FEAT_TRF, nothing to do */ + if (!drvdata->trfcr) + return; + cpu_prohibit_trace(); +} + +/* + * etm4x_allow_trace - Allow CPU tracing in the respective ELs, + * as configured by the drvdata->config.mode for the current + * session. Even though we have TRCVICTLR bits to filter the + * trace in the ELs, it doesn't prevent the ETM from generating + * a packet (e.g, TraceInfo) that might contain the addresses from + * the excluded levels. Thus we use the additional controls provided + * via the Trace Filtering controls (FEAT_TRF) to make sure no trace + * is generated for the excluded ELs. + */ +static void etm4x_allow_trace(struct etmv4_drvdata *drvdata) +{ + u64 trfcr = drvdata->trfcr; + + /* If the CPU doesn't support FEAT_TRF, nothing to do */ + if (!trfcr) + return; + + if (drvdata->config.mode & ETM_MODE_EXCL_KERN) + trfcr &= ~TRFCR_ELx_ExTRE; + if (drvdata->config.mode & ETM_MODE_EXCL_USER) + trfcr &= ~TRFCR_ELx_E0TRE; + + write_trfcr(trfcr); +} + #ifdef CONFIG_ETM4X_IMPDEF_FEATURE #define HISI_HIP08_AMBA_ID 0x000b6d01 @@ -443,6 +482,7 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata) if (etm4x_is_ete(drvdata)) etm4x_relaxed_write32(csa, TRCRSR_TA, TRCRSR); + etm4x_allow_trace(drvdata); /* Enable the trace unit */ etm4x_relaxed_write32(csa, 1, TRCPRGCTLR); @@ -738,7 +778,6 @@ static int etm4_enable(struct coresight_device *csdev, static void etm4_disable_hw(void *info) { u32 control; - u64 trfcr; struct etmv4_drvdata *drvdata = info; struct etmv4_config *config = &drvdata->config; struct coresight_device *csdev = drvdata->csdev; @@ -765,12 +804,7 @@ static void etm4_disable_hw(void *info) * If the CPU supports v8.4 Trace filter Control, * set the ETM to trace prohibited region. */ - if (drvdata->trfc) { - trfcr = read_sysreg_s(SYS_TRFCR_EL1); - write_sysreg_s(trfcr & ~(TRFCR_ELx_ExTRE | TRFCR_ELx_E0TRE), - SYS_TRFCR_EL1); - isb(); - } + etm4x_prohibit_trace(drvdata); /* * Make sure everything completes before disabling, as recommended * by section 7.3.77 ("TRCVICTLR, ViewInst Main Control Register, @@ -786,9 +820,6 @@ static void etm4_disable_hw(void *info) if (coresight_timeout(csa, TRCSTATR, TRCSTATR_PMSTABLE_BIT, 1)) dev_err(etm_dev, "timeout while waiting for PM stable Trace Status\n"); - if (drvdata->trfc) - write_sysreg_s(trfcr, SYS_TRFCR_EL1); - /* read the status of the single shot comparators */ for (i = 0; i < drvdata->nr_ss_cmp; i++) { config->ss_status[i] = @@ -990,15 +1021,15 @@ static bool etm4_init_csdev_access(struct etmv4_drvdata *drvdata, return false; } -static void cpu_enable_tracing(struct etmv4_drvdata *drvdata) +static void cpu_detect_trace_filtering(struct etmv4_drvdata *drvdata) { u64 dfr0 = read_sysreg(id_aa64dfr0_el1); u64 trfcr; + drvdata->trfcr = 0; if (!cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_TRACE_FILT_SHIFT)) return; - drvdata->trfc = true; /* * If the CPU supports v8.4 SelfHosted Tracing, enable * tracing at the kernel EL and EL0, forcing to use the @@ -1012,7 +1043,7 @@ static void cpu_enable_tracing(struct etmv4_drvdata *drvdata) if (is_kernel_in_hyp_mode()) trfcr |= TRFCR_EL2_CX; - write_trfcr(trfcr); + drvdata->trfcr = trfcr; } static void etm4_init_arch_data(void *info) @@ -1203,7 +1234,7 @@ static void etm4_init_arch_data(void *info) /* NUMCNTR, bits[30:28] number of counters available for tracing */ drvdata->nr_cntr = BMVAL(etmidr5, 28, 30); etm4_cs_lock(drvdata, csa); - cpu_enable_tracing(drvdata); + cpu_detect_trace_filtering(drvdata); } static inline u32 etm4_get_victlr_access_type(struct etmv4_config *config) @@ -1699,7 +1730,7 @@ static int etm4_cpu_save(struct etmv4_drvdata *drvdata) int ret = 0; /* Save the TRFCR irrespective of whether the ETM is ON */ - if (drvdata->trfc) + if (drvdata->trfcr) drvdata->save_trfcr = read_trfcr(); /* * Save and restore the ETM Trace registers only if @@ -1808,7 +1839,7 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata) static void etm4_cpu_restore(struct etmv4_drvdata *drvdata) { - if (drvdata->trfc) + if (drvdata->trfcr) write_trfcr(drvdata->save_trfcr); if (drvdata->state_needs_restore) __etm4_cpu_restore(drvdata); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index 82cba16b73a6..3c4d69b096ca 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -919,7 +919,10 @@ struct etmv4_save_state { * @nooverflow: Indicate if overflow prevention is supported. * @atbtrig: If the implementation can support ATB triggers * @lpoverride: If the implementation can support low-power state over. - * @trfc: If the implementation supports Arm v8.4 trace filter controls. + * @trfcr: If the CPU supports FEAT_TRF, value of the TRFCR_ELx that + * allows tracing at all ELs. We don't want to compute this + * at runtime, due to the additional setting of TRFCR_CX when + * in EL2. Otherwise, 0. * @config: structure holding configuration parameters. * @save_trfcr: Saved TRFCR_EL1 register during a CPU PM event. * @save_state: State to be preserved across power loss @@ -972,7 +975,7 @@ struct etmv4_drvdata { bool nooverflow; bool atbtrig; bool lpoverride; - bool trfc; + u64 trfcr; struct etmv4_config config; u64 save_trfcr; struct etmv4_save_state *save_state; diff --git a/drivers/hwtracing/coresight/coresight-self-hosted-trace.h b/drivers/hwtracing/coresight/coresight-self-hosted-trace.h index 303d71911870..23f05df3f173 100644 --- a/drivers/hwtracing/coresight/coresight-self-hosted-trace.h +++ b/drivers/hwtracing/coresight/coresight-self-hosted-trace.h @@ -21,4 +21,11 @@ static inline void write_trfcr(u64 val) isb(); } +static inline void cpu_prohibit_trace(void) +{ + u64 trfcr = read_trfcr(); + + /* Prohibit tracing at EL0 & the kernel EL */ + write_trfcr(trfcr & ~(TRFCR_ELx_ExTRE | TRFCR_ELx_E0TRE)); +} #endif /* __CORESIGHT_SELF_HOSTED_TRACE_H */ -- cgit v1.2.3-70-g09d2 From 2ef43054bb26cd3df53a32f50d7eea796a578931 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Tue, 14 Sep 2021 11:26:34 +0100 Subject: coresight: etm-pmu: Ensure the AUX handle is valid The ETM perf infrastructure closes out a handle during event_stop or on an error in starting the event. In either case, it is possible for a "sink" to update/close the handle, under certain circumstances. (e.g no space in ring buffer.). So, ensure that we handle this gracefully in the PMU driver by verifying the handle is still valid. Cc: Mathieu Poirier Cc: Mike Leach Cc: Anshuman Khandual Cc: Leo Yan Reviewed-by: Anshuman Khandual Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20210914102641.1852544-4-suzuki.poulose@arm.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-etm-perf.c | 27 +++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 3bce1ec4c4bf..c039b6ae206f 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -485,8 +485,15 @@ out: fail_disable_path: coresight_disable_path(path); fail_end_stop: - perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); - perf_aux_output_end(handle, 0); + /* + * Check if the handle is still associated with the event, + * to handle cases where if the sink failed to start the + * trace and TRUNCATED the handle already. + */ + if (READ_ONCE(handle->event)) { + perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); + perf_aux_output_end(handle, 0); + } fail: event->hw.state = PERF_HES_STOPPED; return; @@ -567,7 +574,21 @@ static void etm_event_stop(struct perf_event *event, int mode) size = sink_ops(sink)->update_buffer(sink, handle, event_data->snk_config); - perf_aux_output_end(handle, size); + /* + * Make sure the handle is still valid as the + * sink could have closed it from an IRQ. + * The sink driver must handle the race with + * update_buffer() and IRQ. Thus either we + * should get a valid handle and valid size + * (which may be 0). + * + * But we should never get a non-zero size with + * an invalid handle. + */ + if (READ_ONCE(handle->event)) + perf_aux_output_end(handle, size); + else + WARN_ON(size); } /* Disabling the path make its elements available to other sessions */ -- cgit v1.2.3-70-g09d2 From 5bd9ff830c8728bf6a532ff893b73e7afae007f1 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Tue, 14 Sep 2021 11:26:35 +0100 Subject: coresight: trbe: Ensure the format flag is always set When the TRBE is stopped on truncating an event, we may not set the FORMAT flag, even though the size of the record is 0. Let us be consistent and not confuse the user. To ensure that the format flag is always set on all the records generated by TRBE, set the flag when we have a new handle. Rather than deferring to the "end" operation, which makes it clear. So, we can do this from - arm_trbe_enable() -> When a new handle is provided by the CoreSight PMU, triggered via etm_event_start() - trbe_handle_overflow() -> When we begin a new handle after closing the previous on overflow. Cc: Anshuman Khandual Cc: Leo Yan Cc: Mike Leach Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Reviewed-by: Anshuman Khandual Link: https://lore.kernel.org/r/20210914102641.1852544-5-suzuki.poulose@arm.com [Fixed inverted words in title] Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-trbe.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index 176868496879..81bf183a73a1 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -554,8 +554,6 @@ static unsigned long arm_trbe_update_buffer(struct coresight_device *csdev, if (cpudata->mode != CS_MODE_PERF) return 0; - perf_aux_output_flag(handle, PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW); - /* * We are about to disable the TRBE. And this could in turn * fill up the buffer triggering, an IRQ. This could be consumed @@ -648,6 +646,7 @@ static int arm_trbe_enable(struct coresight_device *csdev, u32 mode, void *data) if (mode != CS_MODE_PERF) return -EINVAL; + perf_aux_output_flag(handle, PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW); *this_cpu_ptr(drvdata->handle) = handle; cpudata->buf = buf; cpudata->mode = mode; @@ -710,8 +709,7 @@ static void trbe_handle_overflow(struct perf_output_handle *handle) * Mark the buffer as truncated, as we have stopped the trace * collection upon the WRAP event, without stopping the source. */ - perf_aux_output_flag(handle, PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW | - PERF_AUX_FLAG_TRUNCATED); + perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); perf_aux_output_end(handle, size); event_data = perf_aux_output_begin(handle, event); if (!event_data) { @@ -725,6 +723,7 @@ static void trbe_handle_overflow(struct perf_output_handle *handle) *this_cpu_ptr(buf->cpudata->drvdata->handle) = NULL; return; } + perf_aux_output_flag(handle, PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW); buf->trbe_limit = compute_trbe_buffer_limit(handle); buf->trbe_write = buf->trbe_base + PERF_IDX2OFF(handle->head, buf); if (buf->trbe_limit == buf->trbe_base) { -- cgit v1.2.3-70-g09d2 From acee3ef86d5cfda094fa9806935aee45833444e3 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Tue, 14 Sep 2021 11:26:36 +0100 Subject: coresight: trbe: Drop duplicate TRUNCATE flags We mark the buffer as TRUNCATED when there is no space left in the buffer. But we do it at different points. __trbe_normal_offset() and also, at all the callers of the above function via compute_trbe_buffer_limit(), when the limit == base (i.e offset = 0 as returned by the __trbe_normal_offset()). So, given that the callers already mark the buffer as TRUNCATED drop the caller inside the __trbe_normal_offset(). This is in preparation to moving the handling of TRUNCATED into a central place. Cc: Anshuman Khandual Cc: Mathieu Poirier Cc: Mike Leach Cc: Leo Yan Signed-off-by: Suzuki K Poulose Reviewed-by: Anshuman Khandual Link: https://lore.kernel.org/r/20210914102641.1852544-6-suzuki.poulose@arm.com [Moved comment as Anshuman requested] Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-trbe.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index 81bf183a73a1..4479e689c38a 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -252,13 +252,9 @@ static unsigned long __trbe_normal_offset(struct perf_output_handle *handle) * trbe_base trbe_base + nr_pages * * Perf aux buffer does not have any space for the driver to write into. - * Just communicate trace truncation event to the user space by marking - * it with PERF_AUX_FLAG_TRUNCATED. */ - if (!handle->size) { - perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); + if (!handle->size) return 0; - } /* Compute the tail and wakeup indices now that we've aligned head */ tail = PERF_IDX2OFF(handle->head + handle->size, buf); @@ -360,7 +356,6 @@ static unsigned long __trbe_normal_offset(struct perf_output_handle *handle) return limit; trbe_pad_buf(handle, handle->size); - perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); return 0; } @@ -688,6 +683,11 @@ static void trbe_handle_spurious(struct perf_output_handle *handle) buf->trbe_write = buf->trbe_base + PERF_IDX2OFF(handle->head, buf); if (buf->trbe_limit == buf->trbe_base) { trbe_drain_and_disable_local(); + /* + * Just communicate trace truncation event to the user space by + * marking it with PERF_AUX_FLAG_TRUNCATED. + */ + perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); return; } trbe_enable_hw(buf); -- cgit v1.2.3-70-g09d2 From 04a37a174e5697d90c129c286bdb6bbcb516058f Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Tue, 14 Sep 2021 11:26:39 +0100 Subject: coresight: trbe: Unify the enabling sequence Unify the sequence of enabling the TRBE. We do this from event_start and also from the TRBE IRQ handler. Lets move this to a common helper. The only minor functional change is returning an error when we fail to enable the TRBE. This should be handled already. Since we now have unique entry point to trying to enable TRBE, move the format flag setting to the central place. Cc: Mathieu Poirier Cc: Mike Leach Cc: Leo Yan Reviewed-by: Anshuman Khandual Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20210914102641.1852544-9-suzuki.poulose@arm.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-trbe.c | 37 ++++++++++++++-------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index 4479e689c38a..2cf28de2e447 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -629,6 +629,21 @@ done: return size; } +static int __arm_trbe_enable(struct trbe_buf *buf, + struct perf_output_handle *handle) +{ + perf_aux_output_flag(handle, PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW); + buf->trbe_limit = compute_trbe_buffer_limit(handle); + buf->trbe_write = buf->trbe_base + PERF_IDX2OFF(handle->head, buf); + if (buf->trbe_limit == buf->trbe_base) { + trbe_stop_and_truncate_event(handle); + return -ENOSPC; + } + *this_cpu_ptr(buf->cpudata->drvdata->handle) = handle; + trbe_enable_hw(buf); + return 0; +} + static int arm_trbe_enable(struct coresight_device *csdev, u32 mode, void *data) { struct trbe_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -641,19 +656,11 @@ static int arm_trbe_enable(struct coresight_device *csdev, u32 mode, void *data) if (mode != CS_MODE_PERF) return -EINVAL; - perf_aux_output_flag(handle, PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW); - *this_cpu_ptr(drvdata->handle) = handle; cpudata->buf = buf; cpudata->mode = mode; buf->cpudata = cpudata; - buf->trbe_limit = compute_trbe_buffer_limit(handle); - buf->trbe_write = buf->trbe_base + PERF_IDX2OFF(handle->head, buf); - if (buf->trbe_limit == buf->trbe_base) { - trbe_stop_and_truncate_event(handle); - return 0; - } - trbe_enable_hw(buf); - return 0; + + return __arm_trbe_enable(buf, handle); } static int arm_trbe_disable(struct coresight_device *csdev) @@ -723,15 +730,7 @@ static void trbe_handle_overflow(struct perf_output_handle *handle) *this_cpu_ptr(buf->cpudata->drvdata->handle) = NULL; return; } - perf_aux_output_flag(handle, PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW); - buf->trbe_limit = compute_trbe_buffer_limit(handle); - buf->trbe_write = buf->trbe_base + PERF_IDX2OFF(handle->head, buf); - if (buf->trbe_limit == buf->trbe_base) { - trbe_stop_and_truncate_event(handle); - return; - } - *this_cpu_ptr(buf->cpudata->drvdata->handle) = handle; - trbe_enable_hw(buf); + __arm_trbe_enable(buf, handle); } static bool is_perf_trbe(struct perf_output_handle *handle) -- cgit v1.2.3-70-g09d2 From 85fb92353e0dece18048bdb35c7e6b682129deff Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 23 Sep 2021 15:39:15 +0100 Subject: coresight: trbe: irq handler: Do not disable TRBE if no action is needed The IRQ handler of the TRBE driver could race against the update_buffer() in consuming the IRQ. So, if the update_buffer() gets to processing the TRBE irq, the TRBSR will be cleared. Thus by the time IRQ handler is triggered, there is nothing to do there. Handle these cases and do not disable the TRBE unnecessarily. Since the TRBSR can be read without stopping the TRBE, we can check that before disabling the TRBE. Cc: Mathieu Poirier Cc: Anshuman Khandual Cc: Leo Yan Cc: Mike Leach Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20210923143919.2944311-2-suzuki.poulose@arm.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-trbe.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index 2cf28de2e447..e78800ba5b5b 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -762,12 +762,7 @@ static irqreturn_t arm_trbe_irq_handler(int irq, void *dev) enum trbe_fault_action act; u64 status; - /* - * Ensure the trace is visible to the CPUs and - * any external aborts have been resolved. - */ - trbe_drain_and_disable_local(); - + /* Reads to TRBSR_EL1 is fine when TRBE is active */ status = read_sysreg_s(SYS_TRBSR_EL1); /* * If the pending IRQ was handled by update_buffer callback @@ -776,6 +771,11 @@ static irqreturn_t arm_trbe_irq_handler(int irq, void *dev) if (!is_trbe_irq(status)) return IRQ_NONE; + /* + * Ensure the trace is visible to the CPUs and + * any external aborts have been resolved. + */ + trbe_drain_and_disable_local(); clr_trbe_irq(); isb(); -- cgit v1.2.3-70-g09d2 From 7037a39d379733f5352af5e04a202dad35562ef3 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 23 Sep 2021 15:39:16 +0100 Subject: coresight: trbe: Fix handling of spurious interrupts On a spurious IRQ, right now we disable the TRBE and then re-enable it back, resetting the "buffer" pointers(i.e BASE, LIMIT and more importantly WRITE) to the original pointers from the AUX handle. This implies that we overwrite any trace that was written so far, (by overwriting TRBPTR) while we should have ignored the IRQ. On detecting a spurious IRQ after examining the TRBSR we simply re-enable the TRBE without touching the other parameters. Cc: Anshuman Khandual Cc: Mathieu Poirier Cc: Mike Leach Cc: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20210923143919.2944311-3-suzuki.poulose@arm.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-trbe.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index e78800ba5b5b..47120d263639 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -684,20 +684,16 @@ static int arm_trbe_disable(struct coresight_device *csdev) static void trbe_handle_spurious(struct perf_output_handle *handle) { - struct trbe_buf *buf = etm_perf_sink_config(handle); + u64 limitr = read_sysreg_s(SYS_TRBLIMITR_EL1); - buf->trbe_limit = compute_trbe_buffer_limit(handle); - buf->trbe_write = buf->trbe_base + PERF_IDX2OFF(handle->head, buf); - if (buf->trbe_limit == buf->trbe_base) { - trbe_drain_and_disable_local(); - /* - * Just communicate trace truncation event to the user space by - * marking it with PERF_AUX_FLAG_TRUNCATED. - */ - perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); - return; - } - trbe_enable_hw(buf); + /* + * If the IRQ was spurious, simply re-enable the TRBE + * back without modifying the buffer parameters to + * retain the trace collected so far. + */ + limitr |= TRBLIMITR_ENABLE; + write_sysreg_s(limitr, SYS_TRBLIMITR_EL1); + isb(); } static void trbe_handle_overflow(struct perf_output_handle *handle) -- cgit v1.2.3-70-g09d2 From 0a5f355633eaacb7300598d96e8013a401a02c5e Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 23 Sep 2021 15:39:17 +0100 Subject: coresight: trbe: Do not truncate buffer on IRQ The TRBE driver marks the AUX buffer as TRUNCATED when we get an IRQ on FILL event. This has rather unwanted side-effect of the event being disabled when there may be more space in the ring buffer. So, instead of TRUNCATE we need a different flag to indicate that the trace may have lost a few bytes (i.e from the point of generating the FILL event until the IRQ is consumed). Anyways, the userspace must use the size from RECORD_AUX headers to restrict the "trace" decoding. Using PARTIAL flag causes the perf tool to generate the following warning: Warning: AUX data had gaps in it XX times out of YY! Are you running a KVM guest in the background? which is pointlessly scary for a user. The other remaining options are : - COLLISION - Use by SPE to indicate samples collided - Add a new flag - Specifically for CoreSight, doesn't sound so good, if we can re-use something. Given that we don't already use the "COLLISION" flag, the above behavior can be notified using this flag for CoreSight. Cc: Mathieu Poirier Cc: James Clark Cc: Mike Leach Cc: Anshuman Khandual Cc: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20210923143919.2944311-4-suzuki.poulose@arm.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-trbe.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index 47120d263639..0a9106c15639 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -120,6 +120,25 @@ static void trbe_reset_local(void) write_sysreg_s(0, SYS_TRBSR_EL1); } +static void trbe_report_wrap_event(struct perf_output_handle *handle) +{ + /* + * Mark the buffer to indicate that there was a WRAP event by + * setting the COLLISION flag. This indicates to the user that + * the TRBE trace collection was stopped without stopping the + * ETE and thus there might be some amount of trace that was + * lost between the time the WRAP was detected and the IRQ + * was consumed by the CPU. + * + * Setting the TRUNCATED flag would move the event to STOPPED + * state unnecessarily, even when there is space left in the + * ring buffer. Using the COLLISION flag doesn't have this side + * effect. We only set TRUNCATED flag when there is no space + * left in the ring buffer. + */ + perf_aux_output_flag(handle, PERF_AUX_FLAG_COLLISION); +} + static void trbe_stop_and_truncate_event(struct perf_output_handle *handle) { struct trbe_buf *buf = etm_perf_sink_config(handle); @@ -612,7 +631,7 @@ static unsigned long arm_trbe_update_buffer(struct coresight_device *csdev, * for correct size. Also, mark the buffer truncated. */ write = get_trbe_limit_pointer(); - perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); + trbe_report_wrap_event(handle); } offset = write - base; @@ -708,11 +727,7 @@ static void trbe_handle_overflow(struct perf_output_handle *handle) if (buf->snapshot) handle->head += size; - /* - * Mark the buffer as truncated, as we have stopped the trace - * collection upon the WRAP event, without stopping the source. - */ - perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); + trbe_report_wrap_event(handle); perf_aux_output_end(handle, size); event_data = perf_aux_output_begin(handle, event); if (!event_data) { -- cgit v1.2.3-70-g09d2 From 9bef9d0850a024a07f46d13828c05becf03eb79f Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 23 Sep 2021 15:39:18 +0100 Subject: coresight: trbe: End the AUX handle on truncation When we detect that there isn't enough space left to start a meaningful session, we disable the TRBE, marking the buffer as TRUNCATED. But we delay the notification to the perf layer by perf_aux_output_end() until the event is scheduled out, triggered from the kernel perf layer. This will cause significant black outs in the trace. Now that the CoreSight PMU layer can handle a closed "AUX" handle properly, we can close the handle as soon as we detect the case, allowing the userspace to collect and re-enable the event. Also, while in the IRQ handler, move the irq_work_run() after we have updated the handle, to make sure the "TRUNCATED" flag causes the event to be disabled as soon as possible. Cc: Anshuman Khandual Cc: Mathieu Poirier Cc: Mike Leach Cc: Leo Yan Cc: Peter Zijlstra (Intel) Cc: Will Deacon Reviewed-by: Anshuman Khandual Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20210923143919.2944311-5-suzuki.poulose@arm.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-trbe.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index 0a9106c15639..4174300f1344 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -152,6 +152,7 @@ static void trbe_stop_and_truncate_event(struct perf_output_handle *handle) */ trbe_drain_and_disable_local(); perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); + perf_aux_output_end(handle, 0); *this_cpu_ptr(buf->cpudata->drvdata->handle) = NULL; } @@ -715,7 +716,7 @@ static void trbe_handle_spurious(struct perf_output_handle *handle) isb(); } -static void trbe_handle_overflow(struct perf_output_handle *handle) +static int trbe_handle_overflow(struct perf_output_handle *handle) { struct perf_event *event = handle->event; struct trbe_buf *buf = etm_perf_sink_config(handle); @@ -739,9 +740,10 @@ static void trbe_handle_overflow(struct perf_output_handle *handle) */ trbe_drain_and_disable_local(); *this_cpu_ptr(buf->cpudata->drvdata->handle) = NULL; - return; + return -EINVAL; } - __arm_trbe_enable(buf, handle); + + return __arm_trbe_enable(buf, handle); } static bool is_perf_trbe(struct perf_output_handle *handle) @@ -772,6 +774,7 @@ static irqreturn_t arm_trbe_irq_handler(int irq, void *dev) struct perf_output_handle *handle = *handle_ptr; enum trbe_fault_action act; u64 status; + bool truncated = false; /* Reads to TRBSR_EL1 is fine when TRBE is active */ status = read_sysreg_s(SYS_TRBSR_EL1); @@ -796,24 +799,27 @@ static irqreturn_t arm_trbe_irq_handler(int irq, void *dev) if (!is_perf_trbe(handle)) return IRQ_NONE; - /* - * Ensure perf callbacks have completed, which may disable - * the trace buffer in response to a TRUNCATION flag. - */ - irq_work_run(); - act = trbe_get_fault_act(status); switch (act) { case TRBE_FAULT_ACT_WRAP: - trbe_handle_overflow(handle); + truncated = !!trbe_handle_overflow(handle); break; case TRBE_FAULT_ACT_SPURIOUS: trbe_handle_spurious(handle); break; case TRBE_FAULT_ACT_FATAL: trbe_stop_and_truncate_event(handle); + truncated = true; break; } + + /* + * If the buffer was truncated, ensure perf callbacks + * have completed, which will disable the event. + */ + if (truncated) + irq_work_run(); + return IRQ_HANDLED; } -- cgit v1.2.3-70-g09d2 From dcfecfa444b12546c028e0f5a66255347a78cbd2 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 23 Sep 2021 15:39:19 +0100 Subject: coresight: trbe: Prohibit trace before disabling TRBE When the TRBE generates an IRQ, we stop the TRBE, collect the trace and then reprogram the TRBE with the updated buffer pointers, whenever possible. We might also leave the TRBE disabled, if there is not enough space left in the buffer. However, we do not touch the ETE at all during all of this. This means the ETE is only disabled when the event is disabled later (via irq_work). This is incorrect, as the ETE trace is still ON without actually being captured and may be routed to the ATB (even if it is for a short duration). So, we move the CPU into trace prohibited state always before disabling the TRBE, upon entering the IRQ handler. The state is restored if the TRBE is enabled back. Otherwise the trace remains prohibited. Since, the ETM/ETE driver now controls the TRFCR_EL1 per session, the tracing can be restored/enabled back when the event is rescheduled in. Cc: Anshuman Khandual Cc: Mathieu Poirier Cc: Mike Leach Cc: Leo Yan Reviewed-by: Anshuman Khandual Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20210923143919.2944311-6-suzuki.poulose@arm.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-self-hosted-trace.h | 4 +++- drivers/hwtracing/coresight/coresight-trbe.c | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-self-hosted-trace.h b/drivers/hwtracing/coresight/coresight-self-hosted-trace.h index 23f05df3f173..53840a2c41f2 100644 --- a/drivers/hwtracing/coresight/coresight-self-hosted-trace.h +++ b/drivers/hwtracing/coresight/coresight-self-hosted-trace.h @@ -21,11 +21,13 @@ static inline void write_trfcr(u64 val) isb(); } -static inline void cpu_prohibit_trace(void) +static inline u64 cpu_prohibit_trace(void) { u64 trfcr = read_trfcr(); /* Prohibit tracing at EL0 & the kernel EL */ write_trfcr(trfcr & ~(TRFCR_ELx_ExTRE | TRFCR_ELx_E0TRE)); + /* Return the original value of the TRFCR */ + return trfcr; } #endif /* __CORESIGHT_SELF_HOSTED_TRACE_H */ diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index 4174300f1344..a53ee98f312f 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -16,6 +16,7 @@ #define pr_fmt(fmt) DRVNAME ": " fmt #include +#include "coresight-self-hosted-trace.h" #include "coresight-trbe.h" #define PERF_IDX2OFF(idx, buf) ((idx) % ((buf)->nr_pages << PAGE_SHIFT)) @@ -775,6 +776,7 @@ static irqreturn_t arm_trbe_irq_handler(int irq, void *dev) enum trbe_fault_action act; u64 status; bool truncated = false; + u64 trfcr; /* Reads to TRBSR_EL1 is fine when TRBE is active */ status = read_sysreg_s(SYS_TRBSR_EL1); @@ -785,6 +787,8 @@ static irqreturn_t arm_trbe_irq_handler(int irq, void *dev) if (!is_trbe_irq(status)) return IRQ_NONE; + /* Prohibit the CPU from tracing before we disable the TRBE */ + trfcr = cpu_prohibit_trace(); /* * Ensure the trace is visible to the CPUs and * any external aborts have been resolved. @@ -816,9 +820,14 @@ static irqreturn_t arm_trbe_irq_handler(int irq, void *dev) /* * If the buffer was truncated, ensure perf callbacks * have completed, which will disable the event. + * + * Otherwise, restore the trace filter controls to + * allow the tracing. */ if (truncated) irq_work_run(); + else + write_trfcr(trfcr); return IRQ_HANDLED; } -- cgit v1.2.3-70-g09d2 From 0605b89d0597f326aba3f70d0574750368a9fbe0 Mon Sep 17 00:00:00 2001 From: Tao Zhang Date: Fri, 24 Sep 2021 18:06:20 +0800 Subject: coresight: etm4x: Add ETM PID for Kryo-5XX Add ETM PID for Kryo-5XX to the list of supported ETMs. Otherwise, Kryo-5XX ETMs will not be initialized successfully. e.g. This change can be verified on qrb5165-rb5 board. ETM4-ETM7 nodes will not be visible without this change. Signed-off-by: Tao Zhang Link: https://lore.kernel.org/r/1632477981-13632-2-git-send-email-quic_taozha@quicinc.com Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-etm4x-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index d9a85ca25b60..86a313857b58 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -2149,6 +2149,7 @@ static const struct amba_id etm4_ids[] = { CS_AMBA_UCI_ID(0x000bb803, uci_id_etm4),/* Qualcomm Kryo 385 Cortex-A75 */ CS_AMBA_UCI_ID(0x000bb805, uci_id_etm4),/* Qualcomm Kryo 4XX Cortex-A55 */ CS_AMBA_UCI_ID(0x000bb804, uci_id_etm4),/* Qualcomm Kryo 4XX Cortex-A76 */ + CS_AMBA_UCI_ID(0x000bbd0d, uci_id_etm4),/* Qualcomm Kryo 5XX Cortex-A77 */ CS_AMBA_UCI_ID(0x000cc0af, uci_id_etm4),/* Marvell ThunderX2 */ CS_AMBA_UCI_ID(0x000b6d01, uci_id_etm4),/* HiSilicon-Hip08 */ CS_AMBA_UCI_ID(0x000b6d02, uci_id_etm4),/* HiSilicon-Hip09 */ -- cgit v1.2.3-70-g09d2 From bb5293e334af51b19b62d8bef1852ea13e935e9b Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Tue, 21 Sep 2021 14:41:05 +0100 Subject: coresight: trbe: Fix incorrect access of the sink specific data The TRBE driver wrongly treats the aux private data as the TRBE driver specific buffer for a given perf handle, while it is the ETM PMU's event specific data. Fix this by correcting the instance to use appropriate helper. Cc: stable Fixes: 3fbf7f011f24 ("coresight: sink: Add TRBE driver") Signed-off-by: Suzuki K Poulose Reviewed-by: Anshuman Khandual Link: https://lore.kernel.org/r/20210921134121.2423546-2-suzuki.poulose@arm.com [Fixed 13 character SHA down to 12] Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-trbe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index a53ee98f312f..2825ccb0cf39 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -382,7 +382,7 @@ static unsigned long __trbe_normal_offset(struct perf_output_handle *handle) static unsigned long trbe_normal_offset(struct perf_output_handle *handle) { - struct trbe_buf *buf = perf_get_aux(handle); + struct trbe_buf *buf = etm_perf_sink_config(handle); u64 limit = __trbe_normal_offset(handle); u64 head = PERF_IDX2OFF(handle->head, buf); -- cgit v1.2.3-70-g09d2 From a08025b3fe56185290a1ea476581f03ca733f967 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 14 Oct 2021 15:22:38 +0100 Subject: coresight: trbe: Defer the probe on offline CPUs If a CPU is offline during the driver init, we could end up causing a kernel crash trying to register the coresight device for the TRBE instance. The trbe_cpudata for the TRBE instance is initialized only when it is probed. Otherwise, we could end up dereferencing a NULL cpudata->drvdata. e.g: [ 0.149999] coresight ete0: CPU0: ete v1.1 initialized [ 0.149999] coresight-etm4x ete_1: ETM arch init failed [ 0.149999] coresight-etm4x: probe of ete_1 failed with error -22 [ 0.150085] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000050 [ 0.150085] Mem abort info: [ 0.150085] ESR = 0x96000005 [ 0.150085] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.150085] SET = 0, FnV = 0 [ 0.150085] EA = 0, S1PTW = 0 [ 0.150085] Data abort info: [ 0.150085] ISV = 0, ISS = 0x00000005 [ 0.150085] CM = 0, WnR = 0 [ 0.150085] [0000000000000050] user address but active_mm is swapper [ 0.150085] Internal error: Oops: 96000005 [#1] PREEMPT SMP [ 0.150085] Modules linked in: [ 0.150085] Hardware name: FVP Base RevC (DT) [ 0.150085] pstate: 00800009 (nzcv daif -PAN +UAO -TCO BTYPE=--) [ 0.150155] pc : arm_trbe_register_coresight_cpu+0x74/0x144 [ 0.150155] lr : arm_trbe_register_coresight_cpu+0x48/0x144 ... [ 0.150237] Call trace: [ 0.150237] arm_trbe_register_coresight_cpu+0x74/0x144 [ 0.150237] arm_trbe_device_probe+0x1c0/0x2d8 [ 0.150259] platform_drv_probe+0x94/0xbc [ 0.150259] really_probe+0x1bc/0x4a8 [ 0.150266] driver_probe_device+0x7c/0xb8 [ 0.150266] device_driver_attach+0x6c/0xac [ 0.150266] __driver_attach+0xc4/0x148 [ 0.150266] bus_for_each_dev+0x7c/0xc8 [ 0.150266] driver_attach+0x24/0x30 [ 0.150266] bus_add_driver+0x100/0x1e0 [ 0.150266] driver_register+0x78/0x110 [ 0.150266] __platform_driver_register+0x44/0x50 [ 0.150266] arm_trbe_init+0x28/0x84 [ 0.150266] do_one_initcall+0x94/0x2bc [ 0.150266] do_initcall_level+0xa4/0x158 [ 0.150266] do_initcalls+0x54/0x94 [ 0.150319] do_basic_setup+0x24/0x30 [ 0.150319] kernel_init_freeable+0xe8/0x14c [ 0.150319] kernel_init+0x14/0x18c [ 0.150319] ret_from_fork+0x10/0x30 [ 0.150319] Code: f94012c8 b0004ce2 9134a442 52819801 (f9402917) [ 0.150319] ---[ end trace d23e0cfe5098535e ]--- [ 0.150346] Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b Fix this by skipping the step, if we are unable to probe the CPU. Fixes: 3fbf7f011f24 ("coresight: sink: Add TRBE driver") Reported-by: Bransilav Rankov Cc: Anshuman Khandual Cc: Mathieu Poirier Cc: Mike Leach Cc: Leo Yan Cc: stable Tested-by: Branislav Rankov Signed-off-by: Suzuki K Poulose Reviewed-by: Anshuman Khandual Link: https://lore.kernel.org/r/20211014142238.2221248-1-suzuki.poulose@arm.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-trbe.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index 2825ccb0cf39..5d77baba8b0f 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -893,6 +893,10 @@ static void arm_trbe_register_coresight_cpu(struct trbe_drvdata *drvdata, int cp if (WARN_ON(trbe_csdev)) return; + /* If the TRBE was not probed on the CPU, we shouldn't be here */ + if (WARN_ON(!cpudata->drvdata)) + return; + dev = &cpudata->drvdata->pdev->dev; desc.name = devm_kasprintf(dev, GFP_KERNEL, "trbe%d", cpu); if (!desc.name) @@ -974,7 +978,9 @@ static int arm_trbe_probe_coresight(struct trbe_drvdata *drvdata) return -ENOMEM; for_each_cpu(cpu, &drvdata->supported_cpus) { - smp_call_function_single(cpu, arm_trbe_probe_cpu, drvdata, 1); + /* If we fail to probe the CPU, let us defer it to hotplug callbacks */ + if (smp_call_function_single(cpu, arm_trbe_probe_cpu, drvdata, 1)) + continue; if (cpumask_test_cpu(cpu, &drvdata->supported_cpus)) arm_trbe_register_coresight_cpu(drvdata, cpu); if (cpumask_test_cpu(cpu, &drvdata->supported_cpus)) -- cgit v1.2.3-70-g09d2 From 41c0e5b7a35392c90a467a9a085178642a131324 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Tue, 19 Oct 2021 17:31:43 +0100 Subject: coresight: trbe: Add a helper to calculate the trace generated We collect the trace from the TRBE on FILL event from IRQ context and via update_buffer(), when the event is stopped. Let us consolidate how we calculate the trace generated into a helper. Cc: Mathieu Poirier Cc: Mike Leach Cc: Leo Yan Reviewed-by: Anshuman Khandual Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20211019163153.3692640-6-suzuki.poulose@arm.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-trbe.c | 47 +++++++++++++++++----------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index 5d77baba8b0f..54f5d61ee9fb 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -499,6 +499,29 @@ static enum trbe_fault_action trbe_get_fault_act(u64 trbsr) return TRBE_FAULT_ACT_SPURIOUS; } +static unsigned long trbe_get_trace_size(struct perf_output_handle *handle, + struct trbe_buf *buf, bool wrap) +{ + u64 write; + u64 start_off, end_off; + + /* + * If the TRBE has wrapped around the write pointer has + * wrapped and should be treated as limit. + */ + if (wrap) + write = get_trbe_limit_pointer(); + else + write = get_trbe_write_pointer(); + + end_off = write - get_trbe_base_pointer(); + start_off = PERF_IDX2OFF(handle->head, buf); + + if (WARN_ON_ONCE(end_off < start_off)) + return 0; + return (end_off - start_off); +} + static void *arm_trbe_alloc_buffer(struct coresight_device *csdev, struct perf_event *event, void **pages, int nr_pages, bool snapshot) @@ -560,9 +583,9 @@ static unsigned long arm_trbe_update_buffer(struct coresight_device *csdev, struct trbe_cpudata *cpudata = dev_get_drvdata(&csdev->dev); struct trbe_buf *buf = config; enum trbe_fault_action act; - unsigned long size, offset; - unsigned long write, base, status; + unsigned long size, status; unsigned long flags; + bool wrap = false; WARN_ON(buf->cpudata != cpudata); WARN_ON(cpudata->cpu != smp_processor_id()); @@ -602,8 +625,6 @@ static unsigned long arm_trbe_update_buffer(struct coresight_device *csdev, * handle gets freed in etm_event_stop(). */ trbe_drain_and_disable_local(); - write = get_trbe_write_pointer(); - base = get_trbe_base_pointer(); /* Check if there is a pending interrupt and handle it here */ status = read_sysreg_s(SYS_TRBSR_EL1); @@ -627,20 +648,11 @@ static unsigned long arm_trbe_update_buffer(struct coresight_device *csdev, goto done; } - /* - * Otherwise, the buffer is full and the write pointer - * has reached base. Adjust this back to the Limit pointer - * for correct size. Also, mark the buffer truncated. - */ - write = get_trbe_limit_pointer(); trbe_report_wrap_event(handle); + wrap = true; } - offset = write - base; - if (WARN_ON_ONCE(offset < PERF_IDX2OFF(handle->head, buf))) - size = 0; - else - size = offset - PERF_IDX2OFF(handle->head, buf); + size = trbe_get_trace_size(handle, buf, wrap); done: local_irq_restore(flags); @@ -721,11 +733,10 @@ static int trbe_handle_overflow(struct perf_output_handle *handle) { struct perf_event *event = handle->event; struct trbe_buf *buf = etm_perf_sink_config(handle); - unsigned long offset, size; + unsigned long size; struct etm_event_data *event_data; - offset = get_trbe_limit_pointer() - get_trbe_base_pointer(); - size = offset - PERF_IDX2OFF(handle->head, buf); + size = trbe_get_trace_size(handle, buf, true); if (buf->snapshot) handle->head += size; -- cgit v1.2.3-70-g09d2 From 4585481af322e3ae53dfb78e3ec046ddfdf8902a Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Tue, 19 Oct 2021 17:31:44 +0100 Subject: coresight: trbe: Add a helper to pad a given buffer area Refactor the helper to pad a given AUX buffer area to allow "filling" ignore packets, without moving any handle pointers. This will be useful in working around errata, where we may have to fill the buffer after a session. Cc: Mathieu Poirier Cc: Mike Leach Cc: Leo Yan Reviewed-by: Anshuman Khandual Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20211019163153.3692640-7-suzuki.poulose@arm.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-trbe.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index 54f5d61ee9fb..e3767f21ee68 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -199,12 +199,18 @@ static void trbe_stop_and_truncate_event(struct perf_output_handle *handle) * consumed from the user space. The enabled TRBE buffer area is a moving subset of * the allocated perf auxiliary buffer. */ + +static void __trbe_pad_buf(struct trbe_buf *buf, u64 offset, int len) +{ + memset((void *)buf->trbe_base + offset, ETE_IGNORE_PACKET, len); +} + static void trbe_pad_buf(struct perf_output_handle *handle, int len) { struct trbe_buf *buf = etm_perf_sink_config(handle); u64 head = PERF_IDX2OFF(handle->head, buf); - memset((void *)buf->trbe_base + head, ETE_IGNORE_PACKET, len); + __trbe_pad_buf(buf, head, len); if (!buf->snapshot) perf_aux_output_skip(handle, len); } -- cgit v1.2.3-70-g09d2 From 2336a7b29b5826a9cd15c39818ded665f88d2e5b Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Tue, 19 Oct 2021 17:31:45 +0100 Subject: coresight: trbe: Decouple buffer base from the hardware base We always set the TRBBASER_EL1 to the base of the virtual ring buffer. We are about to change this for working around an erratum. So, in preparation to that, allow the driver to choose a different base for the TRBBASER_EL1 (which is within the buffer range). Cc: Anshuman Khandual Cc: Mike Leach Cc: Mathieu Poirier Cc: Leo Yan Reviewed-by: Anshuman Khandual Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20211019163153.3692640-8-suzuki.poulose@arm.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-trbe.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index e3767f21ee68..ae0bde9630f6 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -57,6 +57,8 @@ struct trbe_buf { * trbe_limit sibling pointers. */ unsigned long trbe_base; + /* The base programmed into the TRBE */ + unsigned long trbe_hw_base; unsigned long trbe_limit; unsigned long trbe_write; int nr_pages; @@ -470,12 +472,13 @@ static void set_trbe_limit_pointer_enabled(unsigned long addr) static void trbe_enable_hw(struct trbe_buf *buf) { - WARN_ON(buf->trbe_write < buf->trbe_base); + WARN_ON(buf->trbe_hw_base < buf->trbe_base); + WARN_ON(buf->trbe_write < buf->trbe_hw_base); WARN_ON(buf->trbe_write >= buf->trbe_limit); set_trbe_disabled(); isb(); clr_trbe_status(); - set_trbe_base_pointer(buf->trbe_base); + set_trbe_base_pointer(buf->trbe_hw_base); set_trbe_write_pointer(buf->trbe_write); /* @@ -520,7 +523,12 @@ static unsigned long trbe_get_trace_size(struct perf_output_handle *handle, else write = get_trbe_write_pointer(); - end_off = write - get_trbe_base_pointer(); + /* + * TRBE may use a different base address than the base + * of the ring buffer. Thus use the beginning of the ring + * buffer to compute the offsets. + */ + end_off = write - buf->trbe_base; start_off = PERF_IDX2OFF(handle->head, buf); if (WARN_ON_ONCE(end_off < start_off)) @@ -678,6 +686,8 @@ static int __arm_trbe_enable(struct trbe_buf *buf, trbe_stop_and_truncate_event(handle); return -ENOSPC; } + /* Set the base of the TRBE to the buffer base */ + buf->trbe_hw_base = buf->trbe_base; *this_cpu_ptr(buf->cpudata->drvdata->handle) = handle; trbe_enable_hw(buf); return 0; @@ -771,7 +781,7 @@ static bool is_perf_trbe(struct perf_output_handle *handle) struct trbe_drvdata *drvdata = cpudata->drvdata; int cpu = smp_processor_id(); - WARN_ON(buf->trbe_base != get_trbe_base_pointer()); + WARN_ON(buf->trbe_hw_base != get_trbe_base_pointer()); WARN_ON(buf->trbe_limit != get_trbe_limit_pointer()); if (cpudata->mode != CS_MODE_PERF) -- cgit v1.2.3-70-g09d2 From e4bc8829a748c61ab07617b41da5d071b060126e Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Tue, 19 Oct 2021 17:31:46 +0100 Subject: coresight: trbe: Allow driver to choose a different alignment The TRBE hardware mandates a minimum alignment for the TRBPTR_EL1, advertised via the TRBIDR_EL1. This is used by the driver to align the buffer write head. This patch allows the driver to choose a different alignment from that of the hardware, by decoupling the alignment tracking. This will be useful for working around errata. Cc: Mathieu Poirier Cc: Anshuman Khandual Cc: Mike Leach Cc: Leo Yan Reviewed-by: Anshuman Khandual Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20211019163153.3692640-9-suzuki.poulose@arm.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-trbe.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index ae0bde9630f6..cdbb134892f6 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -67,8 +67,18 @@ struct trbe_buf { struct trbe_cpudata *cpudata; }; +/* + * struct trbe_cpudata: TRBE instance specific data + * @trbe_flag - TRBE dirty/access flag support + * @trbe_hw_align - Actual TRBE alignment required for TRBPTR_EL1. + * @trbe_align - Software alignment used for the TRBPTR_EL1. + * @cpu - CPU this TRBE belongs to. + * @mode - Mode of current operation. (perf/disabled) + * @drvdata - TRBE specific drvdata + */ struct trbe_cpudata { bool trbe_flag; + u64 trbe_hw_align; u64 trbe_align; int cpu; enum cs_mode mode; @@ -875,7 +885,7 @@ static ssize_t align_show(struct device *dev, struct device_attribute *attr, cha { struct trbe_cpudata *cpudata = dev_get_drvdata(dev); - return sprintf(buf, "%llx\n", cpudata->trbe_align); + return sprintf(buf, "%llx\n", cpudata->trbe_hw_align); } static DEVICE_ATTR_RO(align); @@ -967,11 +977,13 @@ static void arm_trbe_probe_cpu(void *info) goto cpu_clear; } - cpudata->trbe_align = 1ULL << get_trbe_address_align(trbidr); - if (cpudata->trbe_align > SZ_2K) { + cpudata->trbe_hw_align = 1ULL << get_trbe_address_align(trbidr); + if (cpudata->trbe_hw_align > SZ_2K) { pr_err("Unsupported alignment on cpu %d\n", cpu); goto cpu_clear; } + + cpudata->trbe_align = cpudata->trbe_hw_align; cpudata->trbe_flag = get_trbe_flag_update(trbidr); cpudata->cpu = cpu; cpudata->drvdata = drvdata; -- cgit v1.2.3-70-g09d2 From 8a1065127d952e22a95ccf1c4b84e733f656bb2c Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Tue, 19 Oct 2021 17:31:47 +0100 Subject: coresight: trbe: Add infrastructure for Errata handling Add a minimal infrastructure to keep track of the errata affecting the given TRBE instance. Given that we have heterogeneous CPUs, we have to manage the list per-TRBE instance to be able to apply the work around as needed. Thus we will need to check if individual CPUs are affected by the erratum. We rely on the arm64 errata framework for the actual description and the discovery of a given erratum, to keep the Erratum work around at a central place and benefit from the code and the advertisement from the kernel. Though we could reuse the "this_cpu_has_cap()" to apply an erratum work around, it is a bit of a heavy operation, as it must go through the "erratum" detection check on the CPU every time it is called (e.g, scanning through a table of affected MIDRs). Since we need to do this check for every session, may be multiple times (depending on the wrok around), we could save the cycles by caching the affected errata per-CPU instance in the per-CPU struct trbe_cpudata. Since we are only interested in the errata affecting the TRBE driver, we only need to track a very few of them per-CPU. Thus we use a local mapping of the CPUCAP for the erratum to avoid bloating up a bitmap for trbe_cpudata. i.e, each arm64 TRBE erratum bit is assigned a "index" within the driver to track. Each trbe instance updates the list of affected erratum at probe time on the CPU. This makes sure that we can easily access the list of errata on a given TRBE instance without much overhead. Cc: Mathieu Poirier Cc: Mike Leach Cc: Leo Yan Cc: Anshuman Khandual Reviewed-by: Anshuman Khandual Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20211019163153.3692640-10-suzuki.poulose@arm.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-trbe.c | 69 +++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index cdbb134892f6..fdd60ff4fb3d 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -16,6 +16,8 @@ #define pr_fmt(fmt) DRVNAME ": " fmt #include +#include + #include "coresight-self-hosted-trace.h" #include "coresight-trbe.h" @@ -67,6 +69,34 @@ struct trbe_buf { struct trbe_cpudata *cpudata; }; +/* + * TRBE erratum list + * + * The errata are defined in arm64 generic cpu_errata framework. + * Since the errata work arounds could be applied individually + * to the affected CPUs inside the TRBE driver, we need to know if + * a given CPU is affected by the erratum. Unlike the other erratum + * work arounds, TRBE driver needs to check multiple times during + * a trace session. Thus we need a quicker access to per-CPU + * errata and not issue costly this_cpu_has_cap() everytime. + * We keep a set of the affected errata in trbe_cpudata, per TRBE. + * + * We rely on the corresponding cpucaps to be defined for a given + * TRBE erratum. We map the given cpucap into a TRBE internal number + * to make the tracking of the errata lean. + * + * This helps in : + * - Not duplicating the detection logic + * - Streamlined detection of erratum across the system + */ + +static int trbe_errata_cpucaps[] = { + -1, /* Sentinel, must be the last entry */ +}; + +/* The total number of listed errata in trbe_errata_cpucaps */ +#define TRBE_ERRATA_MAX (ARRAY_SIZE(trbe_errata_cpucaps) - 1) + /* * struct trbe_cpudata: TRBE instance specific data * @trbe_flag - TRBE dirty/access flag support @@ -75,6 +105,7 @@ struct trbe_buf { * @cpu - CPU this TRBE belongs to. * @mode - Mode of current operation. (perf/disabled) * @drvdata - TRBE specific drvdata + * @errata - Bit map for the errata on this TRBE. */ struct trbe_cpudata { bool trbe_flag; @@ -84,6 +115,7 @@ struct trbe_cpudata { enum cs_mode mode; struct trbe_buf *buf; struct trbe_drvdata *drvdata; + DECLARE_BITMAP(errata, TRBE_ERRATA_MAX); }; struct trbe_drvdata { @@ -96,6 +128,25 @@ struct trbe_drvdata { struct platform_device *pdev; }; +static void trbe_check_errata(struct trbe_cpudata *cpudata) +{ + int i; + + for (i = 0; i < TRBE_ERRATA_MAX; i++) { + int cap = trbe_errata_cpucaps[i]; + + if (WARN_ON_ONCE(cap < 0)) + return; + if (this_cpu_has_cap(cap)) + set_bit(i, cpudata->errata); + } +} + +static inline bool trbe_has_erratum(struct trbe_cpudata *cpudata, int i) +{ + return (i < TRBE_ERRATA_MAX) && test_bit(i, cpudata->errata); +} + static int trbe_alloc_node(struct perf_event *event) { if (event->cpu == -1) @@ -956,6 +1007,9 @@ cpu_clear: cpumask_clear_cpu(cpu, &drvdata->supported_cpus); } +/* + * Must be called with preemption disabled, for trbe_check_errata(). + */ static void arm_trbe_probe_cpu(void *info) { struct trbe_drvdata *drvdata = info; @@ -983,6 +1037,12 @@ static void arm_trbe_probe_cpu(void *info) goto cpu_clear; } + /* + * Run the TRBE erratum checks, now that we know + * this instance is about to be registered. + */ + trbe_check_errata(cpudata); + cpudata->trbe_align = cpudata->trbe_hw_align; cpudata->trbe_flag = get_trbe_flag_update(trbidr); cpudata->cpu = cpu; @@ -1038,6 +1098,13 @@ static int arm_trbe_remove_coresight(struct trbe_drvdata *drvdata) return 0; } +static void arm_trbe_probe_hotplugged_cpu(struct trbe_drvdata *drvdata) +{ + preempt_disable(); + arm_trbe_probe_cpu(drvdata); + preempt_enable(); +} + static int arm_trbe_cpu_startup(unsigned int cpu, struct hlist_node *node) { struct trbe_drvdata *drvdata = hlist_entry_safe(node, struct trbe_drvdata, hotplug_node); @@ -1049,7 +1116,7 @@ static int arm_trbe_cpu_startup(unsigned int cpu, struct hlist_node *node) * initialize it now. */ if (!coresight_get_percpu_sink(cpu)) { - arm_trbe_probe_cpu(drvdata); + arm_trbe_probe_hotplugged_cpu(drvdata); if (cpumask_test_cpu(cpu, &drvdata->supported_cpus)) arm_trbe_register_coresight_cpu(drvdata, cpu); if (cpumask_test_cpu(cpu, &drvdata->supported_cpus)) -- cgit v1.2.3-70-g09d2 From 5cb75f18800bac3c4bfc46f28d3d42d87439d9e2 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Tue, 19 Oct 2021 17:31:48 +0100 Subject: coresight: trbe: Workaround TRBE errata overwrite in FILL mode ARM Neoverse-N2 (#2139208) and Cortex-A710(##2119858) suffers from an erratum, which when triggered, might cause the TRBE to overwrite the trace data already collected in FILL mode, in the event of a WRAP. i.e, the TRBE doesn't stop writing the data, instead wraps to the base and could write upto 3 cache line size worth trace. Thus, this could corrupt the trace at the "BASE" pointer. The workaround is to program the write pointer 256bytes from the base, such that if the erratum is triggered, it doesn't overwrite the trace data that was captured. This skipped region could be padded with ignore packets at the end of the session, so that the decoder sees a continuous buffer with some padding at the beginning. The trace data written at the base is considered lost as the limit could have been in the middle of the perf ring buffer, and jumping to the "base" is not acceptable. We set the flags already to indicate that some amount of trace was lost during the FILL event IRQ. So this is fine. One important change with the work around is, we program the TRBBASER_EL1 to current page where we are allowed to write. Otherwise, it could overwrite a region that may be consumed by the perf. Towards this, we always make sure that the "handle->head" and thus the trbe_write is PAGE_SIZE aligned, so that we can set the BASE to the PAGE base and move the TRBPTR to the 256bytes offset. Cc: Mike Leach Cc: Mathieu Poirier Cc: Anshuman Khandual Cc: Leo Yan Reviewed-by: Anshuman Khandual Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20211019163153.3692640-11-suzuki.poulose@arm.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-trbe.c | 168 +++++++++++++++++++++++++-- 1 file changed, 157 insertions(+), 11 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index fdd60ff4fb3d..2d39e5ecb72c 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -89,14 +89,22 @@ struct trbe_buf { * - Not duplicating the detection logic * - Streamlined detection of erratum across the system */ +#define TRBE_WORKAROUND_OVERWRITE_FILL_MODE 0 static int trbe_errata_cpucaps[] = { + [TRBE_WORKAROUND_OVERWRITE_FILL_MODE] = ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE, -1, /* Sentinel, must be the last entry */ }; /* The total number of listed errata in trbe_errata_cpucaps */ #define TRBE_ERRATA_MAX (ARRAY_SIZE(trbe_errata_cpucaps) - 1) +/* + * Safe limit for the number of bytes that may be overwritten + * when ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE is triggered. + */ +#define TRBE_WORKAROUND_OVERWRITE_FILL_MODE_SKIP_BYTES 256 + /* * struct trbe_cpudata: TRBE instance specific data * @trbe_flag - TRBE dirty/access flag support @@ -147,6 +155,11 @@ static inline bool trbe_has_erratum(struct trbe_cpudata *cpudata, int i) return (i < TRBE_ERRATA_MAX) && test_bit(i, cpudata->errata); } +static inline bool trbe_may_overwrite_in_fill_mode(struct trbe_cpudata *cpudata) +{ + return trbe_has_erratum(cpudata, TRBE_WORKAROUND_OVERWRITE_FILL_MODE); +} + static int trbe_alloc_node(struct perf_event *event) { if (event->cpu == -1) @@ -550,10 +563,13 @@ static void trbe_enable_hw(struct trbe_buf *buf) set_trbe_limit_pointer_enabled(buf->trbe_limit); } -static enum trbe_fault_action trbe_get_fault_act(u64 trbsr) +static enum trbe_fault_action trbe_get_fault_act(struct perf_output_handle *handle, + u64 trbsr) { int ec = get_trbe_ec(trbsr); int bsc = get_trbe_bsc(trbsr); + struct trbe_buf *buf = etm_perf_sink_config(handle); + struct trbe_cpudata *cpudata = buf->cpudata; WARN_ON(is_trbe_running(trbsr)); if (is_trbe_trg(trbsr) || is_trbe_abort(trbsr)) @@ -562,10 +578,16 @@ static enum trbe_fault_action trbe_get_fault_act(u64 trbsr) if ((ec == TRBE_EC_STAGE1_ABORT) || (ec == TRBE_EC_STAGE2_ABORT)) return TRBE_FAULT_ACT_FATAL; - if (is_trbe_wrap(trbsr) && (ec == TRBE_EC_OTHERS) && (bsc == TRBE_BSC_FILLED)) { - if (get_trbe_write_pointer() == get_trbe_base_pointer()) - return TRBE_FAULT_ACT_WRAP; - } + /* + * If the trbe is affected by TRBE_WORKAROUND_OVERWRITE_FILL_MODE, + * it might write data after a WRAP event in the fill mode. + * Thus the check TRBPTR == TRBBASER will not be honored. + */ + if ((is_trbe_wrap(trbsr) && (ec == TRBE_EC_OTHERS) && (bsc == TRBE_BSC_FILLED)) && + (trbe_may_overwrite_in_fill_mode(cpudata) || + get_trbe_write_pointer() == get_trbe_base_pointer())) + return TRBE_FAULT_ACT_WRAP; + return TRBE_FAULT_ACT_SPURIOUS; } @@ -574,6 +596,8 @@ static unsigned long trbe_get_trace_size(struct perf_output_handle *handle, { u64 write; u64 start_off, end_off; + u64 size; + u64 overwrite_skip = TRBE_WORKAROUND_OVERWRITE_FILL_MODE_SKIP_BYTES; /* * If the TRBE has wrapped around the write pointer has @@ -594,7 +618,18 @@ static unsigned long trbe_get_trace_size(struct perf_output_handle *handle, if (WARN_ON_ONCE(end_off < start_off)) return 0; - return (end_off - start_off); + + size = end_off - start_off; + /* + * If the TRBE is affected by the following erratum, we must fill + * the space we skipped with IGNORE packets. And we are always + * guaranteed to have at least a PAGE_SIZE space in the buffer. + */ + if (trbe_has_erratum(buf->cpudata, TRBE_WORKAROUND_OVERWRITE_FILL_MODE) && + !WARN_ON(size < overwrite_skip)) + __trbe_pad_buf(buf, start_off, overwrite_skip); + + return size; } static void *arm_trbe_alloc_buffer(struct coresight_device *csdev, @@ -713,7 +748,7 @@ static unsigned long arm_trbe_update_buffer(struct coresight_device *csdev, clr_trbe_irq(); isb(); - act = trbe_get_fault_act(status); + act = trbe_get_fault_act(handle, status); /* * If this was not due to a WRAP event, we have some * errors and as such buffer is empty. @@ -737,21 +772,117 @@ done: return size; } + +static int trbe_apply_work_around_before_enable(struct trbe_buf *buf) +{ + /* + * TRBE_WORKAROUND_OVERWRITE_FILL_MODE causes the TRBE to overwrite a few cache + * line size from the "TRBBASER_EL1" in the event of a "FILL". + * Thus, we could loose some amount of the trace at the base. + * + * Before Fix: + * + * normal-BASE head (normal-TRBPTR) tail (normal-LIMIT) + * | \/ / + * ------------------------------------------------------------- + * | Pg0 | Pg1 | | | PgN | + * ------------------------------------------------------------- + * + * In the normal course of action, we would set the TRBBASER to the + * beginning of the ring-buffer (normal-BASE). But with the erratum, + * the TRBE could overwrite the contents at the "normal-BASE", after + * hitting the "normal-LIMIT", since it doesn't stop as expected. And + * this is wrong. This could result in overwriting trace collected in + * one of the previous runs, being consumed by the user. So we must + * always make sure that the TRBBASER is within the region + * [head, head+size]. Note that TRBBASER must be PAGE aligned, + * + * After moving the BASE: + * + * normal-BASE head (normal-TRBPTR) tail (normal-LIMIT) + * | \/ / + * ------------------------------------------------------------- + * | | |xyzdef. |.. tuvw| | + * ------------------------------------------------------------- + * / + * New-BASER + * + * Also, we would set the TRBPTR to head (after adjusting for + * alignment) at normal-PTR. This would mean that the last few bytes + * of the trace (say, "xyz") might overwrite the first few bytes of + * trace written ("abc"). More importantly they will appear in what + * userspace sees as the beginning of the trace, which is wrong. We may + * not always have space to move the latest trace "xyz" to the correct + * order as it must appear beyond the LIMIT. (i.e, [head..head+size]). + * Thus it is easier to ignore those bytes than to complicate the + * driver to move it, assuming that the erratum was triggered and + * doing additional checks to see if there is indeed allowed space at + * TRBLIMITR.LIMIT. + * + * Thus the full workaround will move the BASE and the PTR and would + * look like (after padding at the skipped bytes at the end of + * session) : + * + * normal-BASE head (normal-TRBPTR) tail (normal-LIMIT) + * | \/ / + * ------------------------------------------------------------- + * | | |///abc.. |.. rst| | + * ------------------------------------------------------------- + * / | + * New-BASER New-TRBPTR + * + * To summarize, with the work around: + * + * - We always align the offset for the next session to PAGE_SIZE + * (This is to ensure we can program the TRBBASER to this offset + * within the region [head...head+size]). + * + * - At TRBE enable: + * - Set the TRBBASER to the page aligned offset of the current + * proposed write offset. (which is guaranteed to be aligned + * as above) + * - Move the TRBPTR to skip first 256bytes (that might be + * overwritten with the erratum). This ensures that the trace + * generated in the session is not re-written. + * + * - At trace collection: + * - Pad the 256bytes skipped above again with IGNORE packets. + */ + if (trbe_has_erratum(buf->cpudata, TRBE_WORKAROUND_OVERWRITE_FILL_MODE)) { + if (WARN_ON(!IS_ALIGNED(buf->trbe_write, PAGE_SIZE))) + return -EINVAL; + buf->trbe_hw_base = buf->trbe_write; + buf->trbe_write += TRBE_WORKAROUND_OVERWRITE_FILL_MODE_SKIP_BYTES; + } + + return 0; +} + static int __arm_trbe_enable(struct trbe_buf *buf, struct perf_output_handle *handle) { + int ret = 0; + perf_aux_output_flag(handle, PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW); buf->trbe_limit = compute_trbe_buffer_limit(handle); buf->trbe_write = buf->trbe_base + PERF_IDX2OFF(handle->head, buf); if (buf->trbe_limit == buf->trbe_base) { - trbe_stop_and_truncate_event(handle); - return -ENOSPC; + ret = -ENOSPC; + goto err; } /* Set the base of the TRBE to the buffer base */ buf->trbe_hw_base = buf->trbe_base; + + ret = trbe_apply_work_around_before_enable(buf); + if (ret) + goto err; + *this_cpu_ptr(buf->cpudata->drvdata->handle) = handle; trbe_enable_hw(buf); return 0; +err: + trbe_stop_and_truncate_event(handle); + return ret; } static int arm_trbe_enable(struct coresight_device *csdev, u32 mode, void *data) @@ -891,7 +1022,7 @@ static irqreturn_t arm_trbe_irq_handler(int irq, void *dev) if (!is_perf_trbe(handle)) return IRQ_NONE; - act = trbe_get_fault_act(status); + act = trbe_get_fault_act(handle, status); switch (act) { case TRBE_FAULT_ACT_WRAP: truncated = !!trbe_handle_overflow(handle); @@ -1043,7 +1174,22 @@ static void arm_trbe_probe_cpu(void *info) */ trbe_check_errata(cpudata); - cpudata->trbe_align = cpudata->trbe_hw_align; + /* + * If the TRBE is affected by erratum TRBE_WORKAROUND_OVERWRITE_FILL_MODE, + * we must always program the TBRPTR_EL1, 256bytes from a page + * boundary, with TRBBASER_EL1 set to the page, to prevent + * TRBE over-writing 256bytes at TRBBASER_EL1 on FILL event. + * + * Thus make sure we always align our write pointer to a PAGE_SIZE, + * which also guarantees that we have at least a PAGE_SIZE space in + * the buffer (TRBLIMITR is PAGE aligned) and thus we can skip + * the required bytes at the base. + */ + if (trbe_may_overwrite_in_fill_mode(cpudata)) + cpudata->trbe_align = PAGE_SIZE; + else + cpudata->trbe_align = cpudata->trbe_hw_align; + cpudata->trbe_flag = get_trbe_flag_update(trbidr); cpudata->cpu = cpu; cpudata->drvdata = drvdata; -- cgit v1.2.3-70-g09d2 From 7c2cc5e26cc0c6bc2478ac203e3fe71950418ea0 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Tue, 19 Oct 2021 17:31:49 +0100 Subject: coresight: trbe: Add a helper to determine the minimum buffer size For the TRBE to operate, we need a minimum space available to collect meaningful trace session. This is currently a few bytes, but we may need to extend this for working around errata. So, abstract this into a helper function. Cc: Anshuman Khandual Cc: Mike Leach Cc: Mathieu Poirier Cc: Leo Yan Reviewed-by: Anshuman Khandual Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20211019163153.3692640-12-suzuki.poulose@arm.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-trbe.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index 2d39e5ecb72c..02a639ece2af 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -303,6 +303,11 @@ static unsigned long trbe_snapshot_offset(struct perf_output_handle *handle) return buf->nr_pages * PAGE_SIZE; } +static u64 trbe_min_trace_buf_size(struct perf_output_handle *handle) +{ + return TRBE_TRACE_MIN_BUF_SIZE; +} + /* * TRBE Limit Calculation * @@ -473,7 +478,7 @@ static unsigned long trbe_normal_offset(struct perf_output_handle *handle) * have space for a meaningful run, we rather pad it * and start fresh. */ - if (limit && (limit - head < TRBE_TRACE_MIN_BUF_SIZE)) { + if (limit && ((limit - head) < trbe_min_trace_buf_size(handle))) { trbe_pad_buf(handle, limit - head); limit = __trbe_normal_offset(handle); } -- cgit v1.2.3-70-g09d2 From adf35d058617c94810c03628efd7ae7821f3c5e7 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Tue, 19 Oct 2021 17:31:50 +0100 Subject: coresight: trbe: Make sure we have enough space The TRBE driver makes sure that there is enough space for a meaningful run, otherwise pads the given space and restarts the offset calculation once. But there is no guarantee that we may find space or hit "no space". Make sure that we repeat the step until, either : - We have the minimum space OR - There is NO space at all. Cc: Anshuman Khandual Cc: Mathieu Poirier Cc: Mike Leach Cc: Leo Yan Reviewed-by: Anshuman Khandual Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20211019163153.3692640-13-suzuki.poulose@arm.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-trbe.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index 02a639ece2af..11ff33ec83b9 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -477,10 +477,14 @@ static unsigned long trbe_normal_offset(struct perf_output_handle *handle) * If the head is too close to the limit and we don't * have space for a meaningful run, we rather pad it * and start fresh. + * + * We might have to do this more than once to make sure + * we have enough required space. */ - if (limit && ((limit - head) < trbe_min_trace_buf_size(handle))) { + while (limit && ((limit - head) < trbe_min_trace_buf_size(handle))) { trbe_pad_buf(handle, limit - head); limit = __trbe_normal_offset(handle); + head = PERF_IDX2OFF(handle->head, buf); } return limit; } -- cgit v1.2.3-70-g09d2 From f9efc79d0ab9b780bf74c624616bd50c6cefdeeb Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Tue, 19 Oct 2021 17:31:51 +0100 Subject: coresight: trbe: Work around write to out of range TRBE implementations affected by Arm erratum (2253138 or 2224489), could write to the next address after the TRBLIMITR.LIMIT, instead of wrapping to the TRBBASER. This implies that the TRBE could potentially corrupt : - A page used by the rest of the kernel/user (if the LIMIT = end of perf ring buffer) - A page within the ring buffer, but outside the driver's range. [head, head + size]. This may contain some trace data, may be consumed by the userspace. We workaround this erratum by : - Making sure that there is at least an extra PAGE space left in the TRBE's range than we normally assign. This will be additional to other restrictions (e.g, the TRBE alignment for working around TRBE_WORKAROUND_OVERWRITE_IN_FILL_MODE, where there is a minimum of PAGE_SIZE. Thus we would have 2 * PAGE_SIZE) - Adjust the LIMIT to leave the last PAGE_SIZE out of the TRBE's allowed range (i.e, TRBEBASER...TRBLIMITR.LIMIT), by : TRBLIMITR.LIMIT -= PAGE_SIZE Cc: Anshuman Khandual Cc: Mathieu Poirier Cc: Mike Leach Cc: Leo Yan Reviewed-by: Anshuman Khandual Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20211019163153.3692640-14-suzuki.poulose@arm.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-trbe.c | 63 +++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index 11ff33ec83b9..276862c07e32 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -90,9 +90,11 @@ struct trbe_buf { * - Streamlined detection of erratum across the system */ #define TRBE_WORKAROUND_OVERWRITE_FILL_MODE 0 +#define TRBE_WORKAROUND_WRITE_OUT_OF_RANGE 1 static int trbe_errata_cpucaps[] = { [TRBE_WORKAROUND_OVERWRITE_FILL_MODE] = ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE, + [TRBE_WORKAROUND_WRITE_OUT_OF_RANGE] = ARM64_WORKAROUND_TRBE_WRITE_OUT_OF_RANGE, -1, /* Sentinel, must be the last entry */ }; @@ -160,6 +162,11 @@ static inline bool trbe_may_overwrite_in_fill_mode(struct trbe_cpudata *cpudata) return trbe_has_erratum(cpudata, TRBE_WORKAROUND_OVERWRITE_FILL_MODE); } +static inline bool trbe_may_write_out_of_range(struct trbe_cpudata *cpudata) +{ + return trbe_has_erratum(cpudata, TRBE_WORKAROUND_WRITE_OUT_OF_RANGE); +} + static int trbe_alloc_node(struct perf_event *event) { if (event->cpu == -1) @@ -305,7 +312,21 @@ static unsigned long trbe_snapshot_offset(struct perf_output_handle *handle) static u64 trbe_min_trace_buf_size(struct perf_output_handle *handle) { - return TRBE_TRACE_MIN_BUF_SIZE; + u64 size = TRBE_TRACE_MIN_BUF_SIZE; + struct trbe_buf *buf = etm_perf_sink_config(handle); + struct trbe_cpudata *cpudata = buf->cpudata; + + /* + * When the TRBE is affected by an erratum that could make it + * write to the next "virtually addressed" page beyond the LIMIT. + * We need to make sure there is always a PAGE after the LIMIT, + * within the buffer. Thus we ensure there is at least an extra + * page than normal. With this we could then adjust the LIMIT + * pointer down by a PAGE later. + */ + if (trbe_may_write_out_of_range(cpudata)) + size += PAGE_SIZE; + return size; } /* @@ -611,6 +632,17 @@ static unsigned long trbe_get_trace_size(struct perf_output_handle *handle, /* * If the TRBE has wrapped around the write pointer has * wrapped and should be treated as limit. + * + * When the TRBE is affected by TRBE_WORKAROUND_WRITE_OUT_OF_RANGE, + * it may write upto 64bytes beyond the "LIMIT". The driver already + * keeps a valid page next to the LIMIT and we could potentially + * consume the trace data that may have been collected there. But we + * cannot be really sure it is available, and the TRBPTR may not + * indicate the same. Also, affected cores are also affected by another + * erratum which forces the PAGE_SIZE alignment on the TRBPTR, and thus + * could potentially pad an entire PAGE_SIZE - 64bytes, to get those + * 64bytes. Thus we ignore the potential triggering of the erratum + * on WRAP and limit the data to LIMIT. */ if (wrap) write = get_trbe_limit_pointer(); @@ -864,6 +896,35 @@ static int trbe_apply_work_around_before_enable(struct trbe_buf *buf) buf->trbe_write += TRBE_WORKAROUND_OVERWRITE_FILL_MODE_SKIP_BYTES; } + /* + * TRBE_WORKAROUND_WRITE_OUT_OF_RANGE could cause the TRBE to write to + * the next page after the TRBLIMITR.LIMIT. For perf, the "next page" + * may be: + * - The page beyond the ring buffer. This could mean, TRBE could + * corrupt another entity (kernel / user) + * - A portion of the "ring buffer" consumed by the userspace. + * i.e, a page outisde [head, head + size]. + * + * We work around this by: + * - Making sure that we have at least an extra space of PAGE left + * in the ring buffer [head, head + size], than we normally do + * without the erratum. See trbe_min_trace_buf_size(). + * + * - Adjust the TRBLIMITR.LIMIT to leave the extra PAGE outside + * the TRBE's range (i.e [TRBBASER, TRBLIMITR.LIMI] ). + */ + if (trbe_has_erratum(buf->cpudata, TRBE_WORKAROUND_WRITE_OUT_OF_RANGE)) { + s64 space = buf->trbe_limit - buf->trbe_write; + /* + * We must have more than a PAGE_SIZE worth space in the proposed + * range for the TRBE. + */ + if (WARN_ON(space <= PAGE_SIZE || + !IS_ALIGNED(buf->trbe_limit, PAGE_SIZE))) + return -EINVAL; + buf->trbe_limit -= PAGE_SIZE; + } + return 0; } -- cgit v1.2.3-70-g09d2 From 74b2740f57cc161297b800ecb0e1e549d13c3bb9 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Tue, 19 Oct 2021 17:31:52 +0100 Subject: arm64: errata: Enable workaround for TRBE overwrite in FILL mode With the workaround enabled in TRBE, enable the config entries to be built without COMPILE_TEST Cc: Catalin Marinas Cc: Will Deacon Reviewed-by: Anshuman Khandual Acked-by: Will Deacon Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20211019163153.3692640-15-suzuki.poulose@arm.com Signed-off-by: Mathieu Poirier --- arch/arm64/Kconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index a500af15ebd5..d44b4442611e 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -672,7 +672,6 @@ config ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE config ARM64_ERRATUM_2119858 bool "Cortex-A710: 2119858: workaround TRBE overwriting trace data in FILL mode" default y - depends on COMPILE_TEST # Until the CoreSight TRBE driver changes are in depends on CORESIGHT_TRBE select ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE help @@ -691,7 +690,6 @@ config ARM64_ERRATUM_2119858 config ARM64_ERRATUM_2139208 bool "Neoverse-N2: 2139208: workaround TRBE overwriting trace data in FILL mode" default y - depends on COMPILE_TEST # Until the CoreSight TRBE driver changes are in depends on CORESIGHT_TRBE select ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE help -- cgit v1.2.3-70-g09d2 From 561ced0bb90a4be298b7db5fb54f29731d74a3f6 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Tue, 19 Oct 2021 17:31:53 +0100 Subject: arm64: errata: Enable TRBE workaround for write to out-of-range address With the TRBE driver workaround available, enable the config symbols to be built without COMPILE_TEST Cc: Catalin Marinas Cc: Will Deacon Reviewed-by: Anshuman Khandual Acked-by: Will Deacon Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20211019163153.3692640-16-suzuki.poulose@arm.com Signed-off-by: Mathieu Poirier --- arch/arm64/Kconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index d44b4442611e..9c33ce54509a 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -743,7 +743,6 @@ config ARM64_WORKAROUND_TRBE_WRITE_OUT_OF_RANGE config ARM64_ERRATUM_2253138 bool "Neoverse-N2: 2253138: workaround TRBE writing to address out-of-range" - depends on COMPILE_TEST # Until the CoreSight TRBE driver changes are in depends on CORESIGHT_TRBE default y select ARM64_WORKAROUND_TRBE_WRITE_OUT_OF_RANGE @@ -762,7 +761,6 @@ config ARM64_ERRATUM_2253138 config ARM64_ERRATUM_2224489 bool "Cortex-A710: 2224489: workaround TRBE writing to address out-of-range" - depends on COMPILE_TEST # Until the CoreSight TRBE driver changes are in depends on CORESIGHT_TRBE default y select ARM64_WORKAROUND_TRBE_WRITE_OUT_OF_RANGE -- cgit v1.2.3-70-g09d2 From 907767da8f3a925b060c740e0b5c92ea7dbec440 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 27 Oct 2021 11:35:28 +0200 Subject: comedi: ni_usb6501: fix NULL-deref in command paths The driver uses endpoint-sized USB transfer buffers but had no sanity checks on the sizes. This can lead to zero-size-pointer dereferences or overflowed transfer buffers in ni6501_port_command() and ni6501_counter_command() if a (malicious) device has smaller max-packet sizes than expected (or when doing descriptor fuzz testing). Add the missing sanity checks to probe(). Fixes: a03bb00e50ab ("staging: comedi: add NI USB-6501 support") Cc: stable@vger.kernel.org # 3.18 Cc: Luca Ellero Reviewed-by: Ian Abbott Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20211027093529.30896-2-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/ni_usb6501.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/comedi/drivers/ni_usb6501.c b/drivers/comedi/drivers/ni_usb6501.c index 5b6d9d783b2f..c42987b74b1d 100644 --- a/drivers/comedi/drivers/ni_usb6501.c +++ b/drivers/comedi/drivers/ni_usb6501.c @@ -144,6 +144,10 @@ static const u8 READ_COUNTER_RESPONSE[] = {0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00}; +/* Largest supported packets */ +static const size_t TX_MAX_SIZE = sizeof(SET_PORT_DIR_REQUEST); +static const size_t RX_MAX_SIZE = sizeof(READ_PORT_RESPONSE); + enum commands { READ_PORT, WRITE_PORT, @@ -501,6 +505,12 @@ static int ni6501_find_endpoints(struct comedi_device *dev) if (!devpriv->ep_rx || !devpriv->ep_tx) return -ENODEV; + if (usb_endpoint_maxp(devpriv->ep_rx) < RX_MAX_SIZE) + return -ENODEV; + + if (usb_endpoint_maxp(devpriv->ep_tx) < TX_MAX_SIZE) + return -ENODEV; + return 0; } -- cgit v1.2.3-70-g09d2 From 536de747bc48262225889a533db6650731ab25d3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 27 Oct 2021 11:35:29 +0200 Subject: comedi: dt9812: fix DMA buffers on stack USB transfer buffers are typically mapped for DMA and must not be allocated on the stack or transfers will fail. Allocate proper transfer buffers in the various command helpers and return an error on short transfers instead of acting on random stack data. Note that this also fixes a stack info leak on systems where DMA is not used as 32 bytes are always sent to the device regardless of how short the command is. Fixes: 63274cd7d38a ("Staging: comedi: add usb dt9812 driver") Cc: stable@vger.kernel.org # 2.6.29 Reviewed-by: Ian Abbott Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20211027093529.30896-3-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/dt9812.c | 115 ++++++++++++++++++++++++++++++---------- 1 file changed, 86 insertions(+), 29 deletions(-) diff --git a/drivers/comedi/drivers/dt9812.c b/drivers/comedi/drivers/dt9812.c index 634f57730c1e..704b04d2980d 100644 --- a/drivers/comedi/drivers/dt9812.c +++ b/drivers/comedi/drivers/dt9812.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include "../comedi_usb.h" @@ -237,22 +238,42 @@ static int dt9812_read_info(struct comedi_device *dev, { struct usb_device *usb = comedi_to_usb_dev(dev); struct dt9812_private *devpriv = dev->private; - struct dt9812_usb_cmd cmd; + struct dt9812_usb_cmd *cmd; + size_t tbuf_size; int count, ret; + void *tbuf; - cmd.cmd = cpu_to_le32(DT9812_R_FLASH_DATA); - cmd.u.flash_data_info.address = + tbuf_size = max(sizeof(*cmd), buf_size); + + tbuf = kzalloc(tbuf_size, GFP_KERNEL); + if (!tbuf) + return -ENOMEM; + + cmd = tbuf; + + cmd->cmd = cpu_to_le32(DT9812_R_FLASH_DATA); + cmd->u.flash_data_info.address = cpu_to_le16(DT9812_DIAGS_BOARD_INFO_ADDR + offset); - cmd.u.flash_data_info.numbytes = cpu_to_le16(buf_size); + cmd->u.flash_data_info.numbytes = cpu_to_le16(buf_size); /* DT9812 only responds to 32 byte writes!! */ ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr), - &cmd, 32, &count, DT9812_USB_TIMEOUT); + cmd, sizeof(*cmd), &count, DT9812_USB_TIMEOUT); if (ret) - return ret; + goto out; + + ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, devpriv->cmd_rd.addr), + tbuf, buf_size, &count, DT9812_USB_TIMEOUT); + if (!ret) { + if (count == buf_size) + memcpy(buf, tbuf, buf_size); + else + ret = -EREMOTEIO; + } +out: + kfree(tbuf); - return usb_bulk_msg(usb, usb_rcvbulkpipe(usb, devpriv->cmd_rd.addr), - buf, buf_size, &count, DT9812_USB_TIMEOUT); + return ret; } static int dt9812_read_multiple_registers(struct comedi_device *dev, @@ -261,22 +282,42 @@ static int dt9812_read_multiple_registers(struct comedi_device *dev, { struct usb_device *usb = comedi_to_usb_dev(dev); struct dt9812_private *devpriv = dev->private; - struct dt9812_usb_cmd cmd; + struct dt9812_usb_cmd *cmd; int i, count, ret; + size_t buf_size; + void *buf; - cmd.cmd = cpu_to_le32(DT9812_R_MULTI_BYTE_REG); - cmd.u.read_multi_info.count = reg_count; + buf_size = max_t(size_t, sizeof(*cmd), reg_count); + + buf = kzalloc(buf_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + cmd = buf; + + cmd->cmd = cpu_to_le32(DT9812_R_MULTI_BYTE_REG); + cmd->u.read_multi_info.count = reg_count; for (i = 0; i < reg_count; i++) - cmd.u.read_multi_info.address[i] = address[i]; + cmd->u.read_multi_info.address[i] = address[i]; /* DT9812 only responds to 32 byte writes!! */ ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr), - &cmd, 32, &count, DT9812_USB_TIMEOUT); + cmd, sizeof(*cmd), &count, DT9812_USB_TIMEOUT); if (ret) - return ret; + goto out; + + ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, devpriv->cmd_rd.addr), + buf, reg_count, &count, DT9812_USB_TIMEOUT); + if (!ret) { + if (count == reg_count) + memcpy(value, buf, reg_count); + else + ret = -EREMOTEIO; + } +out: + kfree(buf); - return usb_bulk_msg(usb, usb_rcvbulkpipe(usb, devpriv->cmd_rd.addr), - value, reg_count, &count, DT9812_USB_TIMEOUT); + return ret; } static int dt9812_write_multiple_registers(struct comedi_device *dev, @@ -285,19 +326,27 @@ static int dt9812_write_multiple_registers(struct comedi_device *dev, { struct usb_device *usb = comedi_to_usb_dev(dev); struct dt9812_private *devpriv = dev->private; - struct dt9812_usb_cmd cmd; + struct dt9812_usb_cmd *cmd; int i, count; + int ret; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; - cmd.cmd = cpu_to_le32(DT9812_W_MULTI_BYTE_REG); - cmd.u.read_multi_info.count = reg_count; + cmd->cmd = cpu_to_le32(DT9812_W_MULTI_BYTE_REG); + cmd->u.read_multi_info.count = reg_count; for (i = 0; i < reg_count; i++) { - cmd.u.write_multi_info.write[i].address = address[i]; - cmd.u.write_multi_info.write[i].value = value[i]; + cmd->u.write_multi_info.write[i].address = address[i]; + cmd->u.write_multi_info.write[i].value = value[i]; } /* DT9812 only responds to 32 byte writes!! */ - return usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr), - &cmd, 32, &count, DT9812_USB_TIMEOUT); + ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr), + cmd, sizeof(*cmd), &count, DT9812_USB_TIMEOUT); + kfree(cmd); + + return ret; } static int dt9812_rmw_multiple_registers(struct comedi_device *dev, @@ -306,17 +355,25 @@ static int dt9812_rmw_multiple_registers(struct comedi_device *dev, { struct usb_device *usb = comedi_to_usb_dev(dev); struct dt9812_private *devpriv = dev->private; - struct dt9812_usb_cmd cmd; + struct dt9812_usb_cmd *cmd; int i, count; + int ret; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; - cmd.cmd = cpu_to_le32(DT9812_RMW_MULTI_BYTE_REG); - cmd.u.rmw_multi_info.count = reg_count; + cmd->cmd = cpu_to_le32(DT9812_RMW_MULTI_BYTE_REG); + cmd->u.rmw_multi_info.count = reg_count; for (i = 0; i < reg_count; i++) - cmd.u.rmw_multi_info.rmw[i] = rmw[i]; + cmd->u.rmw_multi_info.rmw[i] = rmw[i]; /* DT9812 only responds to 32 byte writes!! */ - return usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr), - &cmd, 32, &count, DT9812_USB_TIMEOUT); + ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr), + cmd, sizeof(*cmd), &count, DT9812_USB_TIMEOUT); + kfree(cmd); + + return ret; } static int dt9812_digital_in(struct comedi_device *dev, u8 *bits) -- cgit v1.2.3-70-g09d2