From c0c725d7350ec8b8453257676a440bb4b2df2422 Mon Sep 17 00:00:00 2001 From: Bo Liu Date: Thu, 16 Jun 2022 00:47:33 -0400 Subject: gnss: replace ida_simple API Use ida_alloc_xxx()/ida_free() instead of ida_simple_get()/ida_simple_remove(), which has been deprecated. Note that the upper bound is now inclusive. Signed-off-by: Bo Liu Link: https://lore.kernel.org/r/20220616044733.3605-1-liubo03@inspur.com Reviewed-by: Christophe JAILLET [ johan: amend commit message ] Signed-off-by: Johan Hovold --- drivers/gnss/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gnss/core.c b/drivers/gnss/core.c index e6f94501cb28..1e82b7967570 100644 --- a/drivers/gnss/core.c +++ b/drivers/gnss/core.c @@ -217,7 +217,7 @@ static void gnss_device_release(struct device *dev) kfree(gdev->write_buf); kfifo_free(&gdev->read_fifo); - ida_simple_remove(&gnss_minors, gdev->id); + ida_free(&gnss_minors, gdev->id); kfree(gdev); } @@ -232,7 +232,7 @@ struct gnss_device *gnss_allocate_device(struct device *parent) if (!gdev) return NULL; - id = ida_simple_get(&gnss_minors, 0, GNSS_MINORS, GFP_KERNEL); + id = ida_alloc_max(&gnss_minors, GNSS_MINORS - 1, GFP_KERNEL); if (id < 0) { kfree(gdev); return NULL; -- cgit v1.2.3 From 65f5c01033ab85f8d385d65c4b51fe31459da603 Mon Sep 17 00:00:00 2001 From: Matthew Gerlach Date: Tue, 19 Jul 2022 07:56:44 -0700 Subject: fpga: dfl-pci: Add IDs for Intel N6000, N6001 and C6100 cards Add pci_dev_table entries supporting the Intel N6000, N6001 and C6100 cards to the dfl-pci driver. Signed-off-by: Matthew Gerlach Signed-off-by: Tianfei Zhang Tested-by: Marco Pagani Reviewed-by: Tom Rix Acked-by: Wu Hao Acked-by: Xu Yilun Link: https://lore.kernel.org/r/20220719145644.242481-1-matthew.gerlach@linux.intel.com Signed-off-by: Xu Yilun --- drivers/fpga/dfl-pci.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/fpga/dfl-pci.c b/drivers/fpga/dfl-pci.c index fd1fa55c9113..0914e7328b1a 100644 --- a/drivers/fpga/dfl-pci.c +++ b/drivers/fpga/dfl-pci.c @@ -77,12 +77,18 @@ static void cci_pci_free_irq(struct pci_dev *pcidev) #define PCIE_DEVICE_ID_INTEL_PAC_D5005 0x0B2B #define PCIE_DEVICE_ID_SILICOM_PAC_N5010 0x1000 #define PCIE_DEVICE_ID_SILICOM_PAC_N5011 0x1001 +#define PCIE_DEVICE_ID_INTEL_DFL 0xbcce +/* PCI Subdevice ID for PCIE_DEVICE_ID_INTEL_DFL */ +#define PCIE_SUBDEVICE_ID_INTEL_N6000 0x1770 +#define PCIE_SUBDEVICE_ID_INTEL_N6001 0x1771 +#define PCIE_SUBDEVICE_ID_INTEL_C6100 0x17d4 /* VF Device */ #define PCIE_DEVICE_ID_VF_INT_5_X 0xBCBF #define PCIE_DEVICE_ID_VF_INT_6_X 0xBCC1 #define PCIE_DEVICE_ID_VF_DSC_1_X 0x09C5 #define PCIE_DEVICE_ID_INTEL_PAC_D5005_VF 0x0B2C +#define PCIE_DEVICE_ID_INTEL_DFL_VF 0xbccf static struct pci_device_id cci_pcie_id_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_PF_INT_5_X),}, @@ -96,6 +102,18 @@ static struct pci_device_id cci_pcie_id_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_INTEL_PAC_D5005_VF),}, {PCI_DEVICE(PCI_VENDOR_ID_SILICOM_DENMARK, PCIE_DEVICE_ID_SILICOM_PAC_N5010),}, {PCI_DEVICE(PCI_VENDOR_ID_SILICOM_DENMARK, PCIE_DEVICE_ID_SILICOM_PAC_N5011),}, + {PCI_DEVICE_SUB(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_INTEL_DFL, + PCI_VENDOR_ID_INTEL, PCIE_SUBDEVICE_ID_INTEL_N6000),}, + {PCI_DEVICE_SUB(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_INTEL_DFL_VF, + PCI_VENDOR_ID_INTEL, PCIE_SUBDEVICE_ID_INTEL_N6000),}, + {PCI_DEVICE_SUB(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_INTEL_DFL, + PCI_VENDOR_ID_INTEL, PCIE_SUBDEVICE_ID_INTEL_N6001),}, + {PCI_DEVICE_SUB(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_INTEL_DFL_VF, + PCI_VENDOR_ID_INTEL, PCIE_SUBDEVICE_ID_INTEL_N6001),}, + {PCI_DEVICE_SUB(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_INTEL_DFL, + PCI_VENDOR_ID_INTEL, PCIE_SUBDEVICE_ID_INTEL_C6100),}, + {PCI_DEVICE_SUB(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_INTEL_DFL_VF, + PCI_VENDOR_ID_INTEL, PCIE_SUBDEVICE_ID_INTEL_C6100),}, {0,} }; MODULE_DEVICE_TABLE(pci, cci_pcie_id_tbl); -- cgit v1.2.3 From 2a0805f55b8e70241709accdf3a7d59ad316306c Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Thu, 28 Jul 2022 08:50:13 +0100 Subject: fpga: microchip-spi: add missing module author entry Add the missing MODULE_AUTHOR entry for the Microchip spi-slave FPGA programming driver. Signed-off-by: Conor Dooley Acked-by: Ivan Bornyakov Reviewed-by: Tom Rix Acked-by: Xu Yilun Link: https://lore.kernel.org/r/20220728075012.3136914-1-conor.dooley@microchip.com Signed-off-by: Xu Yilun --- drivers/fpga/microchip-spi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/fpga/microchip-spi.c b/drivers/fpga/microchip-spi.c index bd284c7b8dc9..7436976ea904 100644 --- a/drivers/fpga/microchip-spi.c +++ b/drivers/fpga/microchip-spi.c @@ -395,4 +395,5 @@ static struct spi_driver mpf_driver = { module_spi_driver(mpf_driver); MODULE_DESCRIPTION("Microchip Polarfire SPI FPGA Manager"); +MODULE_AUTHOR("Ivan Bornyakov "); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From e167b2c3a0e631a51bbdf96f8e8550fe314cfbbd Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 27 Jul 2022 18:43:47 +0200 Subject: dt-bindings: fpga: microchip,mpf-spi-fpga-mgr: use spi-peripheral-props.yaml Instead of listing directly properties typical for SPI peripherals, reference the spi-peripheral-props.yaml schema. This allows using all properties typical for SPI-connected devices, even these which device bindings author did not tried yet. Remove the spi-* properties which now come via spi-peripheral-props.yaml schema, except for the cases when device schema adds some constraints like maximum frequency. While changing additionalProperties->unevaluatedProperties, put it in typical place, just before example DTS. Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring Reviewed-by: Conor Dooley Acked-by: Ivan Bornyakov Acked-by: Xu Yilun Link: https://lore.kernel.org/r/20220727164347.386398-1-krzysztof.kozlowski@linaro.org Signed-off-by: Xu Yilun --- .../devicetree/bindings/fpga/microchip,mpf-spi-fpga-mgr.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/fpga/microchip,mpf-spi-fpga-mgr.yaml b/Documentation/devicetree/bindings/fpga/microchip,mpf-spi-fpga-mgr.yaml index aee45cb15592..527532f039ce 100644 --- a/Documentation/devicetree/bindings/fpga/microchip,mpf-spi-fpga-mgr.yaml +++ b/Documentation/devicetree/bindings/fpga/microchip,mpf-spi-fpga-mgr.yaml @@ -22,13 +22,14 @@ properties: description: SPI chip select maxItems: 1 - spi-max-frequency: true - required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | -- cgit v1.2.3 From 9d9ec8d01443832d4e6d2f46bfd6b3270f390be3 Mon Sep 17 00:00:00 2001 From: Joe Simmons-Talbott Date: Wed, 27 Jul 2022 14:18:54 -0400 Subject: iio: Add blank lines after declarations. As reported by checkpatch.pl add blank lines after declarations. Signed-off-by: Joe Simmons-Talbott Link: https://lore.kernel.org/r/20220727181855.589052-2-joetalbott@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 1 + drivers/iio/industrialio-core.c | 7 +++++++ drivers/iio/industrialio-trigger.c | 1 + 3 files changed, 9 insertions(+) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index acc2b6c05d57..47a6e97f8e48 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -940,6 +940,7 @@ struct iio_demux_table { static void iio_buffer_demux_free(struct iio_buffer *buffer) { struct iio_demux_table *p, *q; + list_for_each_entry_safe(p, q, &buffer->demux_list, l) { list_del(&p->l); kfree(p); diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 0f4dbda3b9d3..40ebc63b7919 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -236,6 +236,7 @@ static int iio_sysfs_match_string_with_gaps(const char * const *array, size_t n, struct dentry *iio_get_debugfs_dentry(struct iio_dev *indio_dev) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + return iio_dev_opaque->debugfs_dentry; } EXPORT_SYMBOL_GPL(iio_get_debugfs_dentry); @@ -447,6 +448,7 @@ static const struct file_operations iio_debugfs_reg_fops = { static void iio_device_unregister_debugfs(struct iio_dev *indio_dev) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + debugfs_remove_recursive(iio_dev_opaque->debugfs_dentry); } @@ -1021,6 +1023,7 @@ int __iio_device_attr_init(struct device_attribute *dev_attr, int ret = 0; char *name = NULL; char *full_postfix; + sysfs_attr_init(&dev_attr->attr); /* Build up postfix of __postfix */ @@ -1355,6 +1358,7 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, if (chan->ext_info) { unsigned int i = 0; + for (ext_info = chan->ext_info; ext_info->name; ext_info++) { ret = __iio_add_chan_devattr(ext_info->name, chan, @@ -1403,6 +1407,7 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); + return sysfs_emit(buf, "%s\n", indio_dev->name); } @@ -1412,6 +1417,7 @@ static ssize_t label_show(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); + return sysfs_emit(buf, "%s\n", indio_dev->label); } @@ -1777,6 +1783,7 @@ static int iio_chrdev_release(struct inode *inode, struct file *filp) struct iio_dev_opaque *iio_dev_opaque = container_of(inode->i_cdev, struct iio_dev_opaque, chrdev); struct iio_dev *indio_dev = &iio_dev_opaque->indio_dev; + kfree(ib); clear_bit(IIO_BUSY_BIT_POS, &iio_dev_opaque->flags); iio_device_put(indio_dev); diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index b78814d869b7..6885a186fe27 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -50,6 +50,7 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_trigger *trig = to_iio_trigger(dev); + return sysfs_emit(buf, "%s\n", trig->name); } -- cgit v1.2.3 From 3d4b8291df3b5c9bc62ea8213b5483c4ec852947 Mon Sep 17 00:00:00 2001 From: Joe Simmons-Talbott Date: Wed, 27 Jul 2022 14:18:55 -0400 Subject: iio: Fix indentation for multiline conditional. As reported by checkpatch.pl make indentation match previous conditional. Signed-off-by: Joe Simmons-Talbott Link: https://lore.kernel.org/r/20220727181855.589052-3-joetalbott@gmail.com 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 47a6e97f8e48..228598b82a2f 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -843,8 +843,8 @@ static int iio_verify_update(struct iio_dev *indio_dev, * to verify. */ if (remove_buffer && !insert_buffer && - list_is_singular(&iio_dev_opaque->buffer_list)) - return 0; + list_is_singular(&iio_dev_opaque->buffer_list)) + return 0; modes = indio_dev->modes; -- cgit v1.2.3 From 857f09f605bae6702b5ec9afdc47c6b188d7cdf6 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 27 Jul 2022 17:52:03 +0200 Subject: dt-bindings: iio: adc: ti,am3359-adc: add ti,am654-adc Document the ti,am654-adc compatible already used in DTS: arch/arm64/boot/dts/ti/k3-am642-evm.dtb: adc: compatible:0: 'ti,am654-adc' is not one of ['ti,am3359-adc', 'ti,am4372-adc'] Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring Link: https://lore.kernel.org/r/20220727155203.320929-1-krzysztof.kozlowski@linaro.org Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/ti,am3359-adc.yaml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/adc/ti,am3359-adc.yaml b/Documentation/devicetree/bindings/iio/adc/ti,am3359-adc.yaml index d6f21d5cccd7..b32be24a9f98 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,am3359-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,am3359-adc.yaml @@ -11,9 +11,14 @@ maintainers: properties: compatible: - enum: - - ti,am3359-adc - - ti,am4372-adc + oneOf: + - enum: + - ti,am3359-adc + - ti,am4372-adc + - items: + - enum: + - ti,am654-adc + - const: ti,am3359-adc '#io-channel-cells': const: 1 -- cgit v1.2.3 From 9e8284501c8d9e2bde4dfcddaf0201ee7cc8f2a7 Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Sat, 23 Jul 2022 11:20:30 +0200 Subject: MAINTAINERS: Update Microchip MCP3911 to Maintained The actual status of the code is Maintained. Signed-off-by: Marcus Folkesson Cc: Kent Gustavsson Cc: Jonathan Cameron Cc: linux-iio Link: https://lore.kernel.org/r/20220723092030.260812-1-marcus.folkesson@gmail.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 8a5012ba6ff9..b8b6544ba27c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13405,7 +13405,7 @@ MICROCHIP MCP3911 ADC DRIVER M: Marcus Folkesson M: Kent Gustavsson L: linux-iio@vger.kernel.org -S: Supported +S: Maintained F: Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml F: drivers/iio/adc/mcp3911.c -- cgit v1.2.3 From bd1d558c9c65cf56623a3a237a2998697586c409 Mon Sep 17 00:00:00 2001 From: Martin Larsson Date: Wed, 20 Jul 2022 15:31:54 +0000 Subject: iio: adc: imx8qxp-adc: propagate regulator_get_voltage error If the ADC vref regulator returns an error, for example, if CONFIG_REGULATOR is not set, the error will be used as a reference voltage. Introduce a guard for negative return values instead of unconditionally casting it to u32. Acked-by: Haibo Chen Signed-off-by: Martin Larsson Reviewed-by: Fabio Estevam Link: https://lore.kernel.org/r/20220720153136.3502440-1-martin.larsson@actia.se Signed-off-by: Jonathan Cameron --- drivers/iio/adc/imx8qxp-adc.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/imx8qxp-adc.c b/drivers/iio/adc/imx8qxp-adc.c index e48446784a0a..36777b827165 100644 --- a/drivers/iio/adc/imx8qxp-adc.c +++ b/drivers/iio/adc/imx8qxp-adc.c @@ -202,7 +202,7 @@ static int imx8qxp_adc_read_raw(struct iio_dev *indio_dev, struct imx8qxp_adc *adc = iio_priv(indio_dev); struct device *dev = adc->dev; - u32 ctrl, vref_uv; + u32 ctrl; long ret; switch (mask) { @@ -245,8 +245,10 @@ static int imx8qxp_adc_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - vref_uv = regulator_get_voltage(adc->vref); - *val = vref_uv / 1000; + ret = regulator_get_voltage(adc->vref); + if (ret < 0) + return ret; + *val = ret / 1000; *val2 = 12; return IIO_VAL_FRACTIONAL_LOG2; -- cgit v1.2.3 From af0a61e940f8cea62c3c9a18ff5b3830b16e2087 Mon Sep 17 00:00:00 2001 From: Shreeya Patel Date: Mon, 25 Jul 2022 16:10:49 +0530 Subject: dt-bindings: Document ltrf216a light sensor bindings Add devicetree bindings for ltrf216a ambient light sensor. Reviewed-by: Rob Herring Signed-off-by: Shreeya Patel Link: https://lore.kernel.org/r/20220725104050.491396-2-shreeya.patel@collabora.com Signed-off-by: Jonathan Cameron --- .../bindings/iio/light/liteon,ltrf216a.yaml | 49 ++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/light/liteon,ltrf216a.yaml diff --git a/Documentation/devicetree/bindings/iio/light/liteon,ltrf216a.yaml b/Documentation/devicetree/bindings/iio/light/liteon,ltrf216a.yaml new file mode 100644 index 000000000000..7de1b0e721ca --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/liteon,ltrf216a.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/light/liteon,ltrf216a.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: LTRF216A Ambient Light Sensor + +maintainers: + - Shreeya Patel + +description: + Ambient light sensing with an i2c interface. + +properties: + compatible: + const: liteon,ltrf216a + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + vdd-supply: + description: Regulator that provides power to the sensor. + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + light-sensor@53 { + compatible = "liteon,ltrf216a"; + reg = <0x53>; + vdd-supply = <&vdd_regulator>; + interrupt-parent = <&gpio0>; + interrupts = <5 IRQ_TYPE_LEVEL_LOW>; + }; + }; -- cgit v1.2.3 From 83f0bcd40d5cf88870d2f2bb8b97c772b92a3d1f Mon Sep 17 00:00:00 2001 From: Shreeya Patel Date: Mon, 25 Jul 2022 16:10:50 +0530 Subject: iio: light: Add support for ltrf216a sensor Add initial support for ltrf216a ambient light sensor. Datasheet: https://gitlab.collabora.com/shreeya/iio/-/blob/master/LTRF216A.pdf Reviewed-by: Andy Shevchenko Co-developed-by: Zhigang Shi Signed-off-by: Zhigang Shi Co-developed-by: Dmitry Osipenko Signed-off-by: Dmitry Osipenko Signed-off-by: Shreeya Patel Link: https://lore.kernel.org/r/20220725104050.491396-3-shreeya.patel@collabora.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/Kconfig | 11 + drivers/iio/light/Makefile | 1 + drivers/iio/light/ltrf216a.c | 537 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 549 insertions(+) create mode 100644 drivers/iio/light/ltrf216a.c diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 8537e88f02e3..7cf6e8490123 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -331,6 +331,17 @@ config LTR501 This driver can also be built as a module. If so, the module will be called ltr501. +config LTRF216A + tristate "Liteon LTRF216A Light Sensor" + depends on I2C + select REGMAP_I2C + help + If you say Y or M here, you get support for Liteon LTRF216A + Ambient Light Sensor. + + If built as a dynamically linked module, it will be called + ltrf216a. + config LV0104CS tristate "LV0104CS Ambient Light Sensor" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index d10912faf964..6f23817fae6f 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_ISL29125) += isl29125.o obj-$(CONFIG_JSA1212) += jsa1212.o obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o obj-$(CONFIG_LTR501) += ltr501.o +obj-$(CONFIG_LTRF216A) += ltrf216a.o obj-$(CONFIG_LV0104CS) += lv0104cs.o obj-$(CONFIG_MAX44000) += max44000.o obj-$(CONFIG_MAX44009) += max44009.o diff --git a/drivers/iio/light/ltrf216a.c b/drivers/iio/light/ltrf216a.c new file mode 100644 index 000000000000..e6e24e70d2b9 --- /dev/null +++ b/drivers/iio/light/ltrf216a.c @@ -0,0 +1,537 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * LTRF216A Ambient Light Sensor + * + * Copyright (C) 2022 Collabora, Ltd. + * Author: Shreeya Patel + * + * Copyright (C) 2021 Lite-On Technology Corp (Singapore) + * Author: Shi Zhigang + * + * IIO driver for LTRF216A (7-bit I2C slave address 0x53). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define LTRF216A_ALS_RESET_MASK BIT(4) +#define LTRF216A_ALS_DATA_STATUS BIT(3) +#define LTRF216A_ALS_ENABLE_MASK BIT(1) +#define LTRF216A_MAIN_CTRL 0x00 +#define LTRF216A_ALS_MEAS_RES 0x04 +#define LTRF216A_ALS_GAIN 0x05 +#define LTRF216A_PART_ID 0x06 +#define LTRF216A_MAIN_STATUS 0x07 +#define LTRF216A_ALS_CLEAR_DATA_0 0x0a +#define LTRF216A_ALS_CLEAR_DATA_1 0x0b +#define LTRF216A_ALS_CLEAR_DATA_2 0x0c +#define LTRF216A_ALS_DATA_0 0x0d +#define LTRF216A_ALS_DATA_1 0x0e +#define LTRF216A_ALS_DATA_2 0x0f +#define LTRF216A_INT_CFG 0x19 +#define LTRF216A_INT_PST 0x1a +#define LTRF216A_ALS_THRES_UP_0 0x21 +#define LTRF216A_ALS_THRES_UP_1 0x22 +#define LTRF216A_ALS_THRES_UP_2 0x23 +#define LTRF216A_ALS_THRES_LOW_0 0x24 +#define LTRF216A_ALS_THRES_LOW_1 0x25 +#define LTRF216A_ALS_THRES_LOW_2 0x26 +#define LTRF216A_ALS_READ_DATA_DELAY_US 20000 + +static const int ltrf216a_int_time_available[][2] = { + { 0, 400000 }, + { 0, 200000 }, + { 0, 100000 }, + { 0, 50000 }, + { 0, 25000 }, +}; + +static const int ltrf216a_int_time_reg[][2] = { + { 400, 0x03 }, + { 200, 0x13 }, + { 100, 0x22 }, + { 50, 0x31 }, + { 25, 0x40 }, +}; + +/* + * Window Factor is needed when the device is under Window glass + * with coated tinted ink. This is to compensate for the light loss + * due to the lower transmission rate of the window glass and helps + * in calculating lux. + */ +#define LTRF216A_WIN_FAC 1 + +struct ltrf216a_data { + struct regmap *regmap; + struct i2c_client *client; + u32 int_time; + u16 int_time_fac; + u8 als_gain_fac; + /* + * Protects regmap accesses and makes sure integration time + * remains constant during the measurement of lux. + */ + struct mutex lock; +}; + +static const struct iio_chan_spec ltrf216a_channels[] = { + { + .type = IIO_LIGHT, + .info_mask_separate = + BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_INT_TIME), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_INT_TIME), + }, +}; + +static void ltrf216a_reset(struct iio_dev *indio_dev) +{ + struct ltrf216a_data *data = iio_priv(indio_dev); + + /* reset sensor, chip fails to respond to this, so ignore any errors */ + regmap_write(data->regmap, LTRF216A_MAIN_CTRL, LTRF216A_ALS_RESET_MASK); + + /* reset time */ + usleep_range(1000, 2000); +} + +static int ltrf216a_enable(struct iio_dev *indio_dev) +{ + struct ltrf216a_data *data = iio_priv(indio_dev); + struct device *dev = &data->client->dev; + int ret; + + /* enable sensor */ + ret = regmap_set_bits(data->regmap, + LTRF216A_MAIN_CTRL, LTRF216A_ALS_ENABLE_MASK); + if (ret) { + dev_err(dev, "failed to enable sensor: %d\n", ret); + return ret; + } + + /* sleep for one integration cycle after enabling the device */ + msleep(ltrf216a_int_time_reg[0][0]); + + return 0; +} + +static int ltrf216a_disable(struct iio_dev *indio_dev) +{ + struct ltrf216a_data *data = iio_priv(indio_dev); + struct device *dev = &data->client->dev; + int ret; + + ret = regmap_write(data->regmap, LTRF216A_MAIN_CTRL, 0); + if (ret) + dev_err(dev, "failed to disable sensor: %d\n", ret); + + return ret; +} + +static void ltrf216a_cleanup(void *data) +{ + struct iio_dev *indio_dev = data; + + ltrf216a_disable(indio_dev); +} + +static int ltrf216a_set_int_time(struct ltrf216a_data *data, int itime) +{ + struct device *dev = &data->client->dev; + unsigned int i; + u8 reg_val; + int ret; + + for (i = 0; i < ARRAY_SIZE(ltrf216a_int_time_available); i++) { + if (ltrf216a_int_time_available[i][1] == itime) + break; + } + if (i == ARRAY_SIZE(ltrf216a_int_time_available)) + return -EINVAL; + + reg_val = ltrf216a_int_time_reg[i][1]; + + ret = regmap_write(data->regmap, LTRF216A_ALS_MEAS_RES, reg_val); + if (ret) { + dev_err(dev, "failed to set integration time: %d\n", ret); + return ret; + } + + data->int_time_fac = ltrf216a_int_time_reg[i][0]; + data->int_time = itime; + + return 0; +} + +static int ltrf216a_get_int_time(struct ltrf216a_data *data, + int *val, int *val2) +{ + *val = 0; + *val2 = data->int_time; + return IIO_VAL_INT_PLUS_MICRO; +} + +static int ltrf216a_set_power_state(struct ltrf216a_data *data, bool on) +{ + struct device *dev = &data->client->dev; + int ret = 0; + + if (on) { + ret = pm_runtime_resume_and_get(dev); + if (ret) { + dev_err(dev, "failed to resume runtime PM: %d\n", ret); + return ret; + } + } else { + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + } + + return ret; +} + +static int ltrf216a_read_data(struct ltrf216a_data *data, u8 addr) +{ + struct device *dev = &data->client->dev; + int ret, val; + u8 buf[3]; + + ret = regmap_read_poll_timeout(data->regmap, LTRF216A_MAIN_STATUS, + val, val & LTRF216A_ALS_DATA_STATUS, + LTRF216A_ALS_READ_DATA_DELAY_US, + LTRF216A_ALS_READ_DATA_DELAY_US * 50); + if (ret) { + dev_err(dev, "failed to wait for measurement data: %d\n", ret); + return ret; + } + + ret = regmap_bulk_read(data->regmap, addr, buf, sizeof(buf)); + if (ret) { + dev_err(dev, "failed to read measurement data: %d\n", ret); + return ret; + } + + return get_unaligned_le24(&buf[0]); +} + +static int ltrf216a_get_lux(struct ltrf216a_data *data) +{ + int ret, greendata; + u64 lux, div; + + ret = ltrf216a_set_power_state(data, true); + if (ret) + return ret; + + greendata = ltrf216a_read_data(data, LTRF216A_ALS_DATA_0); + if (greendata < 0) + return greendata; + + ltrf216a_set_power_state(data, false); + + lux = greendata * 45 * LTRF216A_WIN_FAC * 100; + div = data->als_gain_fac * data->int_time_fac * 100; + + return div_u64(lux, div); +} + +static int ltrf216a_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct ltrf216a_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + mutex_lock(&data->lock); + ret = ltrf216a_get_lux(data); + mutex_unlock(&data->lock); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_INT_TIME: + mutex_lock(&data->lock); + ret = ltrf216a_get_int_time(data, val, val2); + mutex_unlock(&data->lock); + return ret; + default: + return -EINVAL; + } +} + +static int ltrf216a_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct ltrf216a_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + if (val != 0) + return -EINVAL; + mutex_lock(&data->lock); + ret = ltrf216a_set_int_time(data, val2); + mutex_unlock(&data->lock); + return ret; + default: + return -EINVAL; + } +} + +static int ltrf216a_read_available(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_INT_TIME: + *length = ARRAY_SIZE(ltrf216a_int_time_available) * 2; + *vals = (const int *)ltrf216a_int_time_available; + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static const struct iio_info ltrf216a_info = { + .read_raw = ltrf216a_read_raw, + .write_raw = ltrf216a_write_raw, + .read_avail = ltrf216a_read_available, +}; + +static bool ltrf216a_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LTRF216A_MAIN_CTRL: + case LTRF216A_ALS_MEAS_RES: + case LTRF216A_ALS_GAIN: + case LTRF216A_PART_ID: + case LTRF216A_MAIN_STATUS: + case LTRF216A_ALS_CLEAR_DATA_0: + case LTRF216A_ALS_CLEAR_DATA_1: + case LTRF216A_ALS_CLEAR_DATA_2: + case LTRF216A_ALS_DATA_0: + case LTRF216A_ALS_DATA_1: + case LTRF216A_ALS_DATA_2: + case LTRF216A_INT_CFG: + case LTRF216A_INT_PST: + case LTRF216A_ALS_THRES_UP_0: + case LTRF216A_ALS_THRES_UP_1: + case LTRF216A_ALS_THRES_UP_2: + case LTRF216A_ALS_THRES_LOW_0: + case LTRF216A_ALS_THRES_LOW_1: + case LTRF216A_ALS_THRES_LOW_2: + return true; + default: + return false; + } +} + +static bool ltrf216a_writable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LTRF216A_MAIN_CTRL: + case LTRF216A_ALS_MEAS_RES: + case LTRF216A_ALS_GAIN: + case LTRF216A_INT_CFG: + case LTRF216A_INT_PST: + case LTRF216A_ALS_THRES_UP_0: + case LTRF216A_ALS_THRES_UP_1: + case LTRF216A_ALS_THRES_UP_2: + case LTRF216A_ALS_THRES_LOW_0: + case LTRF216A_ALS_THRES_LOW_1: + case LTRF216A_ALS_THRES_LOW_2: + return true; + default: + return false; + } +} + +static bool ltrf216a_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LTRF216A_MAIN_STATUS: + case LTRF216A_ALS_CLEAR_DATA_0: + case LTRF216A_ALS_CLEAR_DATA_1: + case LTRF216A_ALS_CLEAR_DATA_2: + case LTRF216A_ALS_DATA_0: + case LTRF216A_ALS_DATA_1: + case LTRF216A_ALS_DATA_2: + return true; + default: + return false; + } +} + +static bool ltrf216a_precious_reg(struct device *dev, unsigned int reg) +{ + return reg == LTRF216A_MAIN_STATUS; +} + +static const struct regmap_config ltrf216a_regmap_config = { + .name = "ltrf216a", + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .max_register = LTRF216A_ALS_THRES_LOW_2, + .readable_reg = ltrf216a_readable_reg, + .writeable_reg = ltrf216a_writable_reg, + .volatile_reg = ltrf216a_volatile_reg, + .precious_reg = ltrf216a_precious_reg, + .disable_locking = true, +}; + +static int ltrf216a_probe(struct i2c_client *client) +{ + struct ltrf216a_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + + data->regmap = devm_regmap_init_i2c(client, <rf216a_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(&client->dev, PTR_ERR(data->regmap), + "regmap initialization failed\n"); + + i2c_set_clientdata(client, indio_dev); + data->client = client; + + mutex_init(&data->lock); + + indio_dev->info = <rf216a_info; + indio_dev->name = "ltrf216a"; + indio_dev->channels = ltrf216a_channels; + indio_dev->num_channels = ARRAY_SIZE(ltrf216a_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = pm_runtime_set_active(&client->dev); + if (ret) + return ret; + + /* reset sensor, chip fails to respond to this, so ignore any errors */ + ltrf216a_reset(indio_dev); + + ret = regmap_reinit_cache(data->regmap, <rf216a_regmap_config); + if (ret) + return dev_err_probe(&client->dev, ret, + "failed to reinit regmap cache\n"); + + ret = ltrf216a_enable(indio_dev); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&client->dev, ltrf216a_cleanup, + indio_dev); + if (ret) + return ret; + + ret = devm_pm_runtime_enable(&client->dev); + if (ret) + return dev_err_probe(&client->dev, ret, + "failed to enable runtime PM\n"); + + pm_runtime_set_autosuspend_delay(&client->dev, 1000); + pm_runtime_use_autosuspend(&client->dev); + + data->int_time = 100000; + data->int_time_fac = 100; + data->als_gain_fac = 3; + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static int ltrf216a_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct ltrf216a_data *data = iio_priv(indio_dev); + int ret; + + ret = ltrf216a_disable(indio_dev); + if (ret) + return ret; + + regcache_cache_only(data->regmap, true); + + return 0; +} + +static int ltrf216a_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct ltrf216a_data *data = iio_priv(indio_dev); + int ret; + + regcache_cache_only(data->regmap, false); + regcache_mark_dirty(data->regmap); + ret = regcache_sync(data->regmap); + if (ret) + goto cache_only; + + ret = ltrf216a_enable(indio_dev); + if (ret) + goto cache_only; + + return 0; + +cache_only: + regcache_cache_only(data->regmap, true); + + return ret; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(ltrf216a_pm_ops, ltrf216a_runtime_suspend, + ltrf216a_runtime_resume, NULL); + +static const struct i2c_device_id ltrf216a_id[] = { + { "ltrf216a" }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ltrf216a_id); + +static const struct of_device_id ltrf216a_of_match[] = { + { .compatible = "liteon,ltrf216a" }, + { .compatible = "ltr,ltrf216a" }, + {} +}; +MODULE_DEVICE_TABLE(of, ltrf216a_of_match); + +static struct i2c_driver ltrf216a_driver = { + .driver = { + .name = "ltrf216a", + .pm = pm_ptr(<rf216a_pm_ops), + .of_match_table = ltrf216a_of_match, + }, + .probe_new = ltrf216a_probe, + .id_table = ltrf216a_id, +}; +module_i2c_driver(ltrf216a_driver); + +MODULE_AUTHOR("Shreeya Patel "); +MODULE_AUTHOR("Shi Zhigang "); +MODULE_DESCRIPTION("LTRF216A ambient light sensor driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 3b7eee5b38755501b8cc09b8f3b46c005af40a50 Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Tue, 19 Jul 2022 22:52:43 +0800 Subject: dt-bindings: iio: adc: Add rtq6056 adc support Add the documentation for Richtek rtq6056. Signed-off-by: ChiYuan Huang Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/1658242365-27797-2-git-send-email-u0084500@gmail.com Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/richtek,rtq6056.yaml | 56 ++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/richtek,rtq6056.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/richtek,rtq6056.yaml b/Documentation/devicetree/bindings/iio/adc/richtek,rtq6056.yaml new file mode 100644 index 000000000000..88e008629ea8 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/richtek,rtq6056.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/richtek,rtq6056.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: RTQ6056 Bi-Directional Current and Power Monitor with 16-bit ADC + +maintainers: + - ChiYuan Huang + +description: | + The RTQ6056 is a high accuracy current-sense monitor with I2C and SMBus + interface, and the device provides full information for system by reading + out the loading current and power. + + The device monitors both of the drops across sense resistor and the BUS + voltage, converts into the current in amperes, and power in watts through + internal analog-to-digital converter ADC. The programmable calibration, + adjustable conversion time, and averaging function are also built in for + more design flexibility. + + Datasheet is available at + https://www.richtek.com/assets/product_file/RTQ6056/DSQ6056-00.pdf + +properties: + compatible: + const: richtek,rtq6056 + + reg: + maxItems: 1 + + "#io-channel-cells": + const: 1 + + shunt-resistor-micro-ohms: + description: Shunt IN+/IN- sensing node resistor + +required: + - compatible + - reg + - "#io-channel-cells" + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + adc@40 { + compatible = "richtek,rtq6056"; + reg = <0x40>; + #io-channel-cells = <1>; + }; + }; -- cgit v1.2.3 From 4396f45d211b6aa2f3a821f0a3a77c992335724b Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Tue, 19 Jul 2022 22:52:44 +0800 Subject: iio: adc: Add rtq6056 support Add Richtek rtq6056 supporting. It can be used for the system to monitor load current and power with 16-bit resolution. Signed-off-by: ChiYuan Huang Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/1658242365-27797-3-git-send-email-u0084500@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 15 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/rtq6056.c | 661 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 677 insertions(+) create mode 100644 drivers/iio/adc/rtq6056.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 7fe5930891e0..5a3e8d9ae26c 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -919,6 +919,21 @@ config ROCKCHIP_SARADC To compile this driver as a module, choose M here: the module will be called rockchip_saradc. +config RICHTEK_RTQ6056 + tristate "Richtek RTQ6056 Current and Power Monitor ADC" + depends on I2C + select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to enable RQT6056 ADC support. + RTQ6056 is a high accuracy current-sense monitor with I2C and SMBus + compatible interface, and the device provides full information for + system by reading out the load current and power. + + This driver can also be built as a module. If so, the module will be + called rtq6056. + config RZG2L_ADC tristate "Renesas RZ/G2L ADC driver" depends on ARCH_RZG2L || COMPILE_TEST diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 1772a549a3c8..c1a861a978ad 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -85,6 +85,7 @@ obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o obj-$(CONFIG_RN5T618_ADC) += rn5t618-adc.o obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o +obj-$(CONFIG_RICHTEK_RTQ6056) += rtq6056.o obj-$(CONFIG_RZG2L_ADC) += rzg2l_adc.o obj-$(CONFIG_SC27XX_ADC) += sc27xx_adc.o obj-$(CONFIG_SPEAR_ADC) += spear_adc.o diff --git a/drivers/iio/adc/rtq6056.c b/drivers/iio/adc/rtq6056.c new file mode 100644 index 000000000000..c1b2e8dc9a26 --- /dev/null +++ b/drivers/iio/adc/rtq6056.c @@ -0,0 +1,661 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022 Richtek Technology Corp. + * + * ChiYuan Huang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define RTQ6056_REG_CONFIG 0x00 +#define RTQ6056_REG_SHUNTVOLT 0x01 +#define RTQ6056_REG_BUSVOLT 0x02 +#define RTQ6056_REG_POWER 0x03 +#define RTQ6056_REG_CURRENT 0x04 +#define RTQ6056_REG_CALIBRATION 0x05 +#define RTQ6056_REG_MASKENABLE 0x06 +#define RTQ6056_REG_ALERTLIMIT 0x07 +#define RTQ6056_REG_MANUFACTID 0xFE +#define RTQ6056_REG_DIEID 0xFF + +#define RTQ6056_VENDOR_ID 0x1214 +#define RTQ6056_DEFAULT_CONFIG 0x4127 +#define RTQ6056_CONT_ALLON 7 + +enum { + RTQ6056_CH_VSHUNT = 0, + RTQ6056_CH_VBUS, + RTQ6056_CH_POWER, + RTQ6056_CH_CURRENT, + RTQ6056_MAX_CHANNEL +}; + +enum { + F_OPMODE = 0, + F_VSHUNTCT, + F_VBUSCT, + F_AVG, + F_RESET, + F_MAX_FIELDS +}; + +struct rtq6056_priv { + struct device *dev; + struct regmap *regmap; + struct regmap_field *rm_fields[F_MAX_FIELDS]; + u32 shunt_resistor_uohm; + int vshuntct_us; + int vbusct_us; + int avg_sample; +}; + +static const struct reg_field rtq6056_reg_fields[F_MAX_FIELDS] = { + [F_OPMODE] = REG_FIELD(RTQ6056_REG_CONFIG, 0, 2), + [F_VSHUNTCT] = REG_FIELD(RTQ6056_REG_CONFIG, 3, 5), + [F_VBUSCT] = REG_FIELD(RTQ6056_REG_CONFIG, 6, 8), + [F_AVG] = REG_FIELD(RTQ6056_REG_CONFIG, 9, 11), + [F_RESET] = REG_FIELD(RTQ6056_REG_CONFIG, 15, 15), +}; + +static const struct iio_chan_spec rtq6056_channels[RTQ6056_MAX_CHANNEL + 1] = { + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 0, + .address = RTQ6056_REG_SHUNTVOLT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 1, + .address = RTQ6056_REG_BUSVOLT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .scan_index = 1, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + }, + { + .type = IIO_POWER, + .indexed = 1, + .channel = 2, + .address = RTQ6056_REG_POWER, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .scan_index = 2, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + }, + { + .type = IIO_CURRENT, + .indexed = 1, + .channel = 3, + .address = RTQ6056_REG_CURRENT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .scan_index = 3, + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(RTQ6056_MAX_CHANNEL), +}; + +static int rtq6056_adc_read_channel(struct rtq6056_priv *priv, + struct iio_chan_spec const *ch, + int *val) +{ + struct device *dev = priv->dev; + unsigned int addr = ch->address; + unsigned int regval; + int ret; + + pm_runtime_get_sync(dev); + ret = regmap_read(priv->regmap, addr, ®val); + pm_runtime_mark_last_busy(dev); + pm_runtime_put(dev); + if (ret) + return ret; + + /* Power and VBUS is unsigned 16-bit, others are signed 16-bit */ + if (addr == RTQ6056_REG_BUSVOLT || addr == RTQ6056_REG_POWER) + *val = regval; + else + *val = sign_extend32(regval, 16); + + return IIO_VAL_INT; +} + +static int rtq6056_adc_read_scale(struct iio_chan_spec const *ch, int *val, + int *val2) +{ + switch (ch->address) { + case RTQ6056_REG_SHUNTVOLT: + /* VSHUNT lsb 2.5uV */ + *val = 2500; + *val2 = 1000000; + return IIO_VAL_FRACTIONAL; + case RTQ6056_REG_BUSVOLT: + /* VBUS lsb 1.25mV */ + *val = 1250; + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + case RTQ6056_REG_POWER: + /* Power lsb 25mW */ + *val = 25; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +/* + * Sample frequency for channel VSHUNT and VBUS. The indices correspond + * with the bit value expected by the chip. And it can be found at + * https://www.richtek.com/assets/product_file/RTQ6056/DSQ6056-00.pdf + */ +static const int rtq6056_samp_freq_list[] = { + 7194, 4926, 3717, 1904, 964, 485, 243, 122, +}; + +static int rtq6056_adc_set_samp_freq(struct rtq6056_priv *priv, + struct iio_chan_spec const *ch, int val) +{ + struct regmap_field *rm_field; + unsigned int selector; + int *ct, ret; + + if (val > 7194 || val < 122) + return -EINVAL; + + if (ch->address == RTQ6056_REG_SHUNTVOLT) { + rm_field = priv->rm_fields[F_VSHUNTCT]; + ct = &priv->vshuntct_us; + } else if (ch->address == RTQ6056_REG_BUSVOLT) { + rm_field = priv->rm_fields[F_VBUSCT]; + ct = &priv->vbusct_us; + } else + return -EINVAL; + + selector = find_closest_descending(val, rtq6056_samp_freq_list, + ARRAY_SIZE(rtq6056_samp_freq_list)); + + ret = regmap_field_write(rm_field, selector); + if (ret) + return ret; + + *ct = 1000000 / rtq6056_samp_freq_list[selector]; + + return 0; +} + +/* + * Available averaging rate for rtq6056. The indices correspond with the bit + * value expected by the chip. And it can be found at + * https://www.richtek.com/assets/product_file/RTQ6056/DSQ6056-00.pdf + */ +static const int rtq6056_avg_sample_list[] = { + 1, 4, 16, 64, 128, 256, 512, 1024, +}; + +static int rtq6056_adc_set_average(struct rtq6056_priv *priv, int val) +{ + unsigned int selector; + int ret; + + if (val > 1024 || val < 1) + return -EINVAL; + + selector = find_closest(val, rtq6056_avg_sample_list, + ARRAY_SIZE(rtq6056_avg_sample_list)); + + ret = regmap_field_write(priv->rm_fields[F_AVG], selector); + if (ret) + return ret; + + priv->avg_sample = rtq6056_avg_sample_list[selector]; + + return 0; +} + +static int rtq6056_adc_get_sample_freq(struct rtq6056_priv *priv, + struct iio_chan_spec const *ch, int *val) +{ + int sample_time; + + if (ch->address == RTQ6056_REG_SHUNTVOLT) + sample_time = priv->vshuntct_us; + else if (ch->address == RTQ6056_REG_BUSVOLT) + sample_time = priv->vbusct_us; + else { + sample_time = priv->vshuntct_us + priv->vbusct_us; + sample_time *= priv->avg_sample; + } + + *val = 1000000 / sample_time; + + return IIO_VAL_INT; +} + +static int rtq6056_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct rtq6056_priv *priv = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return rtq6056_adc_read_channel(priv, chan, val); + case IIO_CHAN_INFO_SCALE: + return rtq6056_adc_read_scale(chan, val, val2); + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = priv->avg_sample; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + return rtq6056_adc_get_sample_freq(priv, chan, val); + default: + return -EINVAL; + } +} + +static int rtq6056_adc_read_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 = rtq6056_samp_freq_list; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(rtq6056_samp_freq_list); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = rtq6056_avg_sample_list; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(rtq6056_avg_sample_list); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int rtq6056_adc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct rtq6056_priv *priv = iio_priv(indio_dev); + int ret; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + ret = rtq6056_adc_set_samp_freq(priv, chan, val); + break; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + ret = rtq6056_adc_set_average(priv, val); + break; + default: + ret = -EINVAL; + break; + } + + iio_device_release_direct_mode(indio_dev); + + return ret; +} + +static const char *rtq6056_channel_labels[RTQ6056_MAX_CHANNEL] = { + [RTQ6056_CH_VSHUNT] = "Vshunt", + [RTQ6056_CH_VBUS] = "Vbus", + [RTQ6056_CH_POWER] = "Power", + [RTQ6056_CH_CURRENT] = "Current", +}; + +static int rtq6056_adc_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + char *label) +{ + return sysfs_emit(label, "%s\n", rtq6056_channel_labels[chan->channel]); +} + +static int rtq6056_set_shunt_resistor(struct rtq6056_priv *priv, + int resistor_uohm) +{ + unsigned int calib_val; + int ret; + + if (resistor_uohm <= 0) { + dev_err(priv->dev, "Invalid resistor [%d]\n", resistor_uohm); + return -EINVAL; + } + + /* calibration = 5120000 / (Rshunt (uOhm) * current lsb (1mA)) */ + calib_val = 5120000 / resistor_uohm; + ret = regmap_write(priv->regmap, RTQ6056_REG_CALIBRATION, calib_val); + if (ret) + return ret; + + priv->shunt_resistor_uohm = resistor_uohm; + + return 0; +} + +static ssize_t shunt_resistor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rtq6056_priv *priv = iio_priv(dev_to_iio_dev(dev)); + int vals[2] = { priv->shunt_resistor_uohm, 1000000 }; + + return iio_format_value(buf, IIO_VAL_FRACTIONAL, 1, vals); +} + +static ssize_t shunt_resistor_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 rtq6056_priv *priv = iio_priv(indio_dev); + int val, val_fract, ret; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = iio_str_to_fixpoint(buf, 100000, &val, &val_fract); + if (ret) + goto out_store; + + ret = rtq6056_set_shunt_resistor(priv, val * 1000000 + val_fract); + +out_store: + iio_device_release_direct_mode(indio_dev); + + return ret ?: len; +} + +static IIO_DEVICE_ATTR_RW(shunt_resistor, 0); + +static struct attribute *rtq6056_attributes[] = { + &iio_dev_attr_shunt_resistor.dev_attr.attr, + NULL +}; + +static const struct attribute_group rtq6056_attribute_group = { + .attrs = rtq6056_attributes, +}; + +static const struct iio_info rtq6056_info = { + .attrs = &rtq6056_attribute_group, + .read_raw = rtq6056_adc_read_raw, + .read_avail = rtq6056_adc_read_avail, + .write_raw = rtq6056_adc_write_raw, + .read_label = rtq6056_adc_read_label, +}; + +static irqreturn_t rtq6056_buffer_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct rtq6056_priv *priv = iio_priv(indio_dev); + struct device *dev = priv->dev; + struct { + u16 vals[RTQ6056_MAX_CHANNEL]; + s64 timestamp __aligned(8); + } data; + unsigned int raw; + int i = 0, bit, ret; + + memset(&data, 0, sizeof(data)); + + pm_runtime_get_sync(dev); + + for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { + unsigned int addr = rtq6056_channels[bit].address; + + ret = regmap_read(priv->regmap, addr, &raw); + if (ret) + goto out; + + data.vals[i++] = raw; + } + + iio_push_to_buffers_with_timestamp(indio_dev, &data, iio_get_time_ns(indio_dev)); + +out: + pm_runtime_mark_last_busy(dev); + pm_runtime_put(dev); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static void rtq6056_enter_shutdown_state(void *dev) +{ + struct rtq6056_priv *priv = dev_get_drvdata(dev); + + /* Enter shutdown state */ + regmap_field_write(priv->rm_fields[F_OPMODE], 0); +} + +static bool rtq6056_is_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RTQ6056_REG_CONFIG ... RTQ6056_REG_ALERTLIMIT: + case RTQ6056_REG_MANUFACTID ... RTQ6056_REG_DIEID: + return true; + default: + return false; + } +} + +static bool rtq6056_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RTQ6056_REG_CONFIG: + case RTQ6056_REG_CALIBRATION ... RTQ6056_REG_ALERTLIMIT: + return true; + default: + return false; + } +} + +static const struct regmap_config rtq6056_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .val_format_endian = REGMAP_ENDIAN_BIG, + .max_register = RTQ6056_REG_DIEID, + .readable_reg = rtq6056_is_readable_reg, + .writeable_reg = rtq6056_is_writeable_reg, +}; + +static int rtq6056_probe(struct i2c_client *i2c) +{ + struct iio_dev *indio_dev; + struct rtq6056_priv *priv; + struct device *dev = &i2c->dev; + struct regmap *regmap; + unsigned int vendor_id, shunt_resistor_uohm; + int ret; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -EOPNOTSUPP; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + priv = iio_priv(indio_dev); + priv->dev = dev; + priv->vshuntct_us = priv->vbusct_us = 1037; + priv->avg_sample = 1; + i2c_set_clientdata(i2c, priv); + + regmap = devm_regmap_init_i2c(i2c, &rtq6056_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to init regmap\n"); + + priv->regmap = regmap; + + ret = regmap_read(regmap, RTQ6056_REG_MANUFACTID, &vendor_id); + if (ret) + return dev_err_probe(dev, ret, + "Failed to get manufacturer info\n"); + + if (vendor_id != RTQ6056_VENDOR_ID) + return dev_err_probe(dev, -ENODEV, + "Invalid vendor id 0x%04x\n", vendor_id); + + ret = devm_regmap_field_bulk_alloc(dev, regmap, priv->rm_fields, + rtq6056_reg_fields, F_MAX_FIELDS); + if (ret) + return dev_err_probe(dev, ret, "Failed to init regmap field\n"); + + /* + * By default, configure average sample as 1, bus and shunt conversion + * time as 1037 microsecond, and operating mode to all on. + */ + ret = regmap_write(regmap, RTQ6056_REG_CONFIG, RTQ6056_DEFAULT_CONFIG); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable continuous sensing\n"); + + ret = devm_add_action_or_reset(dev, rtq6056_enter_shutdown_state, dev); + if (ret) + return ret; + + pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_active(dev); + pm_runtime_mark_last_busy(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable pm_runtime\n"); + + /* By default, use 2000 micro-Ohm resistor */ + shunt_resistor_uohm = 2000; + device_property_read_u32(dev, "shunt-resistor-micro-ohms", + &shunt_resistor_uohm); + + ret = rtq6056_set_shunt_resistor(priv, shunt_resistor_uohm); + if (ret) + return dev_err_probe(dev, ret, + "Failed to init shunt resistor\n"); + + indio_dev->name = "rtq6056"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = rtq6056_channels; + indio_dev->num_channels = ARRAY_SIZE(rtq6056_channels); + indio_dev->info = &rtq6056_info; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + rtq6056_buffer_trigger_handler, + NULL); + if (ret) + return dev_err_probe(dev, ret, + "Failed to allocate iio trigger buffer\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static int rtq6056_runtime_suspend(struct device *dev) +{ + struct rtq6056_priv *priv = dev_get_drvdata(dev); + + /* Configure to shutdown mode */ + return regmap_field_write(priv->rm_fields[F_OPMODE], 0); +} + +static int rtq6056_runtime_resume(struct device *dev) +{ + struct rtq6056_priv *priv = dev_get_drvdata(dev); + int sample_rdy_time_us, ret; + + ret = regmap_field_write(priv->rm_fields[F_OPMODE], RTQ6056_CONT_ALLON); + if (ret) + return ret; + + sample_rdy_time_us = priv->vbusct_us + priv->vshuntct_us; + sample_rdy_time_us *= priv->avg_sample; + + usleep_range(sample_rdy_time_us, sample_rdy_time_us + 100); + + return 0; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(rtq6056_pm_ops, rtq6056_runtime_suspend, + rtq6056_runtime_resume, NULL); + +static const struct of_device_id rtq6056_device_match[] = { + { .compatible = "richtek,rtq6056" }, + {} +}; +MODULE_DEVICE_TABLE(of, rtq6056_device_match); + +static struct i2c_driver rtq6056_driver = { + .driver = { + .name = "rtq6056", + .of_match_table = rtq6056_device_match, + .pm = pm_ptr(&rtq6056_pm_ops), + }, + .probe_new = rtq6056_probe, +}; +module_i2c_driver(rtq6056_driver); + +MODULE_AUTHOR("ChiYuan Huang "); +MODULE_DESCRIPTION("Richtek RTQ6056 Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 7898f31b0e7a6ba1ec9c7391e7ece9e31dd93262 Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Tue, 19 Jul 2022 22:52:45 +0800 Subject: Documentation: ABI: testing: rtq6056: Update ABI docs Add documentation for the usage of voltage channel integration time. Signed-off-by: ChiYuan Huang Link: https://lore.kernel.org/r/1658242365-27797-4-git-send-email-u0084500@gmail.com Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index e81ba6f5e1c8..7faec5b3a553 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -2038,3 +2038,14 @@ Description: Available range for the forced calibration value, expressed as: - a range specified as "[min step max]" + +What: /sys/bus/iio/devices/iio:deviceX/in_voltageX_sampling_frequency +What: /sys/bus/iio/devices/iio:deviceX/in_powerY_sampling_frequency +What: /sys/bus/iio/devices/iio:deviceX/in_currentZ_sampling_frequency +KernelVersion: 5.20 +Contact: linux-iio@vger.kernel.org +Description: + Some devices have separate controls of sampling frequency for + individual channels. If multiple channels are enabled in a scan, + then the sampling_frequency of the scan may be computed from the + per channel sampling frequencies. -- cgit v1.2.3 From 1bfb86d97a9fa607ab9bab4b28784cac7064420c Mon Sep 17 00:00:00 2001 From: Joe Simmons-Talbott Date: Sun, 31 Jul 2022 12:01:20 -0400 Subject: iio: Add names for function definition arguments. As reported by checkpatch.pl add missing names for function definition arguments. Signed-off-by: Joe Simmons-Talbott Link: https://lore.kernel.org/r/20220731160120.4831-1-joetalbott@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-event.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index b5e059e15b0a..0e2056894965 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -354,9 +354,10 @@ static int iio_device_add_event(struct iio_dev *indio_dev, enum iio_shared_by shared_by, const unsigned long *mask) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); - ssize_t (*show)(struct device *, struct device_attribute *, char *); - ssize_t (*store)(struct device *, struct device_attribute *, - const char *, size_t); + 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); unsigned int attrcount = 0; unsigned int i; char *postfix; -- cgit v1.2.3 From bb73d5d9164c57c4bb916739a98e5cd8e0a5ed8c Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 3 Aug 2022 13:28:37 +0300 Subject: iio: adc: at91-sama5d2_adc: fix AT91_SAMA5D2_MR_TRACKTIM_MAX All ADC HW versions handled by this driver (SAMA5D2, SAM9X60, SAMA7G5) have MR.TRACKTIM on 4 bits. Fix AT91_SAMA5D2_MR_TRACKTIM_MAX to reflect this. Fixes: 27e177190891 ("iio:adc:at91_adc8xx: introduce new atmel adc driver") Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20220803102855.2191070-2-claudiu.beznea@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 279430c1d88c..ac9ef89fba17 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -77,7 +77,7 @@ struct at91_adc_reg_layout { #define AT91_SAMA5D2_MR_ANACH BIT(23) /* Tracking Time */ #define AT91_SAMA5D2_MR_TRACKTIM(v) ((v) << 24) -#define AT91_SAMA5D2_MR_TRACKTIM_MAX 0xff +#define AT91_SAMA5D2_MR_TRACKTIM_MAX 0xf /* Transfer Time */ #define AT91_SAMA5D2_MR_TRANSFER(v) ((v) << 28) #define AT91_SAMA5D2_MR_TRANSFER_MAX 0x3 -- cgit v1.2.3 From d84ace944a3b24529798dbae1340dea098473155 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 3 Aug 2022 13:28:38 +0300 Subject: iio: adc: at91-sama5d2_adc: check return status for pressure and touch Check return status of at91_adc_read_position() and at91_adc_read_pressure() in at91_adc_read_info_raw(). Fixes: 6794e23fa3fe ("iio: adc: at91-sama5d2_adc: add support for oversampling resolution") Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20220803102855.2191070-3-claudiu.beznea@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index ac9ef89fba17..08d1f806c839 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -1544,8 +1544,10 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, *val = tmp_val; mutex_unlock(&st->lock); iio_device_release_direct_mode(indio_dev); + if (ret > 0) + ret = at91_adc_adjust_val_osr(st, val); - return at91_adc_adjust_val_osr(st, val); + return ret; } if (chan->type == IIO_PRESSURE) { ret = iio_device_claim_direct_mode(indio_dev); @@ -1558,8 +1560,10 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, *val = tmp_val; mutex_unlock(&st->lock); iio_device_release_direct_mode(indio_dev); + if (ret > 0) + ret = at91_adc_adjust_val_osr(st, val); - return at91_adc_adjust_val_osr(st, val); + return ret; } /* in this case we have a voltage channel */ -- cgit v1.2.3 From 9780a23ed5a0a0a63683e078f576719a98d4fb70 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 3 Aug 2022 13:28:39 +0300 Subject: iio: adc: at91-sama5d2_adc: lock around oversampling and sample freq .read_raw()/.write_raw() could be called asynchronously from user space or other in kernel drivers. Without locking on st->lock these could be called asynchronously while there is a conversion in progress. Read will be harmless but changing registers while conversion is in progress may lead to inconsistent results. Thus, to avoid this lock st->lock. Fixes: 27e177190891 ("iio:adc:at91_adc8xx: introduce new atmel adc driver") Fixes: 6794e23fa3fe ("iio: adc: at91-sama5d2_adc: add support for oversampling resolution") Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20220803102855.2191070-4-claudiu.beznea@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 08d1f806c839..3734ddc82952 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -1542,10 +1542,10 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, ret = at91_adc_read_position(st, chan->channel, &tmp_val); *val = tmp_val; - mutex_unlock(&st->lock); - iio_device_release_direct_mode(indio_dev); if (ret > 0) ret = at91_adc_adjust_val_osr(st, val); + mutex_unlock(&st->lock); + iio_device_release_direct_mode(indio_dev); return ret; } @@ -1558,10 +1558,10 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, ret = at91_adc_read_pressure(st, chan->channel, &tmp_val); *val = tmp_val; - mutex_unlock(&st->lock); - iio_device_release_direct_mode(indio_dev); if (ret > 0) ret = at91_adc_adjust_val_osr(st, val); + mutex_unlock(&st->lock); + iio_device_release_direct_mode(indio_dev); return ret; } @@ -1650,16 +1650,20 @@ static int at91_adc_write_raw(struct iio_dev *indio_dev, /* if no change, optimize out */ if (val == st->oversampling_ratio) return 0; + mutex_lock(&st->lock); st->oversampling_ratio = val; /* update ratio */ at91_adc_config_emr(st); + mutex_unlock(&st->lock); return 0; case IIO_CHAN_INFO_SAMP_FREQ: if (val < st->soc_info.min_sample_rate || val > st->soc_info.max_sample_rate) return -EINVAL; + mutex_lock(&st->lock); at91_adc_setup_samp_freq(indio_dev, val); + mutex_unlock(&st->lock); return 0; default: return -EINVAL; -- cgit v1.2.3 From 808175e21d9b7f866eda742e8970f27b78afe5db Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 3 Aug 2022 13:28:40 +0300 Subject: iio: adc: at91-sama5d2_adc: disable/prepare buffer on suspend/resume In case triggered buffers are enabled while system is suspended they will not work anymore after resume. For this call at91_adc_buffer_postdisable() on suspend and at91_adc_buffer_prepare() on resume. On tests it has been seen that at91_adc_buffer_postdisable() call is not necessary but it has been kept because it also does the book keeping for DMA. On resume path there is no need to call at91_adc_configure_touch() as it is embedded in at91_adc_buffer_prepare(). Fixes: 073c662017f2f ("iio: adc: at91-sama5d2_adc: add support for DMA") Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20220803102855.2191070-5-claudiu.beznea@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 3734ddc82952..e2c82c5a2fac 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -2116,6 +2116,9 @@ static int at91_adc_suspend(struct device *dev) struct iio_dev *indio_dev = dev_get_drvdata(dev); struct at91_adc_state *st = iio_priv(indio_dev); + if (iio_buffer_enabled(indio_dev)) + at91_adc_buffer_postdisable(indio_dev); + /* * Do a sofware reset of the ADC before we go to suspend. * this will ensure that all pins are free from being muxed by the ADC @@ -2159,14 +2162,11 @@ static int at91_adc_resume(struct device *dev) if (!iio_buffer_enabled(indio_dev)) return 0; - /* check if we are enabling triggered buffer or the touchscreen */ - if (at91_adc_current_chan_is_touch(indio_dev)) - return at91_adc_configure_touch(st, true); - else - return at91_adc_configure_trigger(st->trig, true); + ret = at91_adc_buffer_prepare(indio_dev); + if (ret) + goto vref_disable_resume; - /* not needed but more explicit */ - return 0; + return at91_adc_configure_trigger(st->trig, true); vref_disable_resume: regulator_disable(st->vref); -- cgit v1.2.3 From cf15a2b518b3e6991ef1c2f87e9a7f7e4afa7b77 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 3 Aug 2022 13:28:41 +0300 Subject: iio: adc: at91-sama5d2_adc: exit from write_raw() when buffers are enabled When buffers are enabled conversion may start asynchronously thus allowing changes on actual hardware could lead to bad behavior. Thus do not allow changing oversampling ratio and sample frequency when if iio_device_claim_direct_mode() returns with error. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20220803102855.2191070-6-claudiu.beznea@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index e2c82c5a2fac..64943d8ea869 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -1641,6 +1641,7 @@ static int at91_adc_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct at91_adc_state *st = iio_priv(indio_dev); + int ret; switch (mask) { case IIO_CHAN_INFO_OVERSAMPLING_RATIO: @@ -1650,20 +1651,29 @@ static int at91_adc_write_raw(struct iio_dev *indio_dev, /* if no change, optimize out */ if (val == st->oversampling_ratio) return 0; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; mutex_lock(&st->lock); st->oversampling_ratio = val; /* update ratio */ at91_adc_config_emr(st); mutex_unlock(&st->lock); + iio_device_release_direct_mode(indio_dev); return 0; case IIO_CHAN_INFO_SAMP_FREQ: if (val < st->soc_info.min_sample_rate || val > st->soc_info.max_sample_rate) return -EINVAL; + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; mutex_lock(&st->lock); at91_adc_setup_samp_freq(indio_dev, val); mutex_unlock(&st->lock); + iio_device_release_direct_mode(indio_dev); return 0; default: return -EINVAL; -- cgit v1.2.3 From 287c271dee146b7ab38f29d055691b8bc3753f35 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 3 Aug 2022 13:28:42 +0300 Subject: iio: adc: at91-sama5d2_adc: handle different EMR.OSR for different hw versions SAMA7G5 introduces 64 and 256 oversampling rates. Due to this EMR.OSR is 3 bits long. Change the code to reflect this. Commit prepares the code for the addition of 64 and 256 oversampling rates. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20220803102855.2191070-7-claudiu.beznea@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 64943d8ea869..0283c8cc3168 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -138,8 +138,7 @@ struct at91_adc_reg_layout { /* Extended Mode Register */ u16 EMR; /* Extended Mode Register - Oversampling rate */ -#define AT91_SAMA5D2_EMR_OSR(V) ((V) << 16) -#define AT91_SAMA5D2_EMR_OSR_MASK GENMASK(17, 16) +#define AT91_SAMA5D2_EMR_OSR(V, M) (((V) << 16) & (M)) #define AT91_SAMA5D2_EMR_OSR_1SAMPLES 0 #define AT91_SAMA5D2_EMR_OSR_4SAMPLES 1 #define AT91_SAMA5D2_EMR_OSR_16SAMPLES 2 @@ -403,6 +402,7 @@ static const struct at91_adc_reg_layout sama7g5_layout = { * @max_index: highest channel index (highest index may be higher * than the total channel number) * @hw_trig_cnt: number of possible hardware triggers + * @osr_mask: oversampling ratio bitmask on EMR register */ struct at91_adc_platform { const struct at91_adc_reg_layout *layout; @@ -414,6 +414,7 @@ struct at91_adc_platform { unsigned int max_channels; unsigned int max_index; unsigned int hw_trig_cnt; + unsigned int osr_mask; }; /** @@ -612,6 +613,7 @@ static const struct at91_adc_platform sama5d2_platform = { .max_index = AT91_SAMA5D2_MAX_CHAN_IDX, #define AT91_SAMA5D2_HW_TRIG_CNT 3 .hw_trig_cnt = AT91_SAMA5D2_HW_TRIG_CNT, + .osr_mask = GENMASK(17, 16), }; static const struct at91_adc_platform sama7g5_platform = { @@ -627,6 +629,7 @@ static const struct at91_adc_platform sama7g5_platform = { .max_index = AT91_SAMA7G5_MAX_CHAN_IDX, #define AT91_SAMA7G5_HW_TRIG_CNT 3 .hw_trig_cnt = AT91_SAMA7G5_HW_TRIG_CNT, + .osr_mask = GENMASK(18, 16), }; static int at91_adc_chan_xlate(struct iio_dev *indio_dev, int chan) @@ -725,30 +728,32 @@ static void at91_adc_eoc_ena(struct at91_adc_state *st, unsigned int channel) at91_adc_writel(st, EOC_IER, BIT(channel)); } -static void at91_adc_config_emr(struct at91_adc_state *st) +static void at91_adc_config_emr(struct at91_adc_state *st, + u32 oversampling_ratio) { /* configure the extended mode register */ unsigned int emr = at91_adc_readl(st, EMR); + unsigned int osr_mask = st->soc_info.platform->osr_mask; /* select oversampling per single trigger event */ emr |= AT91_SAMA5D2_EMR_ASTE(1); /* delete leftover content if it's the case */ - emr &= ~AT91_SAMA5D2_EMR_OSR_MASK; + emr &= ~osr_mask; /* select oversampling ratio from configuration */ - switch (st->oversampling_ratio) { + switch (oversampling_ratio) { case AT91_OSR_1SAMPLES: - emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_1SAMPLES) & - AT91_SAMA5D2_EMR_OSR_MASK; + emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_1SAMPLES, + osr_mask); break; case AT91_OSR_4SAMPLES: - emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_4SAMPLES) & - AT91_SAMA5D2_EMR_OSR_MASK; + emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_4SAMPLES, + osr_mask); break; case AT91_OSR_16SAMPLES: - emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_16SAMPLES) & - AT91_SAMA5D2_EMR_OSR_MASK; + emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_16SAMPLES, + osr_mask); break; } @@ -1658,7 +1663,7 @@ static int at91_adc_write_raw(struct iio_dev *indio_dev, mutex_lock(&st->lock); st->oversampling_ratio = val; /* update ratio */ - at91_adc_config_emr(st); + at91_adc_config_emr(st, val); mutex_unlock(&st->lock); iio_device_release_direct_mode(indio_dev); return 0; @@ -1838,7 +1843,7 @@ static void at91_adc_hw_init(struct iio_dev *indio_dev) at91_adc_setup_samp_freq(indio_dev, st->soc_info.min_sample_rate); /* configure extended mode register */ - at91_adc_config_emr(st); + at91_adc_config_emr(st, st->oversampling_ratio); } static ssize_t at91_adc_get_fifo_state(struct device *dev, -- cgit v1.2.3 From 502966c3b0267aee88803eda639d95fb98db433b Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 3 Aug 2022 13:28:43 +0300 Subject: iio: adc: at91-sama5d2_adc: move the check of oversampling in its function Oversampling values are checked anyway in at91_adc_emr_config(). Remove the checking of these from at91_adc_write_raw() and return -EINVAL instead in at91_adc_emr_config(). Suggested-by: Jonathan Cameron Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20220803102855.2191070-8-claudiu.beznea@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 0283c8cc3168..ace4cc431a95 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -728,8 +728,8 @@ static void at91_adc_eoc_ena(struct at91_adc_state *st, unsigned int channel) at91_adc_writel(st, EOC_IER, BIT(channel)); } -static void at91_adc_config_emr(struct at91_adc_state *st, - u32 oversampling_ratio) +static int at91_adc_config_emr(struct at91_adc_state *st, + u32 oversampling_ratio) { /* configure the extended mode register */ unsigned int emr = at91_adc_readl(st, EMR); @@ -755,9 +755,13 @@ static void at91_adc_config_emr(struct at91_adc_state *st, emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_16SAMPLES, osr_mask); break; + default: + return -EINVAL; } at91_adc_writel(st, EMR, emr); + + return 0; } static int at91_adc_adjust_val_osr(struct at91_adc_state *st, int *val) @@ -1650,9 +1654,6 @@ static int at91_adc_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_OVERSAMPLING_RATIO: - if ((val != AT91_OSR_1SAMPLES) && (val != AT91_OSR_4SAMPLES) && - (val != AT91_OSR_16SAMPLES)) - return -EINVAL; /* if no change, optimize out */ if (val == st->oversampling_ratio) return 0; @@ -1661,12 +1662,13 @@ static int at91_adc_write_raw(struct iio_dev *indio_dev, if (ret) return ret; mutex_lock(&st->lock); - st->oversampling_ratio = val; /* update ratio */ - at91_adc_config_emr(st, val); + ret = at91_adc_config_emr(st, val); + if (!ret) + st->oversampling_ratio = val; mutex_unlock(&st->lock); iio_device_release_direct_mode(indio_dev); - return 0; + return ret; case IIO_CHAN_INFO_SAMP_FREQ: if (val < st->soc_info.min_sample_rate || val > st->soc_info.max_sample_rate) -- cgit v1.2.3 From eea2655e10a05b0836c09a8ac7c27edc79d1d661 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 3 Aug 2022 13:28:44 +0300 Subject: iio: adc: at91-sama5d2_adc: drop AT91_OSR_XSAMPLES defines Drop AT91_OSR_1SAMPLES, AT91_OSR_4SAMPLES, AT91_OSR_16SAMPLES defines and insted use their values inline. Suggested-by: Jonathan Cameron Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20220803102855.2191070-9-claudiu.beznea@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index ace4cc431a95..fe4bec03bea9 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -304,11 +304,6 @@ static const struct at91_adc_reg_layout sama7g5_layout = { #define AT91_HWFIFO_MAX_SIZE_STR "128" #define AT91_HWFIFO_MAX_SIZE 128 -/* Possible values for oversampling ratio */ -#define AT91_OSR_1SAMPLES 1 -#define AT91_OSR_4SAMPLES 4 -#define AT91_OSR_16SAMPLES 16 - #define AT91_SAMA5D2_CHAN_SINGLE(index, num, addr) \ { \ .type = IIO_VOLTAGE, \ @@ -743,15 +738,15 @@ static int at91_adc_config_emr(struct at91_adc_state *st, /* select oversampling ratio from configuration */ switch (oversampling_ratio) { - case AT91_OSR_1SAMPLES: + case 1: emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_1SAMPLES, osr_mask); break; - case AT91_OSR_4SAMPLES: + case 4: emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_4SAMPLES, osr_mask); break; - case AT91_OSR_16SAMPLES: + case 16: emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_16SAMPLES, osr_mask); break; @@ -766,13 +761,13 @@ static int at91_adc_config_emr(struct at91_adc_state *st, static int at91_adc_adjust_val_osr(struct at91_adc_state *st, int *val) { - if (st->oversampling_ratio == AT91_OSR_1SAMPLES) { + if (st->oversampling_ratio == 1) { /* * in this case we only have 12 bits of real data, but channel * is registered as 14 bits, so shift left two bits */ *val <<= 2; - } else if (st->oversampling_ratio == AT91_OSR_4SAMPLES) { + } else if (st->oversampling_ratio == 4) { /* * in this case we have 13 bits of real data, but channel * is registered as 14 bits, so left shift one bit @@ -1875,9 +1870,9 @@ static IIO_CONST_ATTR(hwfifo_watermark_min, "2"); static IIO_CONST_ATTR(hwfifo_watermark_max, AT91_HWFIFO_MAX_SIZE_STR); static IIO_CONST_ATTR(oversampling_ratio_available, - __stringify(AT91_OSR_1SAMPLES) " " - __stringify(AT91_OSR_4SAMPLES) " " - __stringify(AT91_OSR_16SAMPLES)); + __stringify(1) " " + __stringify(4) " " + __stringify(16)); static struct attribute *at91_adc_attributes[] = { &iio_const_attr_oversampling_ratio_available.dev_attr.attr, @@ -1973,7 +1968,7 @@ static int at91_adc_probe(struct platform_device *pdev) bitmap_set(&st->touch_st.channels_bitmask, st->soc_info.platform->touch_chan_p, 1); - st->oversampling_ratio = AT91_OSR_1SAMPLES; + st->oversampling_ratio = 1; ret = of_property_read_u32(pdev->dev.of_node, "atmel,min-sample-rate-hz", -- cgit v1.2.3 From 3c5d62a1e407eb2c941d094bfc0aa9ee85998f88 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 3 Aug 2022 13:28:45 +0300 Subject: iio: adc: at91-sama5d2_adc: add .read_avail() chan_info ops Add .read_avail() to chan_info ops which will retrieve the available oversampling ratio. Suggested-by: Jonathan Cameron Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20220803102855.2191070-10-claudiu.beznea@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 50 ++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index fe4bec03bea9..47caaf271fae 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -319,6 +319,8 @@ static const struct at91_adc_reg_layout sama7g5_layout = { .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ)|\ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .datasheet_name = "CH"#num, \ .indexed = 1, \ } @@ -340,6 +342,8 @@ static const struct at91_adc_reg_layout sama7g5_layout = { .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ)|\ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .datasheet_name = "CH"#num"-CH"#num2, \ .indexed = 1, \ } @@ -359,6 +363,8 @@ static const struct at91_adc_reg_layout sama7g5_layout = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ)|\ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .datasheet_name = name, \ } #define AT91_SAMA5D2_CHAN_PRESSURE(num, name) \ @@ -374,6 +380,8 @@ static const struct at91_adc_reg_layout sama7g5_layout = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ)|\ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .datasheet_name = name, \ } @@ -398,6 +406,8 @@ static const struct at91_adc_reg_layout sama7g5_layout = { * than the total channel number) * @hw_trig_cnt: number of possible hardware triggers * @osr_mask: oversampling ratio bitmask on EMR register + * @oversampling_avail: available oversampling values + * @oversampling_avail_no: number of available oversampling values */ struct at91_adc_platform { const struct at91_adc_reg_layout *layout; @@ -410,6 +420,8 @@ struct at91_adc_platform { unsigned int max_index; unsigned int hw_trig_cnt; unsigned int osr_mask; + unsigned int oversampling_avail[3]; + unsigned int oversampling_avail_no; }; /** @@ -609,6 +621,8 @@ static const struct at91_adc_platform sama5d2_platform = { #define AT91_SAMA5D2_HW_TRIG_CNT 3 .hw_trig_cnt = AT91_SAMA5D2_HW_TRIG_CNT, .osr_mask = GENMASK(17, 16), + .oversampling_avail = { 1, 4, 16, }, + .oversampling_avail_no = 3, }; static const struct at91_adc_platform sama7g5_platform = { @@ -625,6 +639,8 @@ static const struct at91_adc_platform sama7g5_platform = { #define AT91_SAMA7G5_HW_TRIG_CNT 3 .hw_trig_cnt = AT91_SAMA7G5_HW_TRIG_CNT, .osr_mask = GENMASK(18, 16), + .oversampling_avail = { 1, 4, 16, }, + .oversampling_avail_no = 3, }; static int at91_adc_chan_xlate(struct iio_dev *indio_dev, int chan) @@ -1682,6 +1698,24 @@ static int at91_adc_write_raw(struct iio_dev *indio_dev, } } +static int at91_adc_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = (int *)st->soc_info.platform->oversampling_avail; + *type = IIO_VAL_INT; + *length = st->soc_info.platform->oversampling_avail_no; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + static void at91_adc_dma_init(struct at91_adc_state *st) { struct device *dev = &st->indio_dev->dev; @@ -1869,20 +1903,6 @@ static IIO_DEVICE_ATTR(hwfifo_watermark, 0444, static IIO_CONST_ATTR(hwfifo_watermark_min, "2"); static IIO_CONST_ATTR(hwfifo_watermark_max, AT91_HWFIFO_MAX_SIZE_STR); -static IIO_CONST_ATTR(oversampling_ratio_available, - __stringify(1) " " - __stringify(4) " " - __stringify(16)); - -static struct attribute *at91_adc_attributes[] = { - &iio_const_attr_oversampling_ratio_available.dev_attr.attr, - NULL, -}; - -static const struct attribute_group at91_adc_attribute_group = { - .attrs = at91_adc_attributes, -}; - static const struct attribute *at91_adc_fifo_attributes[] = { &iio_const_attr_hwfifo_watermark_min.dev_attr.attr, &iio_const_attr_hwfifo_watermark_max.dev_attr.attr, @@ -1892,7 +1912,7 @@ static const struct attribute *at91_adc_fifo_attributes[] = { }; static const struct iio_info at91_adc_info = { - .attrs = &at91_adc_attribute_group, + .read_avail = &at91_adc_read_avail, .read_raw = &at91_adc_read_raw, .write_raw = &at91_adc_write_raw, .update_scan_mode = &at91_adc_update_scan_mode, -- cgit v1.2.3 From 00ee4add809fa6d2a4508004e3933404de288194 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 3 Aug 2022 13:28:46 +0300 Subject: iio: adc: at91-sama5d2_adc: adjust osr based on specific platform data ADC captures data on 12 bits (if oversampling is not enabled). When using oversampling captured data could go up to 14 bits for SAMA5D2 or up to 16 bits for SAMA7G5 (depending on oversampling settings). All the channels that are subject of oversampling are registered as 14 or 16 real bits. Depending on the oversampling settings the ADC converted value need to be shifted up to 14 or 16 to cope with realbits value registered to IIO subsystem. Commit adds platform specific information to know if we run on a system with up to 14 or 16 bits ADC converted data. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20220803102855.2191070-11-claudiu.beznea@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 47caaf271fae..db9d77385149 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -408,6 +408,7 @@ static const struct at91_adc_reg_layout sama7g5_layout = { * @osr_mask: oversampling ratio bitmask on EMR register * @oversampling_avail: available oversampling values * @oversampling_avail_no: number of available oversampling values + * @chan_realbits: realbits for registered channels */ struct at91_adc_platform { const struct at91_adc_reg_layout *layout; @@ -422,6 +423,7 @@ struct at91_adc_platform { unsigned int osr_mask; unsigned int oversampling_avail[3]; unsigned int oversampling_avail_no; + unsigned int chan_realbits; }; /** @@ -623,6 +625,7 @@ static const struct at91_adc_platform sama5d2_platform = { .osr_mask = GENMASK(17, 16), .oversampling_avail = { 1, 4, 16, }, .oversampling_avail_no = 3, + .chan_realbits = 14, }; static const struct at91_adc_platform sama7g5_platform = { @@ -641,6 +644,7 @@ static const struct at91_adc_platform sama7g5_platform = { .osr_mask = GENMASK(18, 16), .oversampling_avail = { 1, 4, 16, }, .oversampling_avail_no = 3, + .chan_realbits = 16, }; static int at91_adc_chan_xlate(struct iio_dev *indio_dev, int chan) @@ -777,19 +781,24 @@ static int at91_adc_config_emr(struct at91_adc_state *st, static int at91_adc_adjust_val_osr(struct at91_adc_state *st, int *val) { - if (st->oversampling_ratio == 1) { - /* - * in this case we only have 12 bits of real data, but channel - * is registered as 14 bits, so shift left two bits - */ - *val <<= 2; - } else if (st->oversampling_ratio == 4) { - /* - * in this case we have 13 bits of real data, but channel - * is registered as 14 bits, so left shift one bit - */ - *val <<= 1; - } + int nbits, diff; + + if (st->oversampling_ratio == 1) + nbits = 12; + else if (st->oversampling_ratio == 4) + nbits = 13; + else if (st->oversampling_ratio == 16) + nbits = 14; + else + /* Should not happen. */ + return -EINVAL; + + /* + * We have nbits of real data and channel is registered as + * st->soc_info.platform->chan_realbits, so shift left diff bits. + */ + diff = st->soc_info.platform->chan_realbits - nbits; + *val <<= diff; return IIO_VAL_INT; } -- cgit v1.2.3 From 5fc30713acf731f562dfdbe0795dadb71abd9183 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 3 Aug 2022 13:28:47 +0300 Subject: iio: adc: at91-sama5d2_adc: add 64 and 256 oversampling ratio Add 64 and 256 oversampling ratio support. It is necessary for temperature sensor. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20220803102855.2191070-12-claudiu.beznea@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index db9d77385149..d6a93aa7fbaf 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -142,6 +142,8 @@ struct at91_adc_reg_layout { #define AT91_SAMA5D2_EMR_OSR_1SAMPLES 0 #define AT91_SAMA5D2_EMR_OSR_4SAMPLES 1 #define AT91_SAMA5D2_EMR_OSR_16SAMPLES 2 +#define AT91_SAMA5D2_EMR_OSR_64SAMPLES 3 +#define AT91_SAMA5D2_EMR_OSR_256SAMPLES 4 /* Extended Mode Register - Averaging on single trigger event */ #define AT91_SAMA5D2_EMR_ASTE(V) ((V) << 20) @@ -421,7 +423,7 @@ struct at91_adc_platform { unsigned int max_index; unsigned int hw_trig_cnt; unsigned int osr_mask; - unsigned int oversampling_avail[3]; + unsigned int oversampling_avail[5]; unsigned int oversampling_avail_no; unsigned int chan_realbits; }; @@ -642,8 +644,8 @@ static const struct at91_adc_platform sama7g5_platform = { #define AT91_SAMA7G5_HW_TRIG_CNT 3 .hw_trig_cnt = AT91_SAMA7G5_HW_TRIG_CNT, .osr_mask = GENMASK(18, 16), - .oversampling_avail = { 1, 4, 16, }, - .oversampling_avail_no = 3, + .oversampling_avail = { 1, 4, 16, 64, 256, }, + .oversampling_avail_no = 5, .chan_realbits = 16, }; @@ -749,6 +751,15 @@ static int at91_adc_config_emr(struct at91_adc_state *st, /* configure the extended mode register */ unsigned int emr = at91_adc_readl(st, EMR); unsigned int osr_mask = st->soc_info.platform->osr_mask; + int i; + + /* Check against supported oversampling values. */ + for (i = 0; i < st->soc_info.platform->oversampling_avail_no; i++) { + if (oversampling_ratio == st->soc_info.platform->oversampling_avail[i]) + break; + } + if (i == st->soc_info.platform->oversampling_avail_no) + return -EINVAL; /* select oversampling per single trigger event */ emr |= AT91_SAMA5D2_EMR_ASTE(1); @@ -770,8 +781,14 @@ static int at91_adc_config_emr(struct at91_adc_state *st, emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_16SAMPLES, osr_mask); break; - default: - return -EINVAL; + case 64: + emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_64SAMPLES, + osr_mask); + break; + case 256: + emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_256SAMPLES, + osr_mask); + break; } at91_adc_writel(st, EMR, emr); @@ -789,6 +806,10 @@ static int at91_adc_adjust_val_osr(struct at91_adc_state *st, int *val) nbits = 13; else if (st->oversampling_ratio == 16) nbits = 14; + else if (st->oversampling_ratio == 64) + nbits = 15; + else if (st->oversampling_ratio == 256) + nbits = 16; else /* Should not happen. */ return -EINVAL; -- cgit v1.2.3 From 426b64752c4cc2059d5219d81071bf57608f346f Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 3 Aug 2022 13:28:48 +0300 Subject: iio: adc: at91-sama5d2_adc: move oversampling storage in its function Move the storage of oversampling_ratio in at91_adc_config_emr(). This prepares for the next commits. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20220803102855.2191070-13-claudiu.beznea@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 d6a93aa7fbaf..a1df475a6f29 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -793,6 +793,8 @@ static int at91_adc_config_emr(struct at91_adc_state *st, at91_adc_writel(st, EMR, emr); + st->oversampling_ratio = oversampling_ratio; + return 0; } @@ -1705,8 +1707,6 @@ static int at91_adc_write_raw(struct iio_dev *indio_dev, mutex_lock(&st->lock); /* update ratio */ ret = at91_adc_config_emr(st, val); - if (!ret) - st->oversampling_ratio = val; mutex_unlock(&st->lock); iio_device_release_direct_mode(indio_dev); return ret; -- cgit v1.2.3 From 04227f9510799b8f15fc9dcc30e12ed503bb3183 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 3 Aug 2022 13:28:49 +0300 Subject: iio: adc: at91-sama5d2_adc: update trackx on emr Add support for updating trackx bits of EMR register. Having different values of EMR.TRACKX when measuring temperature give a better accuracy. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20220803102855.2191070-14-claudiu.beznea@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index a1df475a6f29..0209353cbfd7 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -145,6 +145,10 @@ struct at91_adc_reg_layout { #define AT91_SAMA5D2_EMR_OSR_64SAMPLES 3 #define AT91_SAMA5D2_EMR_OSR_256SAMPLES 4 +/* Extended Mode Register - TRACKX */ +#define AT91_SAMA5D2_TRACKX_MASK GENMASK(23, 22) +#define AT91_SAMA5D2_TRACKX(x) (((x) << 22) & \ + AT91_SAMA5D2_TRACKX_MASK) /* Extended Mode Register - Averaging on single trigger event */ #define AT91_SAMA5D2_EMR_ASTE(V) ((V) << 20) @@ -746,7 +750,7 @@ static void at91_adc_eoc_ena(struct at91_adc_state *st, unsigned int channel) } static int at91_adc_config_emr(struct at91_adc_state *st, - u32 oversampling_ratio) + u32 oversampling_ratio, u32 trackx) { /* configure the extended mode register */ unsigned int emr = at91_adc_readl(st, EMR); @@ -765,7 +769,7 @@ static int at91_adc_config_emr(struct at91_adc_state *st, emr |= AT91_SAMA5D2_EMR_ASTE(1); /* delete leftover content if it's the case */ - emr &= ~osr_mask; + emr &= ~(osr_mask | AT91_SAMA5D2_TRACKX_MASK); /* select oversampling ratio from configuration */ switch (oversampling_ratio) { @@ -791,6 +795,8 @@ static int at91_adc_config_emr(struct at91_adc_state *st, break; } + /* Update trackx. */ + emr |= AT91_SAMA5D2_TRACKX(trackx); at91_adc_writel(st, EMR, emr); st->oversampling_ratio = oversampling_ratio; @@ -1706,7 +1712,7 @@ static int at91_adc_write_raw(struct iio_dev *indio_dev, return ret; mutex_lock(&st->lock); /* update ratio */ - ret = at91_adc_config_emr(st, val); + ret = at91_adc_config_emr(st, val, 0); mutex_unlock(&st->lock); iio_device_release_direct_mode(indio_dev); return ret; @@ -1904,7 +1910,7 @@ static void at91_adc_hw_init(struct iio_dev *indio_dev) at91_adc_setup_samp_freq(indio_dev, st->soc_info.min_sample_rate); /* configure extended mode register */ - at91_adc_config_emr(st, st->oversampling_ratio); + at91_adc_config_emr(st, st->oversampling_ratio, 0); } static ssize_t at91_adc_get_fifo_state(struct device *dev, -- cgit v1.2.3 From 5f72666f4b1ac269fe5b3b3f0cfaa6ada6add57c Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 3 Aug 2022 13:28:50 +0300 Subject: iio: adc: at91-sama5d2_adc: add startup and tracktim as parameter for at91_adc_setup_samp_freq() Add startup and tracktim as parameter for at91_adc_setup_samp_freq() function. In case of temperature sensor being enabled these parameters will be configured on temperature read request to improve the accuracy of the read temperature. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20220803102855.2191070-15-claudiu.beznea@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 0209353cbfd7..fd02da9b26b2 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -1435,7 +1435,9 @@ static unsigned at91_adc_startup_time(unsigned startup_time_min, return i; } -static void at91_adc_setup_samp_freq(struct iio_dev *indio_dev, unsigned freq) +static void at91_adc_setup_samp_freq(struct iio_dev *indio_dev, unsigned freq, + unsigned int startup_time, + unsigned int tracktim) { struct at91_adc_state *st = iio_priv(indio_dev); unsigned f_per, prescal, startup, mr; @@ -1443,17 +1445,17 @@ static void at91_adc_setup_samp_freq(struct iio_dev *indio_dev, unsigned freq) f_per = clk_get_rate(st->per_clk); prescal = (f_per / (2 * freq)) - 1; - startup = at91_adc_startup_time(st->soc_info.startup_time, - freq / 1000); + startup = at91_adc_startup_time(startup_time, freq / 1000); 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); + mr |= AT91_SAMA5D2_MR_TRACKTIM(tracktim); at91_adc_writel(st, MR, mr); - dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u\n", - freq, startup, prescal); + dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u, tracktim=%u\n", + freq, startup, prescal, tracktim); st->current_sample_rate = freq; } @@ -1725,7 +1727,8 @@ static int at91_adc_write_raw(struct iio_dev *indio_dev, if (ret) return ret; mutex_lock(&st->lock); - at91_adc_setup_samp_freq(indio_dev, val); + at91_adc_setup_samp_freq(indio_dev, val, + st->soc_info.startup_time, 0); mutex_unlock(&st->lock); iio_device_release_direct_mode(indio_dev); return 0; @@ -1907,7 +1910,8 @@ static void at91_adc_hw_init(struct iio_dev *indio_dev) 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); + at91_adc_setup_samp_freq(indio_dev, st->soc_info.min_sample_rate, + st->soc_info.startup_time, 0); /* configure extended mode register */ at91_adc_config_emr(st, st->oversampling_ratio, 0); -- cgit v1.2.3 From a0f96db4ca1279d2c5150ed306e32a589f66070b Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 3 Aug 2022 13:28:51 +0300 Subject: iio: adc: at91-sama5d2_adc: lock around at91_adc_read_info_raw() Remove iio_device_{claim, release}_direct_mode() and lock/unlock to &st->lock from at91_adc_read_info_raw(). Instead add a wrapper around at91_adc_read_info_raw() and do there the lock/unlock. This will allow using the at91_adc_read_info_raw() in patch that add support for temperature sensor. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20220803102855.2191070-16-claudiu.beznea@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 39 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index fd02da9b26b2..1a72e304fc3a 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -1582,6 +1582,7 @@ static irqreturn_t at91_adc_interrupt(int irq, void *private) return IRQ_HANDLED; } +/* This needs to be called with direct mode claimed and st->lock locked. */ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) { @@ -1594,45 +1595,26 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, * if external trigger is enabled */ if (chan->type == IIO_POSITIONRELATIVE) { - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - mutex_lock(&st->lock); - ret = at91_adc_read_position(st, chan->channel, &tmp_val); *val = tmp_val; if (ret > 0) ret = at91_adc_adjust_val_osr(st, val); - mutex_unlock(&st->lock); - iio_device_release_direct_mode(indio_dev); return ret; } if (chan->type == IIO_PRESSURE) { - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - mutex_lock(&st->lock); - ret = at91_adc_read_pressure(st, chan->channel, &tmp_val); *val = tmp_val; if (ret > 0) ret = at91_adc_adjust_val_osr(st, val); - mutex_unlock(&st->lock); - iio_device_release_direct_mode(indio_dev); return ret; } /* in this case we have a voltage channel */ - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - mutex_lock(&st->lock); - st->chan = chan; at91_adc_cor(st, chan); @@ -1661,9 +1643,25 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, /* Needed to ACK the DRDY interruption */ at91_adc_readl(st, LCDR); + return ret; +} + +static int at91_adc_read_info_locked(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + int ret; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + mutex_lock(&st->lock); + ret = at91_adc_read_info_raw(indio_dev, chan, val); mutex_unlock(&st->lock); iio_device_release_direct_mode(indio_dev); + return ret; } @@ -1675,7 +1673,8 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - return at91_adc_read_info_raw(indio_dev, chan, val); + return at91_adc_read_info_locked(indio_dev, chan, val); + case IIO_CHAN_INFO_SCALE: *val = st->vref_uv / 1000; if (chan->differential) -- cgit v1.2.3 From cb6e097d9340e037ced6c580019e823b702c6bb9 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 3 Aug 2022 13:28:52 +0300 Subject: dt-bindings: iio: adc: at91-sama5d2_adc: add id for temperature channel Add ID for temperature channel of AT91 SAMA5D2 ADC. Signed-off-by: Claudiu Beznea Acked-by: Rob Herring Link: https://lore.kernel.org/r/20220803102855.2191070-17-claudiu.beznea@microchip.com Signed-off-by: Jonathan Cameron --- include/dt-bindings/iio/adc/at91-sama5d2_adc.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/dt-bindings/iio/adc/at91-sama5d2_adc.h b/include/dt-bindings/iio/adc/at91-sama5d2_adc.h index 70f99dbdbb42..866d36530583 100644 --- a/include/dt-bindings/iio/adc/at91-sama5d2_adc.h +++ b/include/dt-bindings/iio/adc/at91-sama5d2_adc.h @@ -13,4 +13,7 @@ /* pressure channel index */ #define AT91_SAMA5D2_ADC_P_CHANNEL 26 +/* SAMA7G5 Temperature sensor channel index. */ +#define AT91_SAMA7G5_ADC_TEMP_CHANNEL 31 + #endif -- cgit v1.2.3 From 5ab38b81895c869fb72eab5b528d5ef13a741c66 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 3 Aug 2022 13:28:53 +0300 Subject: iio: adc: at91-sama5d2_adc: add support for temperature sensor The ADC on SAMA7G5 has a dedicated channel (channel 31) for measuring in-SoC temperature. 2 inputs are multiplexed on channel 31, VTEMP and VBG as follows: ` | \ +-----+ VBG --->| | ch31 | | Vtemp --->| |----->| ADC | | / | | | / +-----+ . where: - VTEMP is proportional to the absolute temperature voltage - VBG is a quasi-temperature independent voltage Both VBG and VTEMP are needed to determine the correct in-SoC temperature. At a moment of time only one of these could be measured, the selection being done with bit SRCLCH bit of ACR register. The formula to calculate the temperature is as follows: P1 + (Vref * (VTEMP - P6 - P4 * VBG)) / (VBG * VTEMP_DT) where: - P1, P4, P6 are calibration data retrieved from OTP memory - Vref is the reference voltage for ADC - VTEMP_DT is the voltage sensitivity to temperature and is constant - VTEMP, VBG are the measured values from channel 31 For better resolution before reading the temperature certain settings for oversampling ratio, sample frequency, EMR.TRACKX, MR.TRACKTIM are applied. The initial settings are reapplied at the end of temperature reading. Current support is not integrated with trigger buffers channel 31 not being enabled/disabled in functions at91_adc_buffer_prepare(), at91_adc_buffer_postdisable() thus the conversion for channel 31 is not done in case trigger buffers are enabled. In case of trigger buffers are enabled and temperature requests are received in the driver though at91_adc_read_temp() the at91_adc_read_temp() will return with an error code. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20220803102855.2191070-18-claudiu.beznea@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 256 ++++++++++++++++++++++++++++++++++++- 1 file changed, 249 insertions(+), 7 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 1a72e304fc3a..0a7b1680f158 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -26,9 +27,12 @@ #include #include #include +#include #include #include +#include + struct at91_adc_reg_layout { /* Control Register */ u16 CR; @@ -73,10 +77,13 @@ struct at91_adc_reg_layout { /* Startup Time */ #define AT91_SAMA5D2_MR_STARTUP(v) ((v) << 16) #define AT91_SAMA5D2_MR_STARTUP_MASK GENMASK(19, 16) +/* Minimum startup time for temperature sensor */ +#define AT91_SAMA5D2_MR_STARTUP_TS_MIN (50) /* Analog Change */ #define AT91_SAMA5D2_MR_ANACH BIT(23) /* Tracking Time */ #define AT91_SAMA5D2_MR_TRACKTIM(v) ((v) << 24) +#define AT91_SAMA5D2_MR_TRACKTIM_TS 6 #define AT91_SAMA5D2_MR_TRACKTIM_MAX 0xf /* Transfer Time */ #define AT91_SAMA5D2_MR_TRANSFER(v) ((v) << 28) @@ -149,6 +156,9 @@ struct at91_adc_reg_layout { #define AT91_SAMA5D2_TRACKX_MASK GENMASK(23, 22) #define AT91_SAMA5D2_TRACKX(x) (((x) << 22) & \ AT91_SAMA5D2_TRACKX_MASK) +/* TRACKX for temperature sensor. */ +#define AT91_SAMA5D2_TRACKX_TS (1) + /* Extended Mode Register - Averaging on single trigger event */ #define AT91_SAMA5D2_EMR_ASTE(V) ((V) << 20) @@ -164,6 +174,8 @@ struct at91_adc_reg_layout { u16 ACR; /* Analog Control Register - Pen detect sensitivity mask */ #define AT91_SAMA5D2_ACR_PENDETSENS_MASK GENMASK(1, 0) +/* Analog Control Register - Source last channel */ +#define AT91_SAMA5D2_ACR_SRCLCH BIT(16) /* Touchscreen Mode Register */ u16 TSMR; @@ -231,6 +243,10 @@ struct at91_adc_reg_layout { u16 WPSR; /* Version Register */ u16 VERSION; +/* Temperature Sensor Mode Register */ + u16 TEMPMR; +/* Temperature Sensor Mode - Temperature sensor on */ +#define AT91_SAMA5D2_TEMPMR_TEMPON BIT(0) }; static const struct at91_adc_reg_layout sama5d2_layout = { @@ -285,6 +301,7 @@ static const struct at91_adc_reg_layout sama7g5_layout = { .EOC_IDR = 0x38, .EOC_IMR = 0x3c, .EOC_ISR = 0x40, + .TEMPMR = 0x44, .OVER = 0x4c, .EMR = 0x50, .CWR = 0x54, @@ -391,6 +408,21 @@ static const struct at91_adc_reg_layout sama7g5_layout = { .datasheet_name = name, \ } +#define AT91_SAMA5D2_CHAN_TEMP(num, name, addr) \ + { \ + .type = IIO_TEMP, \ + .channel = num, \ + .address = addr, \ + .scan_index = num, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ + .info_mask_shared_by_all = \ + BIT(IIO_CHAN_INFO_PROCESSED) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .datasheet_name = name, \ + } + #define at91_adc_readl(st, reg) \ readl_relaxed((st)->base + (st)->soc_info.platform->layout->reg) #define at91_adc_read_chan(st, reg) \ @@ -415,6 +447,8 @@ static const struct at91_adc_reg_layout sama7g5_layout = { * @oversampling_avail: available oversampling values * @oversampling_avail_no: number of available oversampling values * @chan_realbits: realbits for registered channels + * @temp_chan: temperature channel index + * @temp_sensor: temperature sensor supported */ struct at91_adc_platform { const struct at91_adc_reg_layout *layout; @@ -430,20 +464,54 @@ struct at91_adc_platform { unsigned int oversampling_avail[5]; unsigned int oversampling_avail_no; unsigned int chan_realbits; + unsigned int temp_chan; + bool temp_sensor; }; +/** + * struct at91_adc_temp_sensor_clb - at91-sama5d2 temperature sensor + * calibration data structure + * @p1: P1 calibration temperature + * @p4: P4 calibration voltage + * @p6: P6 calibration voltage + */ +struct at91_adc_temp_sensor_clb { + u32 p1; + u32 p4; + u32 p6; +}; + +/** + * enum at91_adc_ts_clb_idx - calibration indexes in NVMEM buffer + * @AT91_ADC_TS_CLB_IDX_P1: index for P1 + * @AT91_ADC_TS_CLB_IDX_P4: index for P4 + * @AT91_ADC_TS_CLB_IDX_P6: index for P6 + * @AT91_ADC_TS_CLB_IDX_MAX: max index for temperature calibration packet in OTP + */ +enum at91_adc_ts_clb_idx { + AT91_ADC_TS_CLB_IDX_P1 = 2, + AT91_ADC_TS_CLB_IDX_P4 = 5, + AT91_ADC_TS_CLB_IDX_P6 = 7, + AT91_ADC_TS_CLB_IDX_MAX = 19, +}; + +/* Temperature sensor calibration - Vtemp voltage sensitivity to temperature. */ +#define AT91_ADC_TS_VTEMP_DT (2080U) + /** * 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 + * @temp_sensor_clb: temperature sensor calibration data 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_temp_sensor_clb temp_sensor_clb; }; struct at91_adc_trigger { @@ -491,6 +559,18 @@ struct at91_adc_touch { struct work_struct workq; }; +/** + * struct at91_adc_temp - at91-sama5d2 temperature information structure + * @sample_period_val: sample period value + * @saved_sample_rate: saved sample rate + * @saved_oversampling: saved oversampling + */ +struct at91_adc_temp { + u16 sample_period_val; + u16 saved_sample_rate; + u16 saved_oversampling; +}; + /* * Buffer size requirements: * No channels * bytes_per_channel(2) + timestamp bytes (8) @@ -518,6 +598,7 @@ struct at91_adc_state { wait_queue_head_t wq_data_available; struct at91_adc_dma dma_st; struct at91_adc_touch touch_st; + struct at91_adc_temp temp_st; struct iio_dev *indio_dev; /* Ensure naturally aligned timestamp */ u16 buffer[AT91_BUFFER_MAX_HWORDS] __aligned(8); @@ -607,6 +688,7 @@ static const struct iio_chan_spec at91_sama7g5_adc_channels[] = { AT91_SAMA5D2_CHAN_DIFF(22, 12, 13, 0x90), AT91_SAMA5D2_CHAN_DIFF(23, 14, 15, 0x98), IIO_CHAN_SOFT_TIMESTAMP(24), + AT91_SAMA5D2_CHAN_TEMP(AT91_SAMA7G5_ADC_TEMP_CHANNEL, "temp", 0xdc), }; static const struct at91_adc_platform sama5d2_platform = { @@ -639,10 +721,13 @@ static const struct at91_adc_platform sama7g5_platform = { .adc_channels = &at91_sama7g5_adc_channels, #define AT91_SAMA7G5_SINGLE_CHAN_CNT 16 #define AT91_SAMA7G5_DIFF_CHAN_CNT 8 +#define AT91_SAMA7G5_TEMP_CHAN_CNT 1 .nr_channels = AT91_SAMA7G5_SINGLE_CHAN_CNT + - AT91_SAMA7G5_DIFF_CHAN_CNT, + AT91_SAMA7G5_DIFF_CHAN_CNT + + AT91_SAMA7G5_TEMP_CHAN_CNT, #define AT91_SAMA7G5_MAX_CHAN_IDX (AT91_SAMA7G5_SINGLE_CHAN_CNT + \ - AT91_SAMA7G5_DIFF_CHAN_CNT) + AT91_SAMA7G5_DIFF_CHAN_CNT + \ + AT91_SAMA7G5_TEMP_CHAN_CNT) .max_channels = ARRAY_SIZE(at91_sama7g5_adc_channels), .max_index = AT91_SAMA7G5_MAX_CHAN_IDX, #define AT91_SAMA7G5_HW_TRIG_CNT 3 @@ -651,6 +736,8 @@ static const struct at91_adc_platform sama7g5_platform = { .oversampling_avail = { 1, 4, 16, 64, 256, }, .oversampling_avail_no = 5, .chan_realbits = 16, + .temp_sensor = true, + .temp_chan = AT91_SAMA7G5_ADC_TEMP_CHANNEL, }; static int at91_adc_chan_xlate(struct iio_dev *indio_dev, int chan) @@ -1193,7 +1280,8 @@ static int at91_adc_buffer_prepare(struct iio_dev *indio_dev) continue; /* these channel types cannot be handled by this trigger */ if (chan->type == IIO_POSITIONRELATIVE || - chan->type == IIO_PRESSURE) + chan->type == IIO_PRESSURE || + chan->type == IIO_TEMP) continue; at91_adc_cor(st, chan); @@ -1235,7 +1323,8 @@ static int at91_adc_buffer_postdisable(struct iio_dev *indio_dev) continue; /* these channel types are virtual, no need to do anything */ if (chan->type == IIO_POSITIONRELATIVE || - chan->type == IIO_PRESSURE) + chan->type == IIO_PRESSURE || + chan->type == IIO_TEMP) continue; at91_adc_writel(st, CHDR, BIT(chan->channel)); @@ -1613,12 +1702,19 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, return ret; } - /* in this case we have a voltage channel */ + /* in this case we have a voltage or temperature channel */ st->chan = chan; at91_adc_cor(st, chan); at91_adc_writel(st, CHER, BIT(chan->channel)); + /* + * TEMPMR.TEMPON needs to update after CHER otherwise if none + * of the channels are enabled and TEMPMR.TEMPON = 1 will + * trigger DRDY interruption while preparing for temperature read. + */ + if (chan->type == IIO_TEMP) + at91_adc_writel(st, TEMPMR, AT91_SAMA5D2_TEMPMR_TEMPON); at91_adc_eoc_ena(st, chan->channel); at91_adc_writel(st, CR, AT91_SAMA5D2_CR_START); @@ -1638,6 +1734,8 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, } at91_adc_eoc_dis(st, st->chan->channel); + if (chan->type == IIO_TEMP) + at91_adc_writel(st, TEMPMR, 0U); at91_adc_writel(st, CHDR, BIT(chan->channel)); /* Needed to ACK the DRDY interruption */ @@ -1665,6 +1763,89 @@ static int at91_adc_read_info_locked(struct iio_dev *indio_dev, return ret; } +static void at91_adc_temp_sensor_configure(struct at91_adc_state *st, + bool start) +{ + u32 sample_rate, oversampling_ratio; + u32 startup_time, tracktim, trackx; + + if (start) { + /* + * Configure the sensor for best accuracy: 10MHz frequency, + * oversampling rate of 256, tracktim=0xf and trackx=1. + */ + sample_rate = 10 * MEGA; + oversampling_ratio = 256; + startup_time = AT91_SAMA5D2_MR_STARTUP_TS_MIN; + tracktim = AT91_SAMA5D2_MR_TRACKTIM_TS; + trackx = AT91_SAMA5D2_TRACKX_TS; + + st->temp_st.saved_sample_rate = st->current_sample_rate; + st->temp_st.saved_oversampling = st->oversampling_ratio; + } else { + /* Go back to previous settings. */ + sample_rate = st->temp_st.saved_sample_rate; + oversampling_ratio = st->temp_st.saved_oversampling; + startup_time = st->soc_info.startup_time; + tracktim = 0; + trackx = 0; + } + + at91_adc_setup_samp_freq(st->indio_dev, sample_rate, startup_time, + tracktim); + at91_adc_config_emr(st, oversampling_ratio, trackx); +} + +static int at91_adc_read_temp(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + struct at91_adc_temp_sensor_clb *clb = &st->soc_info.temp_sensor_clb; + u64 div1, div2; + u32 tmp; + int ret, vbg, vtemp; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + mutex_lock(&st->lock); + + at91_adc_temp_sensor_configure(st, true); + + /* Read VBG. */ + tmp = at91_adc_readl(st, ACR); + tmp |= AT91_SAMA5D2_ACR_SRCLCH; + at91_adc_writel(st, ACR, tmp); + ret = at91_adc_read_info_raw(indio_dev, chan, &vbg); + if (ret < 0) + goto restore_config; + + /* Read VTEMP. */ + tmp &= ~AT91_SAMA5D2_ACR_SRCLCH; + at91_adc_writel(st, ACR, tmp); + ret = at91_adc_read_info_raw(indio_dev, chan, &vtemp); + +restore_config: + /* Revert previous settings. */ + at91_adc_temp_sensor_configure(st, false); + mutex_unlock(&st->lock); + iio_device_release_direct_mode(indio_dev); + if (ret < 0) + return ret; + + /* + * Temp[milli] = p1[milli] + (vtemp * clb->p6 - clb->p4 * vbg)/ + * (vbg * AT91_ADC_TS_VTEMP_DT) + */ + div1 = DIV_ROUND_CLOSEST_ULL(((u64)vtemp * clb->p6), vbg); + div1 = DIV_ROUND_CLOSEST_ULL((div1 * 1000), AT91_ADC_TS_VTEMP_DT); + div2 = DIV_ROUND_CLOSEST_ULL((u64)clb->p4, AT91_ADC_TS_VTEMP_DT); + div2 *= 1000; + *val = clb->p1 + (int)div1 - (int)div2; + + return ret; +} + static int at91_adc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -1682,6 +1863,11 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev, *val2 = chan->scan_type.realbits; return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_PROCESSED: + if (chan->type != IIO_TEMP) + return -EINVAL; + return at91_adc_read_temp(indio_dev, chan, val); + case IIO_CHAN_INFO_SAMP_FREQ: *val = at91_adc_get_sample_freq(st); return IIO_VAL_INT; @@ -1997,12 +2183,61 @@ static int at91_adc_buffer_and_trigger_init(struct device *dev, return 0; } +static int at91_adc_temp_sensor_init(struct at91_adc_state *st, + struct device *dev) +{ + struct at91_adc_temp_sensor_clb *clb = &st->soc_info.temp_sensor_clb; + struct nvmem_cell *temp_calib; + u32 *buf; + size_t len; + int ret = 0; + + if (!st->soc_info.platform->temp_sensor) + return 0; + + /* Get the calibration data from NVMEM. */ + temp_calib = devm_nvmem_cell_get(dev, "temperature_calib"); + if (IS_ERR(temp_calib)) { + ret = PTR_ERR(temp_calib); + if (ret != -ENOENT) + dev_err(dev, "Failed to get temperature_calib cell!\n"); + return ret; + } + + buf = nvmem_cell_read(temp_calib, &len); + if (IS_ERR(buf)) { + dev_err(dev, "Failed to read calibration data!\n"); + return PTR_ERR(buf); + } + if (len < AT91_ADC_TS_CLB_IDX_MAX * 4) { + dev_err(dev, "Invalid calibration data!\n"); + ret = -EINVAL; + goto free_buf; + } + + /* Store calibration data for later use. */ + clb->p1 = buf[AT91_ADC_TS_CLB_IDX_P1]; + clb->p4 = buf[AT91_ADC_TS_CLB_IDX_P4]; + clb->p6 = buf[AT91_ADC_TS_CLB_IDX_P6]; + + /* + * We prepare here the conversion to milli and also add constant + * factor (5 degrees Celsius) to p1 here to avoid doing it on + * hotpath. + */ + clb->p1 = clb->p1 * 1000 + 5000; + +free_buf: + kfree(buf); + return ret; +} + static int at91_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; struct at91_adc_state *st; struct resource *res; - int ret, i; + int ret, i, num_channels; u32 edge_type = IRQ_TYPE_NONE; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); @@ -2014,11 +2249,18 @@ static int at91_adc_probe(struct platform_device *pdev) st->soc_info.platform = of_device_get_match_data(&pdev->dev); + ret = at91_adc_temp_sensor_init(st, &pdev->dev); + /* Don't register temperature channel if initialization failed. */ + if (ret) + num_channels = st->soc_info.platform->max_channels - 1; + else + num_channels = st->soc_info.platform->max_channels; + 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 = *st->soc_info.platform->adc_channels; - indio_dev->num_channels = st->soc_info.platform->max_channels; + indio_dev->num_channels = num_channels; bitmap_set(&st->touch_st.channels_bitmask, st->soc_info.platform->touch_chan_x, 1); -- cgit v1.2.3 From 0cf53f303a02f5c4560042e856035b995a457c1f Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 3 Aug 2022 13:28:54 +0300 Subject: iio: adc: at91-sama5d2_adc: add empty line after functions Add empty line after function. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20220803102855.2191070-19-claudiu.beznea@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 0a7b1680f158..94cb96ccd2ee 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -1371,6 +1371,7 @@ static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *indio, return trig; } + static void at91_adc_trigger_handler_nodma(struct iio_dev *indio_dev, struct iio_poll_func *pf) { -- cgit v1.2.3 From 75d7556ac0e4bff830f7f90ceaa8d35f4b6c346a Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 3 Aug 2022 13:28:55 +0300 Subject: iio: adc: at91-sama5d2_adc: add runtime pm support Add runtime PM support by disabling/enabling ADC's peripheral clock. On simple conversion the ADC's clock is kept enabled just while the conversion is in progress. This includes also temperature conversion. For triggered buffers and touch conversions the ADC clock is kept enabled while the triggered buffers or touch are enabled. Along with it removed the __maybe_unused on suspend() and resume() ops as the dev_pm_ops object members are now filled with SYSTEM_SLEEP_PM_OPS(). Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20220803102855.2191070-20-claudiu.beznea@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 208 +++++++++++++++++++++++++++++-------- 1 file changed, 166 insertions(+), 42 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 94cb96ccd2ee..8d8aa9691499 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -600,6 +601,7 @@ struct at91_adc_state { struct at91_adc_touch touch_st; struct at91_adc_temp temp_st; struct iio_dev *indio_dev; + struct device *dev; /* Ensure naturally aligned timestamp */ u16 buffer[AT91_BUFFER_MAX_HWORDS] __aligned(8); /* @@ -840,9 +842,9 @@ static int at91_adc_config_emr(struct at91_adc_state *st, u32 oversampling_ratio, u32 trackx) { /* configure the extended mode register */ - unsigned int emr = at91_adc_readl(st, EMR); + unsigned int emr, osr; unsigned int osr_mask = st->soc_info.platform->osr_mask; - int i; + int i, ret; /* Check against supported oversampling values. */ for (i = 0; i < st->soc_info.platform->oversampling_avail_no; i++) { @@ -852,40 +854,46 @@ static int at91_adc_config_emr(struct at91_adc_state *st, if (i == st->soc_info.platform->oversampling_avail_no) return -EINVAL; - /* select oversampling per single trigger event */ - emr |= AT91_SAMA5D2_EMR_ASTE(1); - - /* delete leftover content if it's the case */ - emr &= ~(osr_mask | AT91_SAMA5D2_TRACKX_MASK); - /* select oversampling ratio from configuration */ switch (oversampling_ratio) { case 1: - emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_1SAMPLES, - osr_mask); + osr = AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_1SAMPLES, + osr_mask); break; case 4: - emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_4SAMPLES, - osr_mask); + osr = AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_4SAMPLES, + osr_mask); break; case 16: - emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_16SAMPLES, - osr_mask); + osr = AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_16SAMPLES, + osr_mask); break; case 64: - emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_64SAMPLES, - osr_mask); + osr = AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_64SAMPLES, + osr_mask); break; case 256: - emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_256SAMPLES, - osr_mask); + osr = AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_256SAMPLES, + osr_mask); break; } - /* Update trackx. */ - emr |= AT91_SAMA5D2_TRACKX(trackx); + ret = pm_runtime_resume_and_get(st->dev); + if (ret < 0) + return ret; + + emr = at91_adc_readl(st, EMR); + /* select oversampling per single trigger event */ + emr |= AT91_SAMA5D2_EMR_ASTE(1); + /* delete leftover content if it's the case */ + emr &= ~(osr_mask | AT91_SAMA5D2_TRACKX_MASK); + /* Update osr and trackx. */ + emr |= osr | AT91_SAMA5D2_TRACKX(trackx); at91_adc_writel(st, EMR, emr); + pm_runtime_mark_last_busy(st->dev); + pm_runtime_put_autosuspend(st->dev); + st->oversampling_ratio = oversampling_ratio; return 0; @@ -944,15 +952,22 @@ static void at91_adc_adjust_val_osr_array(struct at91_adc_state *st, void *buf, static int at91_adc_configure_touch(struct at91_adc_state *st, bool state) { u32 clk_khz = st->current_sample_rate / 1000; - int i = 0; + int i = 0, ret; u16 pendbc; u32 tsmr, acr; - if (!state) { + if (state) { + ret = pm_runtime_resume_and_get(st->dev); + if (ret < 0) + return ret; + } else { /* disabling touch IRQs and setting mode to no touch enabled */ at91_adc_writel(st, IDR, AT91_SAMA5D2_IER_PEN | AT91_SAMA5D2_IER_NOPEN); at91_adc_writel(st, TSMR, 0); + + pm_runtime_mark_last_busy(st->dev); + pm_runtime_put_autosuspend(st->dev); return 0; } /* @@ -1093,10 +1108,9 @@ static int at91_adc_read_pressure(struct at91_adc_state *st, int chan, u16 *val) return IIO_VAL_INT; } -static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state) +static void at91_adc_configure_trigger_registers(struct at91_adc_state *st, + 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, TRGR); /* clear TRGMOD */ @@ -1107,6 +1121,26 @@ static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state) /* set/unset hw trigger */ at91_adc_writel(st, TRGR, status); +} + +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); + int ret; + + if (state) { + ret = pm_runtime_resume_and_get(st->dev); + if (ret < 0) + return ret; + } + + at91_adc_configure_trigger_registers(st, state); + + if (!state) { + pm_runtime_mark_last_busy(st->dev); + pm_runtime_put_autosuspend(st->dev); + } return 0; } @@ -1265,11 +1299,15 @@ static int at91_adc_buffer_prepare(struct iio_dev *indio_dev) if (!(iio_device_get_current_mode(indio_dev) & INDIO_ALL_TRIGGERED_MODES)) return -EINVAL; + ret = pm_runtime_resume_and_get(st->dev); + if (ret < 0) + return ret; + /* we continue with the triggered buffer */ ret = at91_adc_dma_start(indio_dev); if (ret) { dev_err(&indio_dev->dev, "buffer prepare failed\n"); - return ret; + goto pm_runtime_put; } for_each_set_bit(bit, indio_dev->active_scan_mask, @@ -1292,12 +1330,16 @@ static int at91_adc_buffer_prepare(struct iio_dev *indio_dev) if (at91_adc_buffer_check_use_irq(indio_dev, st)) at91_adc_writel(st, IER, AT91_SAMA5D2_IER_DRDY); - return 0; +pm_runtime_put: + pm_runtime_mark_last_busy(st->dev); + pm_runtime_put_autosuspend(st->dev); + return ret; } static int at91_adc_buffer_postdisable(struct iio_dev *indio_dev) { struct at91_adc_state *st = iio_priv(indio_dev); + int ret; u8 bit; /* check if we are disabling triggered buffer or the touchscreen */ @@ -1308,6 +1350,10 @@ static int at91_adc_buffer_postdisable(struct iio_dev *indio_dev) if (!(iio_device_get_current_mode(indio_dev) & INDIO_ALL_TRIGGERED_MODES)) return -EINVAL; + ret = pm_runtime_resume_and_get(st->dev); + if (ret < 0) + return ret; + /* * For each enable channel we must disable it in hardware. * In the case of DMA, we must read the last converted value @@ -1343,6 +1389,9 @@ static int at91_adc_buffer_postdisable(struct iio_dev *indio_dev) if (st->dma_st.dma_chan) dmaengine_terminate_sync(st->dma_st.dma_chan); + pm_runtime_mark_last_busy(st->dev); + pm_runtime_put_autosuspend(st->dev); + return 0; } @@ -1531,12 +1580,17 @@ static void at91_adc_setup_samp_freq(struct iio_dev *indio_dev, unsigned freq, { struct at91_adc_state *st = iio_priv(indio_dev); unsigned f_per, prescal, startup, mr; + int ret; f_per = clk_get_rate(st->per_clk); prescal = (f_per / (2 * freq)) - 1; startup = at91_adc_startup_time(startup_time, freq / 1000); + ret = pm_runtime_resume_and_get(st->dev); + if (ret < 0) + return; + mr = at91_adc_readl(st, MR); mr &= ~(AT91_SAMA5D2_MR_STARTUP_MASK | AT91_SAMA5D2_MR_PRESCAL_MASK); mr |= AT91_SAMA5D2_MR_STARTUP(startup); @@ -1544,6 +1598,9 @@ static void at91_adc_setup_samp_freq(struct iio_dev *indio_dev, unsigned freq, mr |= AT91_SAMA5D2_MR_TRACKTIM(tracktim); at91_adc_writel(st, MR, mr); + pm_runtime_mark_last_busy(st->dev); + pm_runtime_put_autosuspend(st->dev); + dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u, tracktim=%u\n", freq, startup, prescal, tracktim); st->current_sample_rate = freq; @@ -1680,6 +1737,10 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, u16 tmp_val; int ret; + ret = pm_runtime_resume_and_get(st->dev); + if (ret < 0) + return ret; + /* * Keep in mind that we cannot use software trigger or touchscreen * if external trigger is enabled @@ -1691,7 +1752,7 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, if (ret > 0) ret = at91_adc_adjust_val_osr(st, val); - return ret; + goto pm_runtime_put; } if (chan->type == IIO_PRESSURE) { ret = at91_adc_read_pressure(st, chan->channel, @@ -1700,7 +1761,7 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, if (ret > 0) ret = at91_adc_adjust_val_osr(st, val); - return ret; + goto pm_runtime_put; } /* in this case we have a voltage or temperature channel */ @@ -1742,6 +1803,9 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, /* Needed to ACK the DRDY interruption */ at91_adc_readl(st, LCDR); +pm_runtime_put: + pm_runtime_mark_last_busy(st->dev); + pm_runtime_put_autosuspend(st->dev); return ret; } @@ -1811,6 +1875,10 @@ static int at91_adc_read_temp(struct iio_dev *indio_dev, return ret; mutex_lock(&st->lock); + ret = pm_runtime_resume_and_get(st->dev); + if (ret < 0) + goto unlock; + at91_adc_temp_sensor_configure(st, true); /* Read VBG. */ @@ -1829,6 +1897,9 @@ static int at91_adc_read_temp(struct iio_dev *indio_dev, restore_config: /* Revert previous settings. */ at91_adc_temp_sensor_configure(st, false); + pm_runtime_mark_last_busy(st->dev); + pm_runtime_put_autosuspend(st->dev); +unlock: mutex_unlock(&st->lock); iio_device_release_direct_mode(indio_dev); if (ret < 0) @@ -2373,13 +2444,19 @@ static int at91_adc_probe(struct platform_device *pdev) if (ret) goto vref_disable; - at91_adc_hw_init(indio_dev); - platform_set_drvdata(pdev, indio_dev); + st->dev = &pdev->dev; + pm_runtime_set_autosuspend_delay(st->dev, 500); + pm_runtime_use_autosuspend(st->dev); + pm_runtime_set_active(st->dev); + pm_runtime_enable(st->dev); + pm_runtime_get_noresume(st->dev); + + at91_adc_hw_init(indio_dev); ret = at91_adc_buffer_and_trigger_init(&pdev->dev, indio_dev); if (ret < 0) - goto per_clk_disable_unprepare; + goto err_pm_disable; if (dma_coerce_mask_and_coherent(&indio_dev->dev, DMA_BIT_MASK(32))) dev_info(&pdev->dev, "cannot set DMA mask to 32-bit\n"); @@ -2395,11 +2472,18 @@ static int at91_adc_probe(struct platform_device *pdev) dev_info(&pdev->dev, "version: %x\n", readl_relaxed(st->base + st->soc_info.platform->layout->VERSION)); + pm_runtime_mark_last_busy(st->dev); + pm_runtime_put_autosuspend(st->dev); + return 0; dma_disable: at91_adc_dma_disable(st); -per_clk_disable_unprepare: +err_pm_disable: + pm_runtime_put_noidle(st->dev); + pm_runtime_disable(st->dev); + pm_runtime_set_suspended(st->dev); + pm_runtime_dont_use_autosuspend(st->dev); clk_disable_unprepare(st->per_clk); vref_disable: regulator_disable(st->vref); @@ -2417,6 +2501,8 @@ static int at91_adc_remove(struct platform_device *pdev) at91_adc_dma_disable(st); + pm_runtime_disable(st->dev); + pm_runtime_set_suspended(st->dev); clk_disable_unprepare(st->per_clk); regulator_disable(st->vref); @@ -2429,6 +2515,11 @@ static int at91_adc_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct at91_adc_state *st = iio_priv(indio_dev); + int ret; + + ret = pm_runtime_resume_and_get(st->dev); + if (ret < 0) + return ret; if (iio_buffer_enabled(indio_dev)) at91_adc_buffer_postdisable(indio_dev); @@ -2441,6 +2532,8 @@ static int at91_adc_suspend(struct device *dev) */ at91_adc_writel(st, CR, AT91_SAMA5D2_CR_SWRST); + pm_runtime_mark_last_busy(st->dev); + pm_runtime_put_noidle(st->dev); clk_disable_unprepare(st->per_clk); regulator_disable(st->vref); regulator_disable(st->reg); @@ -2470,18 +2563,28 @@ static int at91_adc_resume(struct device *dev) if (ret) goto vref_disable_resume; + pm_runtime_get_noresume(st->dev); + at91_adc_hw_init(indio_dev); /* reconfiguring trigger hardware state */ - if (!iio_buffer_enabled(indio_dev)) - return 0; + if (iio_buffer_enabled(indio_dev)) { + ret = at91_adc_buffer_prepare(indio_dev); + if (ret) + goto pm_runtime_put; - ret = at91_adc_buffer_prepare(indio_dev); - if (ret) - goto vref_disable_resume; + at91_adc_configure_trigger_registers(st, true); + } + + pm_runtime_mark_last_busy(st->dev); + pm_runtime_put_autosuspend(st->dev); - return at91_adc_configure_trigger(st->trig, true); + return 0; +pm_runtime_put: + pm_runtime_mark_last_busy(st->dev); + pm_runtime_put_noidle(st->dev); + clk_disable_unprepare(st->per_clk); vref_disable_resume: regulator_disable(st->vref); reg_disable_resume: @@ -2491,8 +2594,29 @@ resume_failed: return ret; } -static DEFINE_SIMPLE_DEV_PM_OPS(at91_adc_pm_ops, at91_adc_suspend, - at91_adc_resume); +static int at91_adc_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct at91_adc_state *st = iio_priv(indio_dev); + + clk_disable(st->per_clk); + + return 0; +} + +static int at91_adc_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct at91_adc_state *st = iio_priv(indio_dev); + + return clk_enable(st->per_clk); +} + +static const struct dev_pm_ops at91_adc_pm_ops = { + SYSTEM_SLEEP_PM_OPS(at91_adc_suspend, at91_adc_resume) + RUNTIME_PM_OPS(at91_adc_runtime_suspend, at91_adc_runtime_resume, + NULL) +}; static const struct of_device_id at91_adc_dt_match[] = { { @@ -2513,7 +2637,7 @@ static struct platform_driver at91_adc_driver = { .driver = { .name = "at91-sama5d2_adc", .of_match_table = at91_adc_dt_match, - .pm = pm_sleep_ptr(&at91_adc_pm_ops), + .pm = pm_ptr(&at91_adc_pm_ops), }, }; module_platform_driver(at91_adc_driver) -- cgit v1.2.3 From 15b2ac67859006bace228cbdc8607d0ea0af4e89 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Fri, 5 Aug 2022 15:57:26 +0200 Subject: iio: adc: qcom-spmi-adc5: Add missing VCOIN/GPIO[134] channels These channels are specified in downstream kernels [1] and actively used by e.g. the Sony Seine platform on the SM6125 SoC. Note that GPIO2 isn't used on this platform and, while the definition downstream is identical to the other GPIOx_100K_PU definitions, has been omitted for lack of proper testing. [1]: https://source.codeaurora.org/quic/la/kernel/msm-4.14/tree/drivers/iio/adc/qcom-spmi-adc5.c?h=LA.UM.7.11.r1-05200-NICOBAR.0#n688 Signed-off-by: Marijn Suijten Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20220805135729.1037079-3-marijn.suijten@somainline.org Signed-off-by: Jonathan Cameron --- drivers/iio/adc/qcom-spmi-adc5.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c index 87438d1e5c0b..0dc4fe612433 100644 --- a/drivers/iio/adc/qcom-spmi-adc5.c +++ b/drivers/iio/adc/qcom-spmi-adc5.c @@ -526,6 +526,8 @@ static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = { SCALE_HW_CALIB_DEFAULT) [ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 1, SCALE_HW_CALIB_DEFAULT) + [ADC5_VCOIN] = ADC5_CHAN_VOLT("vcoin", 1, + SCALE_HW_CALIB_DEFAULT) [ADC5_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 0, SCALE_HW_CALIB_PMIC_THERM) [ADC5_USB_IN_I] = ADC5_CHAN_VOLT("usb_in_i_uv", 0, @@ -549,6 +551,12 @@ static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = { SCALE_HW_CALIB_THERM_100K_PULLUP) [ADC5_AMUX_THM2] = ADC5_CHAN_TEMP("amux_thm2", 0, SCALE_HW_CALIB_PM5_SMB_TEMP) + [ADC5_GPIO1_100K_PU] = ADC5_CHAN_TEMP("gpio1_100k_pu", 0, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_GPIO3_100K_PU] = ADC5_CHAN_TEMP("gpio3_100k_pu", 0, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_GPIO4_100K_PU] = ADC5_CHAN_TEMP("gpio4_100k_pu", 0, + SCALE_HW_CALIB_THERM_100K_PULLUP) }; static const struct adc5_channels adc7_chans_pmic[ADC5_MAX_CHANNEL] = { -- cgit v1.2.3 From 79c3e84874c7d14f04ad58313b64955a0d2e9437 Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Fri, 15 Jul 2022 14:28:49 +0200 Subject: iio: inkern: only release the device node when done with it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'of_node_put()' can potentially release the memory pointed to by 'iiospec.np' which would leave us with an invalid pointer (and we would still pass it in 'of_xlate()'). Note that it is not guaranteed for the of_node lifespan to be attached to the device (to which is attached) lifespan so that there is (even though very unlikely) the possibility for the node to be freed while the device is still around. Thus, as there are indeed some of_xlate users which do access the node, a race is indeed possible. As such, we can only release the node after we are done with it. Fixes: 17d82b47a215d ("iio: Add OF support") Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20220715122903.332535-2-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/inkern.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index df74765d33dc..9d87057794fc 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -165,9 +165,10 @@ static int __of_iio_channel_get(struct iio_channel *channel, idev = bus_find_device(&iio_bus_type, NULL, iiospec.np, iio_dev_node_match); - of_node_put(iiospec.np); - if (idev == NULL) + if (idev == NULL) { + of_node_put(iiospec.np); return -EPROBE_DEFER; + } indio_dev = dev_to_iio_dev(idev); channel->indio_dev = indio_dev; @@ -175,6 +176,7 @@ static int __of_iio_channel_get(struct iio_channel *channel, index = indio_dev->info->of_xlate(indio_dev, &iiospec); else index = __of_iio_simple_xlate(indio_dev, &iiospec); + of_node_put(iiospec.np); if (index < 0) goto err_put; channel->channel = &indio_dev->channels[index]; -- cgit v1.2.3 From 9e878dbc0e8322f8b2f5ab0093c1e89926362dbe Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Fri, 15 Jul 2022 14:28:50 +0200 Subject: iio: inkern: fix return value in devm_of_iio_channel_get_by_name() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit of_iio_channel_get_by_name() can either return NULL or an error pointer so that only doing IS_ERR() is not enough. Fix it by checking the NULL pointer case and return -ENODEV in that case. Note this is done like this so that users of the function (which only check for error pointers) do not need to be changed. This is not ideal since we are losing error codes and as such, in a follow up change, things will be unified so that of_iio_channel_get_by_name() only returns error codes. Fixes: 6e39b145cef7 ("iio: provide of_iio_channel_get_by_name() and devm_ version it") Signed-off-by: Nuno Sá Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220715122903.332535-3-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/inkern.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 9d87057794fc..87fd2a0d44f2 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -412,6 +412,8 @@ struct iio_channel *devm_of_iio_channel_get_by_name(struct device *dev, channel = of_iio_channel_get_by_name(np, channel_name); if (IS_ERR(channel)) return channel; + if (!channel) + return ERR_PTR(-ENODEV); ret = devm_add_action_or_reset(dev, devm_iio_channel_free, channel); if (ret) -- cgit v1.2.3 From ed5e5ed4e377599825d32162a6ff3e9d2d89d200 Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Fri, 15 Jul 2022 14:28:51 +0200 Subject: iio: inkern: only return error codes in iio_channel_get_*() APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit APIs like of_iio_channel_get_by_name() and of_iio_channel_get_all() were returning a mix of NULL and pointers with NULL being the way to "notify" that we should do a "system" lookup for channels. This make it very confusing and prone to errors as commit 9f63cc0921ec ("iio: inkern: fix return value in devm_of_iio_channel_get_by_name()") proves. On top of this, patterns like 'if (channel != NULL) return channel' were being used where channel could actually be an error code which makes the code hard to read. This change also makes some functional changes on how errors were being handled. In the original behavior, even if we get an error like '-ENOMEM', we still continue with the search. We should only continue to lookup for the channel when it makes sense to do so. Hence, the main error handling in 'of_iio_channel_get_by_name()' is changed to the following logic: * If a channel 'name' is provided and we do find it via 'io-channel-names', we should be able to get it. If we get any error, we should not proceed with the lookup. Moreover, we should return an error so that callers won't proceed with a system lookup. * If a channel 'name' is provided and we cannot find it ('index < 0'), 'of_parse_phandle_with_args()' is expected to fail with '-EINVAL'. Hence, we should only continue if we get that error. * If a channel 'name' is not provided we should only carry on with the search if 'of_parse_phandle_with_args()' returns '-ENOENT'. Also note that a system channel lookup is only done if the returned error code (from 'of_iio_channel_get_by_name()' or 'of_iio_channel_get_all()' is -ENODEV. Signed-off-by: Nuno Sá Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220715122903.332535-4-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/inkern.c | 54 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 87fd2a0d44f2..49913660e087 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -214,7 +214,7 @@ err_free_channel: struct iio_channel *of_iio_channel_get_by_name(struct device_node *np, const char *name) { - struct iio_channel *chan = NULL; + struct iio_channel *chan; /* Walk up the tree of devices looking for a matching iio channel */ while (np) { @@ -231,11 +231,33 @@ struct iio_channel *of_iio_channel_get_by_name(struct device_node *np, name); chan = of_iio_channel_get(np, index); if (!IS_ERR(chan) || PTR_ERR(chan) == -EPROBE_DEFER) - break; - else if (name && index >= 0) { - pr_err("ERROR: could not get IIO channel %pOF:%s(%i)\n", - np, name ? name : "", index); - return NULL; + return chan; + if (name) { + if (index >= 0) { + pr_err("ERROR: could not get IIO channel %pOF:%s(%i)\n", + np, name, index); + /* + * In this case, we found 'name' in 'io-channel-names' + * but somehow we still fail so that we should not proceed + * with any other lookup. Hence, explicitly return -EINVAL + * (maybe not the better error code) so that the caller + * won't do a system lookup. + */ + return ERR_PTR(-EINVAL); + } + /* + * If index < 0, then of_parse_phandle_with_args() fails + * with -EINVAL which is expected. We should not proceed + * if we get any other error. + */ + if (PTR_ERR(chan) != -EINVAL) + return chan; + } else if (PTR_ERR(chan) != -ENOENT) { + /* + * if !name, then we should only proceed the lookup if + * of_parse_phandle_with_args() returns -ENOENT. + */ + return chan; } /* @@ -245,10 +267,10 @@ struct iio_channel *of_iio_channel_get_by_name(struct device_node *np, */ np = np->parent; if (np && !of_get_property(np, "io-channel-ranges", NULL)) - return NULL; + return ERR_PTR(-ENODEV); } - return chan; + return ERR_PTR(-ENODEV); } EXPORT_SYMBOL_GPL(of_iio_channel_get_by_name); @@ -267,8 +289,8 @@ static struct iio_channel *of_iio_channel_get_all(struct device *dev) break; } while (++nummaps); - if (nummaps == 0) /* no error, return NULL to search map table */ - return NULL; + if (nummaps == 0) + return ERR_PTR(-ENODEV); /* NULL terminated array to save passing size */ chans = kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL); @@ -295,7 +317,7 @@ error_free_chans: static inline struct iio_channel *of_iio_channel_get_all(struct device *dev) { - return NULL; + return ERR_PTR(-ENODEV); } #endif /* CONFIG_OF */ @@ -362,7 +384,7 @@ struct iio_channel *iio_channel_get(struct device *dev, if (dev) { channel = of_iio_channel_get_by_name(dev->of_node, channel_name); - if (channel != NULL) + if (!IS_ERR(channel) || PTR_ERR(channel) != -ENODEV) return channel; } @@ -412,8 +434,6 @@ struct iio_channel *devm_of_iio_channel_get_by_name(struct device *dev, channel = of_iio_channel_get_by_name(np, channel_name); if (IS_ERR(channel)) return channel; - if (!channel) - return ERR_PTR(-ENODEV); ret = devm_add_action_or_reset(dev, devm_iio_channel_free, channel); if (ret) @@ -436,7 +456,11 @@ struct iio_channel *iio_channel_get_all(struct device *dev) return ERR_PTR(-EINVAL); chans = of_iio_channel_get_all(dev); - if (chans) + /* + * We only want to carry on if the error is -ENODEV. Anything else + * should be reported up the stack. + */ + if (!IS_ERR(chans) || PTR_ERR(chans) != -ENODEV) return chans; name = dev_name(dev); -- cgit v1.2.3 From d6bb09eab2b3c4c55e5f6006cf8e759439e3e741 Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Fri, 15 Jul 2022 14:28:52 +0200 Subject: iio: inkern: split of_iio_channel_get_by_name() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change splits of_iio_channel_get_by_name() so that it decouples looking for channels in the current node from looking in it's parents nodes. This will be helpful when moving to fwnode properties where we need to release the handles when looking for channels in parent's nodes. No functional change intended... Signed-off-by: Nuno Sá Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220715122903.332535-5-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/inkern.c | 112 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 47 deletions(-) diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 49913660e087..9e0c7cc3a093 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -211,63 +211,81 @@ err_free_channel: return ERR_PTR(err); } +static struct iio_channel *__of_iio_channel_get_by_name(struct device_node *np, + const char *name) +{ + struct iio_channel *chan; + int index = 0; + + /* + * For named iio channels, first look up the name in the + * "io-channel-names" property. If it cannot be found, the + * index will be an error code, and of_iio_channel_get() + * will fail. + */ + if (name) + index = of_property_match_string(np, "io-channel-names", name); + + chan = of_iio_channel_get(np, index); + if (!IS_ERR(chan) || PTR_ERR(chan) == -EPROBE_DEFER) + return chan; + if (name) { + if (index >= 0) { + pr_err("ERROR: could not get IIO channel %pOF:%s(%i)\n", + np, name, index); + /* + * In this case, we found 'name' in 'io-channel-names' + * but somehow we still fail so that we should not proceed + * with any other lookup. Hence, explicitly return -EINVAL + * (maybe not the better error code) so that the caller + * won't do a system lookup. + */ + return ERR_PTR(-EINVAL); + } + /* + * If index < 0, then of_parse_phandle_with_args() fails + * with -EINVAL which is expected. We should not proceed + * if we get any other error. + */ + if (PTR_ERR(chan) != -EINVAL) + return chan; + } else if (PTR_ERR(chan) != -ENOENT) { + /* + * if !name, then we should only proceed the lookup if + * of_parse_phandle_with_args() returns -ENOENT. + */ + return chan; + } + + /* so we continue the lookup */ + return ERR_PTR(-ENODEV); +} + struct iio_channel *of_iio_channel_get_by_name(struct device_node *np, const char *name) { struct iio_channel *chan; /* Walk up the tree of devices looking for a matching iio channel */ + chan = __of_iio_channel_get_by_name(np, name); + if (!IS_ERR(chan) || PTR_ERR(chan) != -ENODEV) + return chan; + + /* + * No matching IIO channel found on this node. + * If the parent node has a "io-channel-ranges" property, + * then we can try one of its channels. + */ + np = np->parent; while (np) { - int index = 0; + if (!of_get_property(np, "io-channel-ranges", NULL)) + return ERR_PTR(-ENODEV); - /* - * For named iio channels, first look up the name in the - * "io-channel-names" property. If it cannot be found, the - * index will be an error code, and of_iio_channel_get() - * will fail. - */ - if (name) - index = of_property_match_string(np, "io-channel-names", - name); - chan = of_iio_channel_get(np, index); - if (!IS_ERR(chan) || PTR_ERR(chan) == -EPROBE_DEFER) - return chan; - if (name) { - if (index >= 0) { - pr_err("ERROR: could not get IIO channel %pOF:%s(%i)\n", - np, name, index); - /* - * In this case, we found 'name' in 'io-channel-names' - * but somehow we still fail so that we should not proceed - * with any other lookup. Hence, explicitly return -EINVAL - * (maybe not the better error code) so that the caller - * won't do a system lookup. - */ - return ERR_PTR(-EINVAL); - } - /* - * If index < 0, then of_parse_phandle_with_args() fails - * with -EINVAL which is expected. We should not proceed - * if we get any other error. - */ - if (PTR_ERR(chan) != -EINVAL) - return chan; - } else if (PTR_ERR(chan) != -ENOENT) { - /* - * if !name, then we should only proceed the lookup if - * of_parse_phandle_with_args() returns -ENOENT. - */ - return chan; - } + chan = __of_iio_channel_get_by_name(np, name); + if (!IS_ERR(chan) || PTR_ERR(chan) != -ENODEV) + return chan; - /* - * No matching IIO channel found on this node. - * If the parent node has a "io-channel-ranges" property, - * then we can try one of its channels. - */ np = np->parent; - if (np && !of_get_property(np, "io-channel-ranges", NULL)) - return ERR_PTR(-ENODEV); } return ERR_PTR(-ENODEV); -- cgit v1.2.3 From 1e64b9c5f9a01f1a752438724bc83180c451e1c7 Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Fri, 15 Jul 2022 14:28:53 +0200 Subject: iio: inkern: move to fwnode properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This moves the IIO in kernel interface to use fwnode properties and thus be firmware agnostic. Note that the interface is still not firmware agnostic. At this point we have both OF and fwnode interfaces so that we don't break any user. On top of this we also want to have a per driver conversion and that is the main reason we have both of_xlate() and fwnode_xlate() support. Signed-off-by: Nuno Sá Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220715122903.332535-6-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/inkern.c | 157 +++++++++++++++++++++++-------------------- include/linux/iio/consumer.h | 36 +++++----- include/linux/iio/iio.h | 5 ++ 3 files changed, 108 insertions(+), 90 deletions(-) diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 9e0c7cc3a093..9cfa66ef9536 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -5,6 +5,7 @@ */ #include #include +#include #include #include #include @@ -117,15 +118,8 @@ static const struct iio_chan_spec return chan; } -#ifdef CONFIG_OF - -static int iio_dev_node_match(struct device *dev, const void *data) -{ - return dev->of_node == data && dev->type == &iio_device_type; -} - /** - * __of_iio_simple_xlate - translate iiospec to the IIO channel index + * __fwnode_iio_simple_xlate - translate iiospec to the IIO channel index * @indio_dev: pointer to the iio_dev structure * @iiospec: IIO specifier as found in the device tree * @@ -134,14 +128,14 @@ static int iio_dev_node_match(struct device *dev, const void *data) * whether IIO index is less than num_channels (that is specified in the * iio_dev). */ -static int __of_iio_simple_xlate(struct iio_dev *indio_dev, - const struct of_phandle_args *iiospec) +static int __fwnode_iio_simple_xlate(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec) { - if (!iiospec->args_count) + if (!iiospec->nargs) return 0; if (iiospec->args[0] >= indio_dev->num_channels) { - dev_err(&indio_dev->dev, "invalid channel index %u\n", + dev_err(&indio_dev->dev, "invalid channel index %llu\n", iiospec->args[0]); return -EINVAL; } @@ -149,34 +143,55 @@ static int __of_iio_simple_xlate(struct iio_dev *indio_dev, return iiospec->args[0]; } -static int __of_iio_channel_get(struct iio_channel *channel, - struct device_node *np, int index) +/* + * Simple helper to copy fwnode_reference_args into of_phandle_args so we + * can pass it to of_xlate(). Ultimate goal is to drop this together with + * of_xlate(). + */ +static int __fwnode_to_of_xlate(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec) { + struct of_phandle_args of_args; + unsigned int i; + + of_args.args_count = iiospec->nargs; + of_args.np = to_of_node(iiospec->fwnode); + + for (i = 0; i < MAX_PHANDLE_ARGS; i++) + of_args.args[i] = i < iiospec->nargs ? iiospec->args[i] : 0; + + return indio_dev->info->of_xlate(indio_dev, &of_args); +} + +static int __fwnode_iio_channel_get(struct iio_channel *channel, + struct fwnode_handle *fwnode, int index) +{ + struct fwnode_reference_args iiospec; struct device *idev; struct iio_dev *indio_dev; int err; - struct of_phandle_args iiospec; - err = of_parse_phandle_with_args(np, "io-channels", - "#io-channel-cells", - index, &iiospec); + err = fwnode_property_get_reference_args(fwnode, "io-channels", + "#io-channel-cells", 0, + index, &iiospec); if (err) return err; - idev = bus_find_device(&iio_bus_type, NULL, iiospec.np, - iio_dev_node_match); + idev = bus_find_device_by_fwnode(&iio_bus_type, iiospec.fwnode); if (idev == NULL) { - of_node_put(iiospec.np); + fwnode_handle_put(iiospec.fwnode); return -EPROBE_DEFER; } indio_dev = dev_to_iio_dev(idev); channel->indio_dev = indio_dev; if (indio_dev->info->of_xlate) - index = indio_dev->info->of_xlate(indio_dev, &iiospec); + index = __fwnode_to_of_xlate(indio_dev, &iiospec); + else if (indio_dev->info->fwnode_xlate) + index = indio_dev->info->fwnode_xlate(indio_dev, &iiospec); else - index = __of_iio_simple_xlate(indio_dev, &iiospec); - of_node_put(iiospec.np); + index = __fwnode_iio_simple_xlate(indio_dev, &iiospec); + fwnode_handle_put(iiospec.fwnode); if (index < 0) goto err_put; channel->channel = &indio_dev->channels[index]; @@ -188,7 +203,8 @@ err_put: return index; } -static struct iio_channel *of_iio_channel_get(struct device_node *np, int index) +static struct iio_channel *fwnode_iio_channel_get(struct fwnode_handle *fwnode, + int index) { struct iio_channel *channel; int err; @@ -200,7 +216,7 @@ static struct iio_channel *of_iio_channel_get(struct device_node *np, int index) if (channel == NULL) return ERR_PTR(-ENOMEM); - err = __of_iio_channel_get(channel, np, index); + err = __fwnode_iio_channel_get(channel, fwnode, index); if (err) goto err_free_channel; @@ -211,8 +227,8 @@ err_free_channel: return ERR_PTR(err); } -static struct iio_channel *__of_iio_channel_get_by_name(struct device_node *np, - const char *name) +static struct iio_channel * +__fwnode_iio_channel_get_by_name(struct fwnode_handle *fwnode, const char *name) { struct iio_channel *chan; int index = 0; @@ -220,19 +236,20 @@ static struct iio_channel *__of_iio_channel_get_by_name(struct device_node *np, /* * For named iio channels, first look up the name in the * "io-channel-names" property. If it cannot be found, the - * index will be an error code, and of_iio_channel_get() + * index will be an error code, and fwnode_iio_channel_get() * will fail. */ if (name) - index = of_property_match_string(np, "io-channel-names", name); + index = fwnode_property_match_string(fwnode, "io-channel-names", + name); - chan = of_iio_channel_get(np, index); + chan = fwnode_iio_channel_get(fwnode, index); if (!IS_ERR(chan) || PTR_ERR(chan) == -EPROBE_DEFER) return chan; if (name) { if (index >= 0) { - pr_err("ERROR: could not get IIO channel %pOF:%s(%i)\n", - np, name, index); + pr_err("ERROR: could not get IIO channel %pfw:%s(%i)\n", + fwnode, name, index); /* * In this case, we found 'name' in 'io-channel-names' * but somehow we still fail so that we should not proceed @@ -243,16 +260,16 @@ static struct iio_channel *__of_iio_channel_get_by_name(struct device_node *np, return ERR_PTR(-EINVAL); } /* - * If index < 0, then of_parse_phandle_with_args() fails - * with -EINVAL which is expected. We should not proceed - * if we get any other error. + * If index < 0, then fwnode_property_get_reference_args() fails + * with -EINVAL or -ENOENT (ACPI case) which is expected. We + * should not proceed if we get any other error. */ - if (PTR_ERR(chan) != -EINVAL) + if (PTR_ERR(chan) != -EINVAL && PTR_ERR(chan) != -ENOENT) return chan; } else if (PTR_ERR(chan) != -ENOENT) { /* * if !name, then we should only proceed the lookup if - * of_parse_phandle_with_args() returns -ENOENT. + * fwnode_property_get_reference_args() returns -ENOENT. */ return chan; } @@ -261,13 +278,14 @@ static struct iio_channel *__of_iio_channel_get_by_name(struct device_node *np, return ERR_PTR(-ENODEV); } -struct iio_channel *of_iio_channel_get_by_name(struct device_node *np, - const char *name) +struct iio_channel *fwnode_iio_channel_get_by_name(struct fwnode_handle *fwnode, + const char *name) { + struct fwnode_handle *parent; struct iio_channel *chan; /* Walk up the tree of devices looking for a matching iio channel */ - chan = __of_iio_channel_get_by_name(np, name); + chan = __fwnode_iio_channel_get_by_name(fwnode, name); if (!IS_ERR(chan) || PTR_ERR(chan) != -ENODEV) return chan; @@ -276,33 +294,34 @@ struct iio_channel *of_iio_channel_get_by_name(struct device_node *np, * If the parent node has a "io-channel-ranges" property, * then we can try one of its channels. */ - np = np->parent; - while (np) { - if (!of_get_property(np, "io-channel-ranges", NULL)) + fwnode_for_each_parent_node(fwnode, parent) { + if (!fwnode_property_present(parent, "io-channel-ranges")) { + fwnode_handle_put(parent); return ERR_PTR(-ENODEV); + } - chan = __of_iio_channel_get_by_name(np, name); - if (!IS_ERR(chan) || PTR_ERR(chan) != -ENODEV) + chan = __fwnode_iio_channel_get_by_name(fwnode, name); + if (!IS_ERR(chan) || PTR_ERR(chan) != -ENODEV) { + fwnode_handle_put(parent); return chan; - - np = np->parent; + } } return ERR_PTR(-ENODEV); } -EXPORT_SYMBOL_GPL(of_iio_channel_get_by_name); +EXPORT_SYMBOL_GPL(fwnode_iio_channel_get_by_name); -static struct iio_channel *of_iio_channel_get_all(struct device *dev) +static struct iio_channel *fwnode_iio_channel_get_all(struct device *dev) { + struct fwnode_handle *fwnode = dev_fwnode(dev); struct iio_channel *chans; int i, mapind, nummaps = 0; int ret; do { - ret = of_parse_phandle_with_args(dev->of_node, - "io-channels", - "#io-channel-cells", - nummaps, NULL); + ret = fwnode_property_get_reference_args(fwnode, "io-channels", + "#io-channel-cells", 0, + nummaps, NULL); if (ret < 0) break; } while (++nummaps); @@ -315,10 +334,9 @@ static struct iio_channel *of_iio_channel_get_all(struct device *dev) if (chans == NULL) return ERR_PTR(-ENOMEM); - /* Search for OF matches */ + /* Search for FW matches */ for (mapind = 0; mapind < nummaps; mapind++) { - ret = __of_iio_channel_get(&chans[mapind], dev->of_node, - mapind); + ret = __fwnode_iio_channel_get(&chans[mapind], fwnode, mapind); if (ret) goto error_free_chans; } @@ -331,15 +349,6 @@ error_free_chans: return ERR_PTR(ret); } -#else /* CONFIG_OF */ - -static inline struct iio_channel *of_iio_channel_get_all(struct device *dev) -{ - return ERR_PTR(-ENODEV); -} - -#endif /* CONFIG_OF */ - static struct iio_channel *iio_channel_get_sys(const char *name, const char *channel_name) { @@ -400,8 +409,8 @@ struct iio_channel *iio_channel_get(struct device *dev, struct iio_channel *channel; if (dev) { - channel = of_iio_channel_get_by_name(dev->of_node, - channel_name); + channel = fwnode_iio_channel_get_by_name(dev_fwnode(dev), + channel_name); if (!IS_ERR(channel) || PTR_ERR(channel) != -ENODEV) return channel; } @@ -442,14 +451,14 @@ struct iio_channel *devm_iio_channel_get(struct device *dev, } EXPORT_SYMBOL_GPL(devm_iio_channel_get); -struct iio_channel *devm_of_iio_channel_get_by_name(struct device *dev, - struct device_node *np, - const char *channel_name) +struct iio_channel *devm_fwnode_iio_channel_get_by_name(struct device *dev, + struct fwnode_handle *fwnode, + const char *channel_name) { struct iio_channel *channel; int ret; - channel = of_iio_channel_get_by_name(np, channel_name); + channel = fwnode_iio_channel_get_by_name(fwnode, channel_name); if (IS_ERR(channel)) return channel; @@ -459,7 +468,7 @@ struct iio_channel *devm_of_iio_channel_get_by_name(struct device *dev, return channel; } -EXPORT_SYMBOL_GPL(devm_of_iio_channel_get_by_name); +EXPORT_SYMBOL_GPL(devm_fwnode_iio_channel_get_by_name); struct iio_channel *iio_channel_get_all(struct device *dev) { @@ -473,7 +482,7 @@ struct iio_channel *iio_channel_get_all(struct device *dev) if (dev == NULL) return ERR_PTR(-EINVAL); - chans = of_iio_channel_get_all(dev); + chans = fwnode_iio_channel_get_all(dev); /* * We only want to carry on if the error is -ENODEV. Anything else * should be reported up the stack. diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h index 5fa5957586cf..2adb1306da3e 100644 --- a/include/linux/iio/consumer.h +++ b/include/linux/iio/consumer.h @@ -7,6 +7,7 @@ #ifndef _IIO_INKERN_CONSUMER_H_ #define _IIO_INKERN_CONSUMER_H_ +#include #include #include @@ -14,6 +15,7 @@ struct iio_dev; struct iio_chan_spec; struct device; struct device_node; +struct fwnode_handle; /** * struct iio_channel - everything needed for a consumer to use a channel @@ -99,26 +101,20 @@ void iio_channel_release_all(struct iio_channel *chan); struct iio_channel *devm_iio_channel_get_all(struct device *dev); /** - * of_iio_channel_get_by_name() - get description of all that is needed to access channel. - * @np: Pointer to consumer device tree node + * fwnode_iio_channel_get_by_name() - get description of all that is needed to access channel. + * @fwnode: Pointer to consumer Firmware node * @consumer_channel: Unique name to identify the channel on the consumer * side. This typically describes the channels use within * the consumer. E.g. 'battery_voltage' */ -#ifdef CONFIG_OF -struct iio_channel *of_iio_channel_get_by_name(struct device_node *np, const char *name); -#else -static inline struct iio_channel * -of_iio_channel_get_by_name(struct device_node *np, const char *name) -{ - return NULL; -} -#endif +struct iio_channel *fwnode_iio_channel_get_by_name(struct fwnode_handle *fwnode, + const char *name); /** - * devm_of_iio_channel_get_by_name() - Resource managed version of of_iio_channel_get_by_name(). + * devm_fwnode_iio_channel_get_by_name() - Resource managed version of + * fwnode_iio_channel_get_by_name(). * @dev: Pointer to consumer device. - * @np: Pointer to consumer device tree node + * @fwnode: Pointer to consumer Firmware node * @consumer_channel: Unique name to identify the channel on the consumer * side. This typically describes the channels use within * the consumer. E.g. 'battery_voltage' @@ -129,9 +125,17 @@ of_iio_channel_get_by_name(struct device_node *np, const char *name) * The allocated iio channel is automatically released when the device is * unbound. */ -struct iio_channel *devm_of_iio_channel_get_by_name(struct device *dev, - struct device_node *np, - const char *consumer_channel); +struct iio_channel *devm_fwnode_iio_channel_get_by_name(struct device *dev, + struct fwnode_handle *fwnode, + const char *consumer_channel); + +static inline struct iio_channel +*devm_of_iio_channel_get_by_name(struct device *dev, struct device_node *np, + const char *consumer_channel) +{ + return devm_fwnode_iio_channel_get_by_name(dev, of_fwnode_handle(np), + consumer_channel); +} struct iio_cb_buffer; /** diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 5dfbfc991c69..6002f8a0baf1 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -18,6 +18,7 @@ */ struct of_phandle_args; +struct fwnode_reference_args; enum iio_shared_by { IIO_SEPARATE, @@ -429,6 +430,8 @@ struct iio_trigger; /* forward declaration */ * provide a custom of_xlate function that reads the * *args* and returns the appropriate index in registered * IIO channels array. + * @fwnode_xlate: fwnode based function pointer to obtain channel specifier index. + * Functionally the same as @of_xlate. * @hwfifo_set_watermark: function pointer to set the current hardware * fifo watermark level; see hwfifo_* entries in * Documentation/ABI/testing/sysfs-bus-iio for details on @@ -510,6 +513,8 @@ struct iio_info { unsigned *readval); int (*of_xlate)(struct iio_dev *indio_dev, const struct of_phandle_args *iiospec); + int (*fwnode_xlate)(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec); int (*hwfifo_set_watermark)(struct iio_dev *indio_dev, unsigned val); int (*hwfifo_flush_to_buffer)(struct iio_dev *indio_dev, unsigned count); -- cgit v1.2.3 From 17fe12a2fe2dc95f22efa388d60b396ca134c1ee Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Fri, 15 Jul 2022 14:28:54 +0200 Subject: thermal: qcom: qcom-spmi-adc-tm5: convert to IIO fwnode API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make usage of the new firmware agnostic API 'devm_of_iio_channel_get_by_name()' to get the IIO channel. Signed-off-by: Nuno Sá Reviewed-by: Andy Shevchenko Acked-by: Daniel Lezcano Link: https://lore.kernel.org/r/20220715122903.332535-7-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/thermal/qcom/qcom-spmi-adc-tm5.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/qcom/qcom-spmi-adc-tm5.c b/drivers/thermal/qcom/qcom-spmi-adc-tm5.c index 073943cbcc2b..0a78053cb798 100644 --- a/drivers/thermal/qcom/qcom-spmi-adc-tm5.c +++ b/drivers/thermal/qcom/qcom-spmi-adc-tm5.c @@ -830,7 +830,8 @@ static int adc_tm5_get_dt_channel_data(struct adc_tm5_chip *adc_tm, } channel->adc_channel = args.args[0]; - channel->iio = devm_of_iio_channel_get_by_name(adc_tm->dev, node, NULL); + channel->iio = devm_fwnode_iio_channel_get_by_name(adc_tm->dev, + of_fwnode_handle(node), NULL); if (IS_ERR(channel->iio)) { ret = PTR_ERR(channel->iio); if (ret != -EPROBE_DEFER) -- cgit v1.2.3 From 9ac075972bd25c83a922d3b9548cbe952248bd8f Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Fri, 15 Jul 2022 14:28:55 +0200 Subject: iio: adc: ingenic-adc: convert to IIO fwnode interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move from 'of_xlate()' to 'fwnode_xlate()'. The end goal is to completely drop OF from the IIO inkernel interface. Signed-off-by: Nuno Sá Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220715122903.332535-8-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ingenic-adc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/ingenic-adc.c b/drivers/iio/adc/ingenic-adc.c index bf5c03c34f84..9e08f3abeea6 100644 --- a/drivers/iio/adc/ingenic-adc.c +++ b/drivers/iio/adc/ingenic-adc.c @@ -719,12 +719,12 @@ static int ingenic_adc_read_raw(struct iio_dev *iio_dev, } } -static int ingenic_adc_of_xlate(struct iio_dev *iio_dev, - const struct of_phandle_args *iiospec) +static int ingenic_adc_fwnode_xlate(struct iio_dev *iio_dev, + const struct fwnode_reference_args *iiospec) { int i; - if (!iiospec->args_count) + if (!iiospec->nargs) return -EINVAL; for (i = 0; i < iio_dev->num_channels; ++i) @@ -743,7 +743,7 @@ static const struct iio_info ingenic_adc_info = { .write_raw = ingenic_adc_write_raw, .read_raw = ingenic_adc_read_raw, .read_avail = ingenic_adc_read_avail, - .of_xlate = ingenic_adc_of_xlate, + .fwnode_xlate = ingenic_adc_fwnode_xlate, }; static int ingenic_adc_buffer_enable(struct iio_dev *iio_dev) -- cgit v1.2.3 From dec7e2c83e678ed0c4cab56c1a099bd39ebfa408 Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Fri, 15 Jul 2022 14:28:56 +0200 Subject: iio: adc: ab8500-gpadc: convert to device properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the conversion to firmware agnostic device properties. As part of the conversion the IIO inkern interface 'of_xlate()' is also converted to 'fwnode_xlate()'. The goal is to completely drop 'of_xlate' and hence OF dependencies from IIO. Signed-off-by: Nuno Sá Acked-by: Linus Walleij Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220715122903.332535-9-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ab8500-gpadc.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/drivers/iio/adc/ab8500-gpadc.c b/drivers/iio/adc/ab8500-gpadc.c index 930ce96e6ff5..4fa2126a354b 100644 --- a/drivers/iio/adc/ab8500-gpadc.c +++ b/drivers/iio/adc/ab8500-gpadc.c @@ -925,8 +925,8 @@ static int ab8500_gpadc_read_raw(struct iio_dev *indio_dev, return -EINVAL; } -static int ab8500_gpadc_of_xlate(struct iio_dev *indio_dev, - const struct of_phandle_args *iiospec) +static int ab8500_gpadc_fwnode_xlate(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec) { int i; @@ -938,7 +938,7 @@ static int ab8500_gpadc_of_xlate(struct iio_dev *indio_dev, } static const struct iio_info ab8500_gpadc_info = { - .of_xlate = ab8500_gpadc_of_xlate, + .fwnode_xlate = ab8500_gpadc_fwnode_xlate, .read_raw = ab8500_gpadc_read_raw, }; @@ -968,7 +968,7 @@ static int ab8500_gpadc_runtime_resume(struct device *dev) /** * ab8500_gpadc_parse_channel() - process devicetree channel configuration * @dev: pointer to containing device - * @np: device tree node for the channel to configure + * @fwnode: fw node for the channel to configure * @ch: channel info to fill in * @iio_chan: IIO channel specification to fill in * @@ -976,15 +976,15 @@ static int ab8500_gpadc_runtime_resume(struct device *dev) * and define usage for things like AUX GPADC inputs more precisely. */ static int ab8500_gpadc_parse_channel(struct device *dev, - struct device_node *np, + struct fwnode_handle *fwnode, struct ab8500_gpadc_chan_info *ch, struct iio_chan_spec *iio_chan) { - const char *name = np->name; + const char *name = fwnode_get_name(fwnode); u32 chan; int ret; - ret = of_property_read_u32(np, "reg", &chan); + ret = fwnode_property_read_u32(fwnode, "reg", &chan); if (ret) { dev_err(dev, "invalid channel number %s\n", name); return ret; @@ -1021,22 +1021,20 @@ static int ab8500_gpadc_parse_channel(struct device *dev, /** * ab8500_gpadc_parse_channels() - Parse the GPADC channels from DT * @gpadc: the GPADC to configure the channels for - * @np: device tree node containing the channel configurations * @chans: the IIO channels we parsed * @nchans: the number of IIO channels we parsed */ static int ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc, - struct device_node *np, struct iio_chan_spec **chans_parsed, unsigned int *nchans_parsed) { - struct device_node *child; + struct fwnode_handle *child; struct ab8500_gpadc_chan_info *ch; struct iio_chan_spec *iio_chans; unsigned int nchans; int i; - nchans = of_get_available_child_count(np); + nchans = device_get_child_node_count(gpadc->dev); if (!nchans) { dev_err(gpadc->dev, "no channel children\n"); return -ENODEV; @@ -1054,7 +1052,7 @@ static int ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc, return -ENOMEM; i = 0; - for_each_available_child_of_node(np, child) { + device_for_each_child_node(gpadc->dev, child) { struct iio_chan_spec *iio_chan; int ret; @@ -1064,7 +1062,7 @@ static int ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc, ret = ab8500_gpadc_parse_channel(gpadc->dev, child, ch, iio_chan); if (ret) { - of_node_put(child); + fwnode_handle_put(child); return ret; } i++; @@ -1081,7 +1079,6 @@ static int ab8500_gpadc_probe(struct platform_device *pdev) struct ab8500_gpadc *gpadc; struct iio_dev *indio_dev; struct device *dev = &pdev->dev; - struct device_node *np = pdev->dev.of_node; struct iio_chan_spec *iio_chans; unsigned int n_iio_chans; int ret; @@ -1096,7 +1093,7 @@ static int ab8500_gpadc_probe(struct platform_device *pdev) gpadc->dev = dev; gpadc->ab8500 = dev_get_drvdata(dev->parent); - ret = ab8500_gpadc_parse_channels(gpadc, np, &iio_chans, &n_iio_chans); + ret = ab8500_gpadc_parse_channels(gpadc, &iio_chans, &n_iio_chans); if (ret) return ret; -- cgit v1.2.3 From 34b6eb89351bc281d5323c119e2deb84273d4266 Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Fri, 15 Jul 2022 14:28:57 +0200 Subject: iio: adc: at91-sama5d2_adc: convert to device properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the conversion to firmware agnostic device properties. As part of the conversion the IIO inkern interface 'of_xlate()' is also converted to 'fwnode_xlate()'. The goal is to completely drop 'of_xlate' and hence OF dependencies from IIO. Signed-off-by: Nuno Sá Reviewed-by: Andy Shevchenko Reviewed-by: Claudiu Beznea Link: https://lore.kernel.org/r/20220715122903.332535-10-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 8d8aa9691499..4294d6539cdb 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -16,8 +16,9 @@ #include #include #include -#include +#include #include +#include #include #include #include @@ -763,8 +764,8 @@ at91_adc_chan_get(struct iio_dev *indio_dev, int chan) return indio_dev->channels + index; } -static inline int at91_adc_of_xlate(struct iio_dev *indio_dev, - const struct of_phandle_args *iiospec) +static inline int at91_adc_fwnode_xlate(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec) { return at91_adc_chan_xlate(indio_dev, iiospec->args[0]); } @@ -2213,7 +2214,7 @@ static const struct iio_info at91_adc_info = { .read_raw = &at91_adc_read_raw, .write_raw = &at91_adc_write_raw, .update_scan_mode = &at91_adc_update_scan_mode, - .of_xlate = &at91_adc_of_xlate, + .fwnode_xlate = &at91_adc_fwnode_xlate, .hwfifo_set_watermark = &at91_adc_set_watermark, }; @@ -2306,6 +2307,7 @@ free_buf: static int at91_adc_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct iio_dev *indio_dev; struct at91_adc_state *st; struct resource *res; @@ -2319,7 +2321,7 @@ static int at91_adc_probe(struct platform_device *pdev) st = iio_priv(indio_dev); st->indio_dev = indio_dev; - st->soc_info.platform = of_device_get_match_data(&pdev->dev); + st->soc_info.platform = device_get_match_data(dev); ret = at91_adc_temp_sensor_init(st, &pdev->dev); /* Don't register temperature channel if initialization failed. */ @@ -2343,34 +2345,32 @@ static int at91_adc_probe(struct platform_device *pdev) st->oversampling_ratio = 1; - ret = of_property_read_u32(pdev->dev.of_node, - "atmel,min-sample-rate-hz", - &st->soc_info.min_sample_rate); + ret = device_property_read_u32(dev, "atmel,min-sample-rate-hz", + &st->soc_info.min_sample_rate); if (ret) { dev_err(&pdev->dev, "invalid or missing value for atmel,min-sample-rate-hz\n"); return ret; } - ret = of_property_read_u32(pdev->dev.of_node, - "atmel,max-sample-rate-hz", - &st->soc_info.max_sample_rate); + ret = device_property_read_u32(dev, "atmel,max-sample-rate-hz", + &st->soc_info.max_sample_rate); if (ret) { dev_err(&pdev->dev, "invalid or missing value for atmel,max-sample-rate-hz\n"); return ret; } - ret = of_property_read_u32(pdev->dev.of_node, "atmel,startup-time-ms", - &st->soc_info.startup_time); + ret = device_property_read_u32(dev, "atmel,startup-time-ms", + &st->soc_info.startup_time); if (ret) { dev_err(&pdev->dev, "invalid or missing value for atmel,startup-time-ms\n"); return ret; } - ret = of_property_read_u32(pdev->dev.of_node, - "atmel,trigger-edge-type", &edge_type); + ret = device_property_read_u32(dev, "atmel,trigger-edge-type", + &edge_type); if (ret) { dev_dbg(&pdev->dev, "atmel,trigger-edge-type not specified, only software trigger available\n"); -- cgit v1.2.3 From 9e90c1772fcb8fe3bf39c112b8ef6852e48dbc71 Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Fri, 15 Jul 2022 14:28:58 +0200 Subject: iio: adc: qcom-pm8xxx-xoadc: convert to device properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the conversion to firmware agnostic device properties. As part of the conversion the IIO inkern interface 'of_xlate()' is also converted to 'fwnode_xlate()'. The goal is to completely drop 'of_xlate' and hence OF dependencies from IIO. Signed-off-by: Nuno Sá Acked-by: Linus Walleij Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220715122903.332535-11-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/qcom-pm8xxx-xoadc.c | 58 ++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c index 5e9e56821075..eb424496ee1d 100644 --- a/drivers/iio/adc/qcom-pm8xxx-xoadc.c +++ b/drivers/iio/adc/qcom-pm8xxx-xoadc.c @@ -14,9 +14,9 @@ #include #include #include -#include -#include +#include #include +#include #include #include #include @@ -694,8 +694,8 @@ static int pm8xxx_read_raw(struct iio_dev *indio_dev, } } -static int pm8xxx_of_xlate(struct iio_dev *indio_dev, - const struct of_phandle_args *iiospec) +static int pm8xxx_fwnode_xlate(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec) { struct pm8xxx_xoadc *adc = iio_priv(indio_dev); u8 pre_scale_mux; @@ -706,10 +706,10 @@ static int pm8xxx_of_xlate(struct iio_dev *indio_dev, * First cell is prescaler or premux, second cell is analog * mux. */ - if (iiospec->args_count != 2) { - dev_err(&indio_dev->dev, "wrong number of arguments for %pOFn need 2 got %d\n", - iiospec->np, - iiospec->args_count); + if (iiospec->nargs != 2) { + dev_err(&indio_dev->dev, "wrong number of arguments for %pfwP need 2 got %d\n", + iiospec->fwnode, + iiospec->nargs); return -EINVAL; } pre_scale_mux = (u8)iiospec->args[0]; @@ -727,34 +727,34 @@ static int pm8xxx_of_xlate(struct iio_dev *indio_dev, } static const struct iio_info pm8xxx_xoadc_info = { - .of_xlate = pm8xxx_of_xlate, + .fwnode_xlate = pm8xxx_fwnode_xlate, .read_raw = pm8xxx_read_raw, }; static int pm8xxx_xoadc_parse_channel(struct device *dev, - struct device_node *np, + struct fwnode_handle *fwnode, const struct xoadc_channel *hw_channels, struct iio_chan_spec *iio_chan, struct pm8xxx_chan_info *ch) { - const char *name = np->name; + const char *name = fwnode_get_name(fwnode); const struct xoadc_channel *hwchan; - u32 pre_scale_mux, amux_channel; + u32 pre_scale_mux, amux_channel, reg[2]; u32 rsv, dec; int ret; int chid; - ret = of_property_read_u32_index(np, "reg", 0, &pre_scale_mux); + ret = fwnode_property_read_u32_array(fwnode, "reg", reg, + ARRAY_SIZE(reg)); if (ret) { - dev_err(dev, "invalid pre scale/mux number %s\n", name); - return ret; - } - ret = of_property_read_u32_index(np, "reg", 1, &amux_channel); - if (ret) { - dev_err(dev, "invalid amux channel number %s\n", name); + dev_err(dev, "invalid pre scale/mux or amux channel number %s\n", + name); return ret; } + pre_scale_mux = reg[0]; + amux_channel = reg[1]; + /* Find the right channel setting */ chid = 0; hwchan = &hw_channels[0]; @@ -778,7 +778,7 @@ static int pm8xxx_xoadc_parse_channel(struct device *dev, /* Everyone seems to use default ("type 2") decimation */ ch->decimation = VADC_DEF_DECIMATION; - if (!of_property_read_u32(np, "qcom,ratiometric", &rsv)) { + if (!fwnode_property_read_u32(fwnode, "qcom,ratiometric", &rsv)) { ch->calibration = VADC_CALIB_RATIOMETRIC; if (rsv > XOADC_RSV_MAX) { dev_err(dev, "%s too large RSV value %d\n", name, rsv); @@ -791,7 +791,7 @@ static int pm8xxx_xoadc_parse_channel(struct device *dev, } /* Optional decimation, if omitted we use the default */ - ret = of_property_read_u32(np, "qcom,decimation", &dec); + ret = fwnode_property_read_u32(fwnode, "qcom,decimation", &dec); if (!ret) { ret = qcom_vadc_decimation_from_dt(dec); if (ret < 0) { @@ -820,15 +820,14 @@ static int pm8xxx_xoadc_parse_channel(struct device *dev, return 0; } -static int pm8xxx_xoadc_parse_channels(struct pm8xxx_xoadc *adc, - struct device_node *np) +static int pm8xxx_xoadc_parse_channels(struct pm8xxx_xoadc *adc) { - struct device_node *child; + struct fwnode_handle *child; struct pm8xxx_chan_info *ch; int ret; int i; - adc->nchans = of_get_available_child_count(np); + adc->nchans = device_get_child_node_count(adc->dev); if (!adc->nchans) { dev_err(adc->dev, "no channel children\n"); return -ENODEV; @@ -846,14 +845,14 @@ static int pm8xxx_xoadc_parse_channels(struct pm8xxx_xoadc *adc, return -ENOMEM; i = 0; - for_each_available_child_of_node(np, child) { + device_for_each_child_node(adc->dev, child) { ch = &adc->chans[i]; ret = pm8xxx_xoadc_parse_channel(adc->dev, child, adc->variant->channels, &adc->iio_chans[i], ch); if (ret) { - of_node_put(child); + fwnode_handle_put(child); return ret; } i++; @@ -884,12 +883,11 @@ static int pm8xxx_xoadc_probe(struct platform_device *pdev) const struct xoadc_variant *variant; struct pm8xxx_xoadc *adc; struct iio_dev *indio_dev; - struct device_node *np = pdev->dev.of_node; struct regmap *map; struct device *dev = &pdev->dev; int ret; - variant = of_device_get_match_data(dev); + variant = device_get_match_data(dev); if (!variant) return -ENODEV; @@ -904,7 +902,7 @@ static int pm8xxx_xoadc_probe(struct platform_device *pdev) init_completion(&adc->complete); mutex_init(&adc->lock); - ret = pm8xxx_xoadc_parse_channels(adc, np); + ret = pm8xxx_xoadc_parse_channels(adc); if (ret) return ret; -- cgit v1.2.3 From e7c672d06b0777342672ee125548310d433855fc Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Fri, 15 Jul 2022 14:28:59 +0200 Subject: iio: adc: qcom-spmi-vadc: convert to device properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the conversion to firmware agnostic device properties. As part of the conversion the IIO inkern interface 'of_xlate()' is also converted to 'fwnode_xlate()'. The goal is to completely drop 'of_xlate' and hence OF dependencies from IIO. Signed-off-by: Nuno Sá Acked-by: Linus Walleij Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220715122903.332535-12-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/qcom-spmi-vadc.c | 44 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/iio/adc/qcom-spmi-vadc.c b/drivers/iio/adc/qcom-spmi-vadc.c index 34202ba52469..bcff0f62b70e 100644 --- a/drivers/iio/adc/qcom-spmi-vadc.c +++ b/drivers/iio/adc/qcom-spmi-vadc.c @@ -13,8 +13,9 @@ #include #include #include -#include +#include #include +#include #include #include #include @@ -481,8 +482,8 @@ static int vadc_read_raw(struct iio_dev *indio_dev, return ret; } -static int vadc_of_xlate(struct iio_dev *indio_dev, - const struct of_phandle_args *iiospec) +static int vadc_fwnode_xlate(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec) { struct vadc_priv *vadc = iio_priv(indio_dev); unsigned int i; @@ -496,7 +497,7 @@ static int vadc_of_xlate(struct iio_dev *indio_dev, static const struct iio_info vadc_info = { .read_raw = vadc_read_raw, - .of_xlate = vadc_of_xlate, + .fwnode_xlate = vadc_fwnode_xlate, }; struct vadc_channels { @@ -647,15 +648,15 @@ static const struct vadc_channels vadc_chans[] = { VADC_CHAN_NO_SCALE(LR_MUX3_BUF_PU1_PU2_XO_THERM, 0) }; -static int vadc_get_dt_channel_data(struct device *dev, +static int vadc_get_fw_channel_data(struct device *dev, struct vadc_channel_prop *prop, - struct device_node *node) + struct fwnode_handle *fwnode) { - const char *name = node->name; + const char *name = fwnode_get_name(fwnode); u32 chan, value, varr[2]; int ret; - ret = of_property_read_u32(node, "reg", &chan); + ret = fwnode_property_read_u32(fwnode, "reg", &chan); if (ret) { dev_err(dev, "invalid channel number %s\n", name); return ret; @@ -669,7 +670,7 @@ static int vadc_get_dt_channel_data(struct device *dev, /* the channel has DT description */ prop->channel = chan; - ret = of_property_read_u32(node, "qcom,decimation", &value); + ret = fwnode_property_read_u32(fwnode, "qcom,decimation", &value); if (!ret) { ret = qcom_vadc_decimation_from_dt(value); if (ret < 0) { @@ -682,7 +683,7 @@ static int vadc_get_dt_channel_data(struct device *dev, prop->decimation = VADC_DEF_DECIMATION; } - ret = of_property_read_u32_array(node, "qcom,pre-scaling", varr, 2); + ret = fwnode_property_read_u32_array(fwnode, "qcom,pre-scaling", varr, 2); if (!ret) { ret = vadc_prescaling_from_dt(varr[0], varr[1]); if (ret < 0) { @@ -695,7 +696,7 @@ static int vadc_get_dt_channel_data(struct device *dev, prop->prescale = vadc_chans[prop->channel].prescale_index; } - ret = of_property_read_u32(node, "qcom,hw-settle-time", &value); + ret = fwnode_property_read_u32(fwnode, "qcom,hw-settle-time", &value); if (!ret) { ret = vadc_hw_settle_time_from_dt(value); if (ret < 0) { @@ -708,7 +709,7 @@ static int vadc_get_dt_channel_data(struct device *dev, prop->hw_settle_time = VADC_DEF_HW_SETTLE_TIME; } - ret = of_property_read_u32(node, "qcom,avg-samples", &value); + ret = fwnode_property_read_u32(fwnode, "qcom,avg-samples", &value); if (!ret) { ret = vadc_avg_samples_from_dt(value); if (ret < 0) { @@ -721,7 +722,7 @@ static int vadc_get_dt_channel_data(struct device *dev, prop->avg_samples = VADC_DEF_AVG_SAMPLES; } - if (of_property_read_bool(node, "qcom,ratiometric")) + if (fwnode_property_read_bool(fwnode, "qcom,ratiometric")) prop->calibration = VADC_CALIB_RATIOMETRIC; else prop->calibration = VADC_CALIB_ABSOLUTE; @@ -731,16 +732,16 @@ static int vadc_get_dt_channel_data(struct device *dev, return 0; } -static int vadc_get_dt_data(struct vadc_priv *vadc, struct device_node *node) +static int vadc_get_fw_data(struct vadc_priv *vadc) { const struct vadc_channels *vadc_chan; struct iio_chan_spec *iio_chan; struct vadc_channel_prop prop; - struct device_node *child; + struct fwnode_handle *child; unsigned int index = 0; int ret; - vadc->nchannels = of_get_available_child_count(node); + vadc->nchannels = device_get_child_node_count(vadc->dev); if (!vadc->nchannels) return -EINVAL; @@ -756,10 +757,10 @@ static int vadc_get_dt_data(struct vadc_priv *vadc, struct device_node *node) iio_chan = vadc->iio_chans; - for_each_available_child_of_node(node, child) { - ret = vadc_get_dt_channel_data(vadc->dev, &prop, child); + device_for_each_child_node(vadc->dev, child) { + ret = vadc_get_fw_channel_data(vadc->dev, &prop, child); if (ret) { - of_node_put(child); + fwnode_handle_put(child); return ret; } @@ -848,7 +849,6 @@ static int vadc_check_revision(struct vadc_priv *vadc) static int vadc_probe(struct platform_device *pdev) { - struct device_node *node = pdev->dev.of_node; struct device *dev = &pdev->dev; struct iio_dev *indio_dev; struct vadc_priv *vadc; @@ -860,7 +860,7 @@ static int vadc_probe(struct platform_device *pdev) if (!regmap) return -ENODEV; - ret = of_property_read_u32(node, "reg", ®); + ret = device_property_read_u32(dev, "reg", ®); if (ret < 0) return ret; @@ -880,7 +880,7 @@ static int vadc_probe(struct platform_device *pdev) if (ret) return ret; - ret = vadc_get_dt_data(vadc, node); + ret = vadc_get_fw_data(vadc); if (ret) return ret; -- cgit v1.2.3 From 4f47a236a23d9f18d018a2f6639daad476d2a3f2 Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Fri, 15 Jul 2022 14:29:00 +0200 Subject: iio: adc: qcom-spmi-adc5: convert to device properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the conversion to firmware agnostic device properties. As part of the conversion the IIO inkern interface 'of_xlate()' is also converted to 'fwnode_xlate()'. The goal is to completely drop 'of_xlate' and hence OF dependencies from IIO. Signed-off-by: Nuno Sá Acked-by: Linus Walleij Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220715122903.332535-13-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/qcom-spmi-adc5.c | 63 +++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c index 0dc4fe612433..e96da2ef1964 100644 --- a/drivers/iio/adc/qcom-spmi-adc5.c +++ b/drivers/iio/adc/qcom-spmi-adc5.c @@ -14,9 +14,9 @@ #include #include #include -#include -#include +#include #include +#include #include #include @@ -403,8 +403,8 @@ static irqreturn_t adc5_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static int adc5_of_xlate(struct iio_dev *indio_dev, - const struct of_phandle_args *iiospec) +static int adc5_fwnode_xlate(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec) { struct adc5_chip *adc = iio_priv(indio_dev); int i; @@ -416,8 +416,8 @@ static int adc5_of_xlate(struct iio_dev *indio_dev, return -EINVAL; } -static int adc7_of_xlate(struct iio_dev *indio_dev, - const struct of_phandle_args *iiospec) +static int adc7_fwnode_xlate(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec) { struct adc5_chip *adc = iio_priv(indio_dev); int i, v_channel; @@ -481,12 +481,12 @@ static int adc7_read_raw(struct iio_dev *indio_dev, static const struct iio_info adc5_info = { .read_raw = adc5_read_raw, - .of_xlate = adc5_of_xlate, + .fwnode_xlate = adc5_fwnode_xlate, }; static const struct iio_info adc7_info = { .read_raw = adc7_read_raw, - .of_xlate = adc7_of_xlate, + .fwnode_xlate = adc7_fwnode_xlate, }; struct adc5_channels { @@ -619,18 +619,18 @@ static const struct adc5_channels adc5_chans_rev2[ADC5_MAX_CHANNEL] = { SCALE_HW_CALIB_THERM_100K_PULLUP) }; -static int adc5_get_dt_channel_data(struct adc5_chip *adc, +static int adc5_get_fw_channel_data(struct adc5_chip *adc, struct adc5_channel_prop *prop, - struct device_node *node, + struct fwnode_handle *fwnode, const struct adc5_data *data) { - const char *name = node->name, *channel_name; + const char *name = fwnode_get_name(fwnode), *channel_name; u32 chan, value, varr[2]; u32 sid = 0; int ret; struct device *dev = adc->dev; - ret = of_property_read_u32(node, "reg", &chan); + ret = fwnode_property_read_u32(fwnode, "reg", &chan); if (ret) { dev_err(dev, "invalid channel number %s\n", name); return ret; @@ -655,15 +655,13 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc, prop->channel = chan; prop->sid = sid; - channel_name = of_get_property(node, - "label", NULL) ? : node->name; - if (!channel_name) { - dev_err(dev, "Invalid channel name\n"); - return -EINVAL; - } + ret = fwnode_property_read_string(fwnode, "label", &channel_name); + if (ret) + channel_name = name; + prop->datasheet_name = channel_name; - ret = of_property_read_u32(node, "qcom,decimation", &value); + ret = fwnode_property_read_u32(fwnode, "qcom,decimation", &value); if (!ret) { ret = qcom_adc5_decimation_from_dt(value, data->decimation); if (ret < 0) { @@ -676,7 +674,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc, prop->decimation = ADC5_DECIMATION_DEFAULT; } - ret = of_property_read_u32_array(node, "qcom,pre-scaling", varr, 2); + ret = fwnode_property_read_u32_array(fwnode, "qcom,pre-scaling", varr, 2); if (!ret) { ret = qcom_adc5_prescaling_from_dt(varr[0], varr[1]); if (ret < 0) { @@ -690,7 +688,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc, adc->data->adc_chans[prop->channel].prescale_index; } - ret = of_property_read_u32(node, "qcom,hw-settle-time", &value); + ret = fwnode_property_read_u32(fwnode, "qcom,hw-settle-time", &value); if (!ret) { u8 dig_version[2]; @@ -721,7 +719,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc, prop->hw_settle_time = VADC_DEF_HW_SETTLE_TIME; } - ret = of_property_read_u32(node, "qcom,avg-samples", &value); + ret = fwnode_property_read_u32(fwnode, "qcom,avg-samples", &value); if (!ret) { ret = qcom_adc5_avg_samples_from_dt(value); if (ret < 0) { @@ -734,7 +732,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc, prop->avg_samples = VADC_DEF_AVG_SAMPLES; } - if (of_property_read_bool(node, "qcom,ratiometric")) + if (fwnode_property_read_bool(fwnode, "qcom,ratiometric")) prop->cal_method = ADC5_RATIOMETRIC_CAL; else prop->cal_method = ADC5_ABSOLUTE_CAL; @@ -809,16 +807,16 @@ static const struct of_device_id adc5_match_table[] = { }; MODULE_DEVICE_TABLE(of, adc5_match_table); -static int adc5_get_dt_data(struct adc5_chip *adc, struct device_node *node) +static int adc5_get_fw_data(struct adc5_chip *adc) { const struct adc5_channels *adc_chan; struct iio_chan_spec *iio_chan; struct adc5_channel_prop prop, *chan_props; - struct device_node *child; + struct fwnode_handle *child; unsigned int index = 0; int ret; - adc->nchannels = of_get_available_child_count(node); + adc->nchannels = device_get_child_node_count(adc->dev); if (!adc->nchannels) return -EINVAL; @@ -834,14 +832,14 @@ static int adc5_get_dt_data(struct adc5_chip *adc, struct device_node *node) chan_props = adc->chan_props; iio_chan = adc->iio_chans; - adc->data = of_device_get_match_data(adc->dev); + adc->data = device_get_match_data(adc->dev); if (!adc->data) adc->data = &adc5_data_pmic; - for_each_available_child_of_node(node, child) { - ret = adc5_get_dt_channel_data(adc, &prop, child, adc->data); + device_for_each_child_node(adc->dev, child) { + ret = adc5_get_fw_channel_data(adc, &prop, child, adc->data); if (ret) { - of_node_put(child); + fwnode_handle_put(child); return ret; } @@ -866,7 +864,6 @@ static int adc5_get_dt_data(struct adc5_chip *adc, struct device_node *node) static int adc5_probe(struct platform_device *pdev) { - struct device_node *node = pdev->dev.of_node; struct device *dev = &pdev->dev; struct iio_dev *indio_dev; struct adc5_chip *adc; @@ -878,7 +875,7 @@ static int adc5_probe(struct platform_device *pdev) if (!regmap) return -ENODEV; - ret = of_property_read_u32(node, "reg", ®); + ret = device_property_read_u32(dev, "reg", ®); if (ret < 0) return ret; @@ -894,7 +891,7 @@ static int adc5_probe(struct platform_device *pdev) init_completion(&adc->complete); mutex_init(&adc->lock); - ret = adc5_get_dt_data(adc, node); + ret = adc5_get_fw_data(adc); if (ret) { dev_err(dev, "adc get dt data failed\n"); return ret; -- cgit v1.2.3 From d7705f35448ada5a04f15326404e40d4254c538d Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Fri, 15 Jul 2022 14:29:01 +0200 Subject: iio: adc: stm32-adc: convert to device properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the conversion to firmware agnostic device properties. As part of the conversion the IIO inkern interface 'of_xlate()' is also converted to 'fwnode_xlate()'. The goal is to completely drop 'of_xlate' and hence OF dependencies from IIO. Signed-off-by: Nuno Sá Reviewed-by: Andy Shevchenko Reviewed-by: Fabrice Gasnier Tested-by: Fabrice Gasnier Link: https://lore.kernel.org/r/20220715122903.332535-14-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc.c | 128 +++++++++++++++++++++++++------------------- 1 file changed, 73 insertions(+), 55 deletions(-) diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 130e8dd6f0c8..6256977eb7f7 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -21,11 +21,11 @@ #include #include #include +#include #include #include #include -#include -#include +#include #include "stm32-adc-core.h" @@ -241,6 +241,7 @@ struct stm32_adc_cfg { * @chan_name: channel name array * @num_diff: number of differential channels * @int_ch: internal channel indexes array + * @nsmps: number of channels with optional sample time */ struct stm32_adc { struct stm32_adc_common *common; @@ -267,6 +268,7 @@ struct stm32_adc { char chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ]; u32 num_diff; int int_ch[STM32_ADC_INT_CH_NB]; + int nsmps; }; struct stm32_adc_diff_channel { @@ -1520,8 +1522,8 @@ static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev, return ret; } -static int stm32_adc_of_xlate(struct iio_dev *indio_dev, - const struct of_phandle_args *iiospec) +static int stm32_adc_fwnode_xlate(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec) { int i; @@ -1575,7 +1577,7 @@ static const struct iio_info stm32_adc_iio_info = { .hwfifo_set_watermark = stm32_adc_set_watermark, .update_scan_mode = stm32_adc_update_scan_mode, .debugfs_reg_access = stm32_adc_debugfs_reg_access, - .of_xlate = stm32_adc_of_xlate, + .fwnode_xlate = stm32_adc_fwnode_xlate, }; static unsigned int stm32_adc_dma_residue(struct stm32_adc *adc) @@ -1772,14 +1774,14 @@ static const struct iio_chan_spec_ext_info stm32_adc_ext_info[] = { {}, }; -static int stm32_adc_of_get_resolution(struct iio_dev *indio_dev) +static int stm32_adc_fw_get_resolution(struct iio_dev *indio_dev) { - struct device_node *node = indio_dev->dev.of_node; + struct device *dev = &indio_dev->dev; struct stm32_adc *adc = iio_priv(indio_dev); unsigned int i; u32 res; - if (of_property_read_u32(node, "assigned-resolution-bits", &res)) + if (device_property_read_u32(dev, "assigned-resolution-bits", &res)) res = adc->cfg->adc_info->resolutions[0]; for (i = 0; i < adc->cfg->adc_info->num_res; i++) @@ -1863,11 +1865,11 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev, 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 device *dev = &indio_dev->dev; const struct stm32_adc_info *adc_info = adc->cfg->adc_info; int num_channels = 0, ret; - ret = of_property_count_u32_elems(node, "st,adc-channels"); + ret = device_property_count_u32(dev, "st,adc-channels"); if (ret > adc_info->max_channels) { dev_err(&indio_dev->dev, "Bad st,adc-channels?\n"); return -EINVAL; @@ -1875,8 +1877,15 @@ static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm num_channels += ret; } - ret = of_property_count_elems_of_size(node, "st,adc-diff-channels", - sizeof(struct stm32_adc_diff_channel)); + /* + * each st,adc-diff-channels is a group of 2 u32 so we divide @ret + * to get the *real* number of channels. + */ + ret = device_property_count_u32(dev, "st,adc-diff-channels"); + if (ret < 0) + return ret; + + ret /= (int)(sizeof(struct stm32_adc_diff_channel) / sizeof(u32)); if (ret > adc_info->max_channels) { dev_err(&indio_dev->dev, "Bad st,adc-diff-channels?\n"); return -EINVAL; @@ -1886,8 +1895,8 @@ static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm } /* 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) { + adc->nsmps = device_property_count_u32(dev, "st,min-sample-time-nsecs"); + if (adc->nsmps > 1 && adc->nsmps != num_channels) { dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n"); return -EINVAL; } @@ -1897,21 +1906,20 @@ static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev, struct stm32_adc *adc, - struct iio_chan_spec *channels) + struct iio_chan_spec *channels, + int nchans) { - 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]; + struct device *dev = &indio_dev->dev; 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; - u32 smp = 0; + int scan_index = 0, ret, i, c; + u32 smp = 0, smps[STM32_ADC_CH_MAX], chans[STM32_ADC_CH_MAX]; if (num_diff) { - ret = of_property_read_u32_array(node, "st,adc-diff-channels", - (u32 *)diff, size); + ret = device_property_read_u32_array(dev, "st,adc-diff-channels", + (u32 *)diff, size); if (ret) { dev_err(&indio_dev->dev, "Failed to get diff channels %d\n", ret); return ret; @@ -1932,32 +1940,47 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev, } } - 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); + ret = device_property_read_u32_array(dev, "st,adc-channels", chans, + nchans); + if (ret) + return ret; + + for (c = 0; c < nchans; c++) { + if (chans[c] >= adc_info->max_channels) { + dev_err(&indio_dev->dev, "Invalid channel %d\n", + chans[c]); return -EINVAL; } /* 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 misconfigured\n", val); + if (chans[c] == diff[i].vinp) { + dev_err(&indio_dev->dev, "channel %d misconfigured\n", chans[c]); return -EINVAL; } } - stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val, - 0, scan_index, false); + stm32_adc_chan_init_one(indio_dev, &channels[scan_index], + chans[c], 0, scan_index, false); scan_index++; } + if (adc->nsmps > 0) { + ret = device_property_read_u32_array(dev, "st,min-sample-time-nsecs", + smps, adc->nsmps); + if (ret) + return 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. + * This check is used with the above logic so that 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. The point is to have the same + * behavior as 'of_property_read_u32_index()'. */ - of_property_read_u32_index(node, "st,min-sample-time-nsecs", i, &smp); + if (i < adc->nsmps) + smp = smps[i]; /* Prepare sampling time settings */ stm32_adc_smpr_init(adc, channels[i].channel, smp); @@ -2005,22 +2028,21 @@ 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; + struct fwnode_handle *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); + device_for_each_child_node(&indio_dev->dev, child) { + ret = fwnode_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); + ret = fwnode_property_read_string(child, "label", &name); /* label is optional */ if (!ret) { if (strlen(name) >= STM32_ADC_CH_SZ) { @@ -2047,7 +2069,7 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev, } differential = false; - ret = of_property_read_u32_array(child, "diff-channels", vin, 2); + ret = fwnode_property_read_u32_array(child, "diff-channels", vin, 2); /* diff-channels is optional */ if (!ret) { differential = true; @@ -2064,7 +2086,7 @@ 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); + ret = fwnode_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); @@ -2082,14 +2104,13 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev, return scan_index; err: - of_node_put(child); + fwnode_handle_put(child); return ret; } -static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) +static int stm32_adc_chan_fw_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; @@ -2099,7 +2120,7 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) 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); + num_channels = device_get_child_node_count(&indio_dev->dev); /* If no channels have been found, fallback to channels legacy properties. */ if (!num_channels) { legacy = true; @@ -2130,7 +2151,8 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) return -ENOMEM; if (legacy) - ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels); + ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels, + num_channels); else ret = stm32_adc_generic_chan_init(indio_dev, adc, channels); if (ret < 0) @@ -2212,9 +2234,6 @@ static int stm32_adc_probe(struct platform_device *pdev) bool timestamping = false; int ret; - if (!pdev->dev.of_node) - return -ENODEV; - indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc)); if (!indio_dev) return -ENOMEM; @@ -2223,17 +2242,16 @@ static int stm32_adc_probe(struct platform_device *pdev) adc->common = dev_get_drvdata(pdev->dev.parent); spin_lock_init(&adc->lock); init_completion(&adc->completion); - adc->cfg = (const struct stm32_adc_cfg *) - of_match_device(dev->driver->of_match_table, dev)->data; + adc->cfg = device_get_match_data(dev); indio_dev->name = dev_name(&pdev->dev); - indio_dev->dev.of_node = pdev->dev.of_node; + device_set_node(&indio_dev->dev, dev_fwnode(&pdev->dev)); indio_dev->info = &stm32_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE | INDIO_HARDWARE_TRIGGERED; platform_set_drvdata(pdev, indio_dev); - ret = of_property_read_u32(pdev->dev.of_node, "reg", &adc->offset); + ret = device_property_read_u32(dev, "reg", &adc->offset); if (ret != 0) { dev_err(&pdev->dev, "missing reg property\n"); return -EINVAL; @@ -2262,7 +2280,7 @@ static int stm32_adc_probe(struct platform_device *pdev) } } - ret = stm32_adc_of_get_resolution(indio_dev); + ret = stm32_adc_fw_get_resolution(indio_dev); if (ret < 0) return ret; @@ -2279,7 +2297,7 @@ static int stm32_adc_probe(struct platform_device *pdev) timestamping = true; } - ret = stm32_adc_chan_of_init(indio_dev, timestamping); + ret = stm32_adc_chan_fw_init(indio_dev, timestamping); if (ret < 0) goto err_dma_disable; -- cgit v1.2.3 From b22bc4d6072e74b768c4c19e2ff3585ba5927904 Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Fri, 15 Jul 2022 14:29:02 +0200 Subject: iio: inkern: remove OF dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since all users of the OF dependendent API are now converted to use the firmware agnostic alternative, we can drop OF dependencies from the IIO in kernel interface. Signed-off-by: Nuno Sá Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220715122903.332535-15-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/inkern.c | 25 +------------------------ include/linux/iio/consumer.h | 10 ---------- include/linux/iio/iio.h | 3 --- 3 files changed, 1 insertion(+), 37 deletions(-) diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 9cfa66ef9536..b667790b6df9 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include @@ -143,26 +142,6 @@ static int __fwnode_iio_simple_xlate(struct iio_dev *indio_dev, return iiospec->args[0]; } -/* - * Simple helper to copy fwnode_reference_args into of_phandle_args so we - * can pass it to of_xlate(). Ultimate goal is to drop this together with - * of_xlate(). - */ -static int __fwnode_to_of_xlate(struct iio_dev *indio_dev, - const struct fwnode_reference_args *iiospec) -{ - struct of_phandle_args of_args; - unsigned int i; - - of_args.args_count = iiospec->nargs; - of_args.np = to_of_node(iiospec->fwnode); - - for (i = 0; i < MAX_PHANDLE_ARGS; i++) - of_args.args[i] = i < iiospec->nargs ? iiospec->args[i] : 0; - - return indio_dev->info->of_xlate(indio_dev, &of_args); -} - static int __fwnode_iio_channel_get(struct iio_channel *channel, struct fwnode_handle *fwnode, int index) { @@ -185,9 +164,7 @@ static int __fwnode_iio_channel_get(struct iio_channel *channel, indio_dev = dev_to_iio_dev(idev); channel->indio_dev = indio_dev; - if (indio_dev->info->of_xlate) - index = __fwnode_to_of_xlate(indio_dev, &iiospec); - else if (indio_dev->info->fwnode_xlate) + if (indio_dev->info->fwnode_xlate) index = indio_dev->info->fwnode_xlate(indio_dev, &iiospec); else index = __fwnode_iio_simple_xlate(indio_dev, &iiospec); diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h index 2adb1306da3e..6802596b017c 100644 --- a/include/linux/iio/consumer.h +++ b/include/linux/iio/consumer.h @@ -7,14 +7,12 @@ #ifndef _IIO_INKERN_CONSUMER_H_ #define _IIO_INKERN_CONSUMER_H_ -#include #include #include struct iio_dev; struct iio_chan_spec; struct device; -struct device_node; struct fwnode_handle; /** @@ -129,14 +127,6 @@ struct iio_channel *devm_fwnode_iio_channel_get_by_name(struct device *dev, struct fwnode_handle *fwnode, const char *consumer_channel); -static inline struct iio_channel -*devm_of_iio_channel_get_by_name(struct device *dev, struct device_node *np, - const char *consumer_channel) -{ - return devm_fwnode_iio_channel_get_by_name(dev, of_fwnode_handle(np), - consumer_channel); -} - struct iio_cb_buffer; /** * iio_channel_get_all_cb() - register callback for triggered capture diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 6002f8a0baf1..f0ec8a5e5a7a 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -17,7 +17,6 @@ * Currently assumes nano seconds. */ -struct of_phandle_args; struct fwnode_reference_args; enum iio_shared_by { @@ -511,8 +510,6 @@ struct iio_info { int (*debugfs_reg_access)(struct iio_dev *indio_dev, unsigned reg, unsigned writeval, unsigned *readval); - int (*of_xlate)(struct iio_dev *indio_dev, - const struct of_phandle_args *iiospec); int (*fwnode_xlate)(struct iio_dev *indio_dev, const struct fwnode_reference_args *iiospec); int (*hwfifo_set_watermark)(struct iio_dev *indio_dev, unsigned val); -- cgit v1.2.3 From 110f11589c877732d8167d4d4ee739a4566785e2 Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Fri, 15 Jul 2022 14:29:03 +0200 Subject: iio: inkern: fix coding style warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just cosmetics. No functional change intended... Signed-off-by: Nuno Sá Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220715122903.332535-16-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/inkern.c | 64 ++++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index b667790b6df9..872fd5c24147 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -45,13 +45,13 @@ int iio_map_array_register(struct iio_dev *indio_dev, struct iio_map *maps) int i = 0, ret = 0; struct iio_map_internal *mapi; - if (maps == NULL) + if (!maps) return 0; mutex_lock(&iio_map_list_lock); - while (maps[i].consumer_dev_name != NULL) { + while (maps[i].consumer_dev_name) { mapi = kzalloc(sizeof(*mapi), GFP_KERNEL); - if (mapi == NULL) { + if (!mapi) { ret = -ENOMEM; goto error_ret; } @@ -69,7 +69,6 @@ error_ret: } EXPORT_SYMBOL_GPL(iio_map_array_register); - /* * Remove all map entries associated with the given iio device */ @@ -157,7 +156,7 @@ static int __fwnode_iio_channel_get(struct iio_channel *channel, return err; idev = bus_find_device_by_fwnode(&iio_bus_type, iiospec.fwnode); - if (idev == NULL) { + if (!idev) { fwnode_handle_put(iiospec.fwnode); return -EPROBE_DEFER; } @@ -190,7 +189,7 @@ static struct iio_channel *fwnode_iio_channel_get(struct fwnode_handle *fwnode, return ERR_PTR(-EINVAL); channel = kzalloc(sizeof(*channel), GFP_KERNEL); - if (channel == NULL) + if (!channel) return ERR_PTR(-ENOMEM); err = __fwnode_iio_channel_get(channel, fwnode, index); @@ -308,7 +307,7 @@ static struct iio_channel *fwnode_iio_channel_get_all(struct device *dev) /* NULL terminated array to save passing size */ chans = kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL); - if (chans == NULL) + if (!chans) return ERR_PTR(-ENOMEM); /* Search for FW matches */ @@ -333,7 +332,7 @@ static struct iio_channel *iio_channel_get_sys(const char *name, struct iio_channel *channel; int err; - if (name == NULL && channel_name == NULL) + if (!(name || channel_name)) return ERR_PTR(-ENODEV); /* first find matching entry the channel map */ @@ -348,11 +347,11 @@ static struct iio_channel *iio_channel_get_sys(const char *name, break; } mutex_unlock(&iio_map_list_lock); - if (c == NULL) + if (!c) return ERR_PTR(-ENODEV); channel = kzalloc(sizeof(*channel), GFP_KERNEL); - if (channel == NULL) { + if (!channel) { err = -ENOMEM; goto error_no_mem; } @@ -364,7 +363,7 @@ static struct iio_channel *iio_channel_get_sys(const char *name, iio_chan_spec_from_name(channel->indio_dev, c->map->adc_channel_label); - if (channel->channel == NULL) { + if (!channel->channel) { err = -EINVAL; goto error_no_chan; } @@ -456,7 +455,7 @@ struct iio_channel *iio_channel_get_all(struct device *dev) int mapind = 0; int i, ret; - if (dev == NULL) + if (!dev) return ERR_PTR(-EINVAL); chans = fwnode_iio_channel_get_all(dev); @@ -484,7 +483,7 @@ struct iio_channel *iio_channel_get_all(struct device *dev) /* NULL terminated array to save passing size */ chans = kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL); - if (chans == NULL) { + if (!chans) { ret = -ENOMEM; goto error_ret; } @@ -498,7 +497,7 @@ struct iio_channel *iio_channel_get_all(struct device *dev) chans[mapind].channel = iio_chan_spec_from_name(chans[mapind].indio_dev, c->map->adc_channel_label); - if (chans[mapind].channel == NULL) { + if (!chans[mapind].channel) { ret = -EINVAL; goto error_free_chans; } @@ -560,14 +559,14 @@ struct iio_channel *devm_iio_channel_get_all(struct device *dev) EXPORT_SYMBOL_GPL(devm_iio_channel_get_all); static int iio_channel_read(struct iio_channel *chan, int *val, int *val2, - enum iio_chan_info_enum info) + enum iio_chan_info_enum info) { int unused; int vals[INDIO_MAX_RAW_ELEMENTS]; int ret; int val_len = 2; - if (val2 == NULL) + if (!val2) val2 = &unused; if (!iio_channel_has_info(chan->channel, info)) @@ -579,9 +578,10 @@ static int iio_channel_read(struct iio_channel *chan, int *val, int *val2, vals, &val_len, info); *val = vals[0]; *val2 = vals[1]; - } else + } else { ret = chan->indio_dev->info->read_raw(chan->indio_dev, chan->channel, val, val2, info); + } return ret; } @@ -592,7 +592,7 @@ int iio_read_channel_raw(struct iio_channel *chan, int *val) int ret; mutex_lock(&iio_dev_opaque->info_exist_lock); - if (chan->indio_dev->info == NULL) { + if (!chan->indio_dev->info) { ret = -ENODEV; goto err_unlock; } @@ -611,7 +611,7 @@ int iio_read_channel_average_raw(struct iio_channel *chan, int *val) int ret; mutex_lock(&iio_dev_opaque->info_exist_lock); - if (chan->indio_dev->info == NULL) { + if (!chan->indio_dev->info) { ret = -ENODEV; goto err_unlock; } @@ -625,7 +625,8 @@ err_unlock: EXPORT_SYMBOL_GPL(iio_read_channel_average_raw); static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan, - int raw, int *processed, unsigned int scale) + int raw, int *processed, + unsigned int scale) { int scale_type, scale_val, scale_val2; int offset_type, offset_val, offset_val2; @@ -658,7 +659,7 @@ static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan, } scale_type = iio_channel_read(chan, &scale_val, &scale_val2, - IIO_CHAN_INFO_SCALE); + IIO_CHAN_INFO_SCALE); if (scale_type < 0) { /* * If no channel scaling is available apply consumer scale to @@ -703,19 +704,19 @@ static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan, } int iio_convert_raw_to_processed(struct iio_channel *chan, int raw, - int *processed, unsigned int scale) + int *processed, unsigned int scale) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); int ret; mutex_lock(&iio_dev_opaque->info_exist_lock); - if (chan->indio_dev->info == NULL) { + if (!chan->indio_dev->info) { ret = -ENODEV; goto err_unlock; } ret = iio_convert_raw_to_processed_unlocked(chan, raw, processed, - scale); + scale); err_unlock: mutex_unlock(&iio_dev_opaque->info_exist_lock); @@ -730,7 +731,7 @@ int iio_read_channel_attribute(struct iio_channel *chan, int *val, int *val2, int ret; mutex_lock(&iio_dev_opaque->info_exist_lock); - if (chan->indio_dev->info == NULL) { + if (!chan->indio_dev->info) { ret = -ENODEV; goto err_unlock; } @@ -756,7 +757,7 @@ int iio_read_channel_processed_scale(struct iio_channel *chan, int *val, int ret; mutex_lock(&iio_dev_opaque->info_exist_lock); - if (chan->indio_dev->info == NULL) { + if (!chan->indio_dev->info) { ret = -ENODEV; goto err_unlock; } @@ -834,7 +835,7 @@ int iio_read_avail_channel_raw(struct iio_channel *chan, int type; ret = iio_read_avail_channel_attribute(chan, vals, &type, length, - IIO_CHAN_INFO_RAW); + IIO_CHAN_INFO_RAW); if (ret >= 0 && type != IIO_VAL_INT) /* raw values are assumed to be IIO_VAL_INT */ @@ -918,7 +919,7 @@ int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type) /* Need to verify underlying driver has not gone away */ mutex_lock(&iio_dev_opaque->info_exist_lock); - if (chan->indio_dev->info == NULL) { + if (!chan->indio_dev->info) { ret = -ENODEV; goto err_unlock; } @@ -945,7 +946,7 @@ int iio_write_channel_attribute(struct iio_channel *chan, int val, int val2, int ret; mutex_lock(&iio_dev_opaque->info_exist_lock); - if (chan->indio_dev->info == NULL) { + if (!chan->indio_dev->info) { ret = -ENODEV; goto err_unlock; } @@ -979,9 +980,8 @@ unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan) } EXPORT_SYMBOL_GPL(iio_get_channel_ext_info_count); -static const struct iio_chan_spec_ext_info *iio_lookup_ext_info( - const struct iio_channel *chan, - const char *attr) +static const struct iio_chan_spec_ext_info * +iio_lookup_ext_info(const struct iio_channel *chan, const char *attr) { const struct iio_chan_spec_ext_info *ext_info; -- cgit v1.2.3 From 1efc41035f1841acf0af2bab153158e27ce94f10 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 26 Jun 2022 13:29:23 +0100 Subject: iio: ABI: Fix wrong format of differential capacitance channel ABI. in_ only occurs once in these attributes. Fixes: 0baf29d658c7 ("staging:iio:documentation Add abi docs for capacitance adcs.") Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220626122938.582107-3-jic23@kernel.org --- Documentation/ABI/testing/sysfs-bus-iio | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 7faec5b3a553..979f2e2b44f6 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -196,7 +196,7 @@ Description: Raw capacitance measurement from channel Y. Units after application of scale and offset are nanofarads. -What: /sys/.../iio:deviceX/in_capacitanceY-in_capacitanceZ_raw +What: /sys/.../iio:deviceX/in_capacitanceY-capacitanceZ_raw KernelVersion: 3.2 Contact: linux-iio@vger.kernel.org Description: -- cgit v1.2.3 From ebf30bed140d1da42331ce2e90125fb5e3cc5191 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 26 Jun 2022 13:29:24 +0100 Subject: staging: iio: cdc: ad7746: Use explicit be24 handling. Chance from fiddly local implementation of be24 to cpu endian conversion by reading into a 3 byte buffer and using get_unaligned_be24() Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220626122938.582107-4-jic23@kernel.org --- drivers/staging/iio/cdc/ad7746.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c index 52b8957c19c9..08f73be5797a 100644 --- a/drivers/staging/iio/cdc/ad7746.c +++ b/drivers/staging/iio/cdc/ad7746.c @@ -15,6 +15,8 @@ #include #include +#include + #include #include @@ -95,10 +97,7 @@ struct ad7746_chip_info { u8 capdac[2][2]; s8 capdac_set; - union { - __be32 d32; - u8 d8[4]; - } data ____cacheline_aligned; + u8 data[3] ____cacheline_aligned; }; enum ad7746_chan { @@ -546,13 +545,14 @@ static int ad7746_read_raw(struct iio_dev *indio_dev, /* Now read the actual register */ ret = i2c_smbus_read_i2c_block_data(chip->client, - chan->address >> 8, 3, - &chip->data.d8[1]); + chan->address >> 8, + sizeof(chip->data), + chip->data); if (ret < 0) goto out; - *val = (be32_to_cpu(chip->data.d32) & 0xFFFFFF) - 0x800000; + *val = get_unaligned_be24(chip->data) - 0x800000; switch (chan->type) { case IIO_TEMP: -- cgit v1.2.3 From 104827ec920d73db1c0176536f02ba5272c4b8fb Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 26 Jun 2022 13:29:25 +0100 Subject: staging: iio: cdc: ad7746: Push handling of supply voltage scale to userspace. The supply voltage is attenuated by 6 before being fed to the ADC. Handle this explicitly rather than pre-multiplying the _raw value by 6. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220626122938.582107-5-jic23@kernel.org --- drivers/staging/iio/cdc/ad7746.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c index 08f73be5797a..0b5cb788abee 100644 --- a/drivers/staging/iio/cdc/ad7746.c +++ b/drivers/staging/iio/cdc/ad7746.c @@ -116,9 +116,8 @@ static const struct iio_chan_spec ad7746_channels[] = { .type = IIO_VOLTAGE, .indexed = 1, .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | - BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), .address = AD7746_REG_VT_DATA_HIGH << 8 | AD7746_VTSETUP_VTMD_EXT_VIN, }, @@ -127,9 +126,8 @@ static const struct iio_chan_spec ad7746_channels[] = { .indexed = 1, .channel = 1, .extend_name = "supply", - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | - BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), .address = AD7746_REG_VT_DATA_HIGH << 8 | AD7746_VTSETUP_VTMD_VDD_MON, }, @@ -562,10 +560,6 @@ static int ad7746_read_raw(struct iio_dev *indio_dev, */ *val = (*val * 125) / 256; break; - case IIO_VOLTAGE: - if (chan->channel == 1) /* supply_raw*/ - *val = *val * 6; - break; default: break; } @@ -620,6 +614,8 @@ static int ad7746_read_raw(struct iio_dev *indio_dev, case IIO_VOLTAGE: /* 1170mV / 2^23 */ *val = 1170; + if (chan->channel == 1) + *val *= 6; *val2 = 23; ret = IIO_VAL_FRACTIONAL_LOG2; break; -- cgit v1.2.3 From 77fdc4cead204f2b3e2e1e365f70528199298737 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 26 Jun 2022 13:29:26 +0100 Subject: staging: iio: cdc: ad7746: Use local buffer for multi byte reads. I2C does not require DMA safe buffers so there is no need to ensure the buffers are in their own cacheline. Hence simplify things by using a local variable instead of embedding the buffer in the chip info structure. Includes a trivial whitespace cleanup to drop a line between function and error handling. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220626122938.582107-6-jic23@kernel.org --- drivers/staging/iio/cdc/ad7746.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c index 0b5cb788abee..496e90f559c7 100644 --- a/drivers/staging/iio/cdc/ad7746.c +++ b/drivers/staging/iio/cdc/ad7746.c @@ -96,8 +96,6 @@ struct ad7746_chip_info { u8 vt_setup; u8 capdac[2][2]; s8 capdac_set; - - u8 data[3] ____cacheline_aligned; }; enum ad7746_chan { @@ -522,6 +520,7 @@ static int ad7746_read_raw(struct iio_dev *indio_dev, struct ad7746_chip_info *chip = iio_priv(indio_dev); int ret, delay, idx; u8 regval, reg; + u8 data[3]; mutex_lock(&chip->lock); @@ -544,13 +543,11 @@ static int ad7746_read_raw(struct iio_dev *indio_dev, ret = i2c_smbus_read_i2c_block_data(chip->client, chan->address >> 8, - sizeof(chip->data), - chip->data); - + sizeof(data), data); if (ret < 0) goto out; - *val = get_unaligned_be24(chip->data) - 0x800000; + *val = get_unaligned_be24(data) - 0x800000; switch (chan->type) { case IIO_TEMP: -- cgit v1.2.3 From 5d54564e47435a5c1c1afa63c1d64908609cd545 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 26 Jun 2022 13:29:27 +0100 Subject: staging: iio: cdc: ad7746: Factor out ad7746_read_channel() Reduce deep indenting and simplify the locking cleanup that follows. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220626122938.582107-7-jic23@kernel.org --- drivers/staging/iio/cdc/ad7746.c | 80 +++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c index 496e90f559c7..8052ac2696d3 100644 --- a/drivers/staging/iio/cdc/ad7746.c +++ b/drivers/staging/iio/cdc/ad7746.c @@ -512,54 +512,66 @@ out: return ret; } +static int ad7746_read_channel(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct ad7746_chip_info *chip = iio_priv(indio_dev); + int ret, delay; + u8 data[3]; + u8 regval; + + ret = ad7746_select_channel(indio_dev, chan); + if (ret < 0) + return ret; + delay = ret; + + regval = chip->config | AD7746_CONF_MODE_SINGLE_CONV; + ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG, regval); + if (ret < 0) + return ret; + + msleep(delay); + /* Now read the actual register */ + ret = i2c_smbus_read_i2c_block_data(chip->client, chan->address >> 8, + sizeof(data), data); + if (ret < 0) + return ret; + + *val = get_unaligned_be24(data) - 0x800000; + + switch (chan->type) { + case IIO_TEMP: + /* + * temperature in milli degrees Celsius + * T = ((*val / 2048) - 4096) * 1000 + */ + *val = (*val * 125) / 256; + break; + default: + break; + } + + return 0; +} + static int ad7746_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { struct ad7746_chip_info *chip = iio_priv(indio_dev); - int ret, delay, idx; - u8 regval, reg; - u8 data[3]; + int ret, idx; + u8 reg; mutex_lock(&chip->lock); switch (mask) { case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_PROCESSED: - ret = ad7746_select_channel(indio_dev, chan); + ret = ad7746_read_channel(indio_dev, chan, val); if (ret < 0) goto out; - delay = ret; - - regval = chip->config | AD7746_CONF_MODE_SINGLE_CONV; - ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG, - regval); - if (ret < 0) - goto out; - - msleep(delay); - /* Now read the actual register */ - - ret = i2c_smbus_read_i2c_block_data(chip->client, - chan->address >> 8, - sizeof(data), data); - if (ret < 0) - goto out; - - *val = get_unaligned_be24(data) - 0x800000; - - switch (chan->type) { - case IIO_TEMP: - /* - * temperature in milli degrees Celsius - * T = ((*val / 2048) - 4096) * 1000 - */ - *val = (*val * 125) / 256; - break; - default: - break; - } ret = IIO_VAL_INT; break; -- cgit v1.2.3 From b1f567bde7e12e7671d8b0015ad3cbff8f3a8d3b Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 26 Jun 2022 13:29:28 +0100 Subject: staging: iio: cdc: ad7764: Push locking down into case statements in read/write_raw Not all paths require any locking at all. So to simplify the removal of such locking push the locks down into the individual case statements. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220626122938.582107-8-jic23@kernel.org --- drivers/staging/iio/cdc/ad7746.c | 127 +++++++++++++++++---------------------- 1 file changed, 54 insertions(+), 73 deletions(-) diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c index 8052ac2696d3..bc03760e44e0 100644 --- a/drivers/staging/iio/cdc/ad7746.c +++ b/drivers/staging/iio/cdc/ad7746.c @@ -420,14 +420,10 @@ static int ad7746_write_raw(struct iio_dev *indio_dev, struct ad7746_chip_info *chip = iio_priv(indio_dev); int ret, reg; - mutex_lock(&chip->lock); - switch (mask) { case IIO_CHAN_INFO_CALIBSCALE: - if (val != 1) { - ret = -EINVAL; - goto out; - } + if (val != 1) + return -EINVAL; val = (val2 * 1024) / 15625; @@ -439,33 +435,31 @@ static int ad7746_write_raw(struct iio_dev *indio_dev, reg = AD7746_REG_VOLT_GAINH; break; default: - ret = -EINVAL; - goto out; + return -EINVAL; } + mutex_lock(&chip->lock); ret = i2c_smbus_write_word_swapped(chip->client, reg, val); + mutex_unlock(&chip->lock); if (ret < 0) - goto out; + return ret; - ret = 0; - break; + return 0; case IIO_CHAN_INFO_CALIBBIAS: - if (val < 0 || val > 0xFFFF) { - ret = -EINVAL; - goto out; - } + if (val < 0 || val > 0xFFFF) + return -EINVAL; + + mutex_lock(&chip->lock); ret = i2c_smbus_write_word_swapped(chip->client, AD7746_REG_CAP_OFFH, val); + mutex_unlock(&chip->lock); if (ret < 0) - goto out; + return ret; - ret = 0; - break; + return 0; case IIO_CHAN_INFO_OFFSET: - if (val < 0 || val > 43008000) { /* 21pF */ - ret = -EINVAL; - goto out; - } + if (val < 0 || val > 43008000) /* 21pF */ + return -EINVAL; /* * CAPDAC Scale = 21pF_typ / 127 @@ -474,42 +468,41 @@ static int ad7746_write_raw(struct iio_dev *indio_dev, */ val /= 338646; - + mutex_lock(&chip->lock); chip->capdac[chan->channel][chan->differential] = val > 0 ? AD7746_CAPDAC_DACP(val) | AD7746_CAPDAC_DACEN : 0; ret = ad7746_set_capdac(chip, chan->channel); - if (ret < 0) - goto out; + if (ret < 0) { + mutex_unlock(&chip->lock); + return ret; + } chip->capdac_set = chan->channel; + mutex_unlock(&chip->lock); - ret = 0; - break; + return 0; case IIO_CHAN_INFO_SAMP_FREQ: - if (val2) { - ret = -EINVAL; - goto out; - } + if (val2) + return -EINVAL; switch (chan->type) { case IIO_CAPACITANCE: + mutex_lock(&chip->lock); ret = ad7746_store_cap_filter_rate_setup(chip, val); - break; + mutex_unlock(&chip->lock); + return ret; case IIO_VOLTAGE: + mutex_lock(&chip->lock); ret = ad7746_store_vt_filter_rate_setup(chip, val); - break; + mutex_unlock(&chip->lock); + return ret; default: - ret = -EINVAL; + return -EINVAL; } - break; default: - ret = -EINVAL; + return -EINVAL; } - -out: - mutex_unlock(&chip->lock); - return ret; } static int ad7746_read_channel(struct iio_dev *indio_dev, @@ -564,17 +557,16 @@ static int ad7746_read_raw(struct iio_dev *indio_dev, int ret, idx; u8 reg; - mutex_lock(&chip->lock); - switch (mask) { case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_PROCESSED: + mutex_lock(&chip->lock); ret = ad7746_read_channel(indio_dev, chan, val); + mutex_unlock(&chip->lock); if (ret < 0) - goto out; + return ret; - ret = IIO_VAL_INT; - break; + return IIO_VAL_INT; case IIO_CHAN_INFO_CALIBSCALE: switch (chan->type) { case IIO_CAPACITANCE: @@ -584,80 +576,69 @@ static int ad7746_read_raw(struct iio_dev *indio_dev, reg = AD7746_REG_VOLT_GAINH; break; default: - ret = -EINVAL; - goto out; + return -EINVAL; } + mutex_lock(&chip->lock); ret = i2c_smbus_read_word_swapped(chip->client, reg); + mutex_unlock(&chip->lock); if (ret < 0) - goto out; + return ret; /* 1 + gain_val / 2^16 */ *val = 1; *val2 = (15625 * ret) / 1024; - ret = IIO_VAL_INT_PLUS_MICRO; - break; + return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_CALIBBIAS: + mutex_lock(&chip->lock); ret = i2c_smbus_read_word_swapped(chip->client, AD7746_REG_CAP_OFFH); + mutex_unlock(&chip->lock); if (ret < 0) - goto out; + return ret; *val = ret; - ret = IIO_VAL_INT; - break; + return IIO_VAL_INT; case IIO_CHAN_INFO_OFFSET: *val = AD7746_CAPDAC_DACP(chip->capdac[chan->channel] [chan->differential]) * 338646; - ret = IIO_VAL_INT; - break; + return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_CAPACITANCE: /* 8.192pf / 2^24 */ *val = 0; *val2 = 488; - ret = IIO_VAL_INT_PLUS_NANO; - break; + return IIO_VAL_INT_PLUS_NANO; case IIO_VOLTAGE: /* 1170mV / 2^23 */ *val = 1170; if (chan->channel == 1) *val *= 6; *val2 = 23; - ret = IIO_VAL_FRACTIONAL_LOG2; - break; + return IIO_VAL_FRACTIONAL_LOG2; default: - ret = -EINVAL; - break; + return -EINVAL; } - - break; case IIO_CHAN_INFO_SAMP_FREQ: switch (chan->type) { case IIO_CAPACITANCE: idx = (chip->config & AD7746_CONF_CAPFS_MASK) >> AD7746_CONF_CAPFS_SHIFT; *val = ad7746_cap_filter_rate_table[idx][0]; - ret = IIO_VAL_INT; - break; + return IIO_VAL_INT; case IIO_VOLTAGE: idx = (chip->config & AD7746_CONF_VTFS_MASK) >> AD7746_CONF_VTFS_SHIFT; *val = ad7746_vt_filter_rate_table[idx][0]; - ret = IIO_VAL_INT; - break; + return IIO_VAL_INT; default: - ret = -EINVAL; + return -EINVAL; } - break; default: - ret = -EINVAL; + return -EINVAL; } -out: - mutex_unlock(&chip->lock); - return ret; } static const struct iio_info ad7746_info = { -- cgit v1.2.3 From 9eee2fc4a670d70ded6cc88c42d760338ce96918 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 26 Jun 2022 13:29:29 +0100 Subject: staging: iio: cdc: ad7746: Break up use of chan->address and use FIELD_PREP etc Instead of encoding several different fields into chan->address use an indirection to a separate per channel structure where the various fields can be expressed in a more readable form. This also allows the register values to be constructed at runtime using FIELD_PREP(). Drop the now redundant _SHIFT macros. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220626122938.582107-9-jic23@kernel.org --- drivers/staging/iio/cdc/ad7746.c | 152 +++++++++++++++++++++++++-------------- 1 file changed, 99 insertions(+), 53 deletions(-) diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c index bc03760e44e0..3e4448f3f3dd 100644 --- a/drivers/staging/iio/cdc/ad7746.c +++ b/drivers/staging/iio/cdc/ad7746.c @@ -5,6 +5,7 @@ * Copyright 2011 Analog Devices Inc. */ +#include #include #include #include @@ -50,11 +51,12 @@ #define AD7746_CAPSETUP_CACHOP BIT(0) /* Voltage/Temperature Setup Register Bit Designations (AD7746_REG_VT_SETUP) */ -#define AD7746_VTSETUP_VTEN (1 << 7) -#define AD7746_VTSETUP_VTMD_INT_TEMP (0 << 5) -#define AD7746_VTSETUP_VTMD_EXT_TEMP (1 << 5) -#define AD7746_VTSETUP_VTMD_VDD_MON (2 << 5) -#define AD7746_VTSETUP_VTMD_EXT_VIN (3 << 5) +#define AD7746_VTSETUP_VTEN BIT(7) +#define AD7746_VTSETUP_VTMD_MASK GENMASK(6, 5) +#define AD7746_VTSETUP_VTMD_INT_TEMP 0 +#define AD7746_VTSETUP_VTMD_EXT_TEMP 1 +#define AD7746_VTSETUP_VTMD_VDD_MON 2 +#define AD7746_VTSETUP_VTMD_EXT_VIN 3 #define AD7746_VTSETUP_EXTREF BIT(4) #define AD7746_VTSETUP_VTSHORT BIT(1) #define AD7746_VTSETUP_VTCHOP BIT(0) @@ -66,23 +68,22 @@ #define AD7746_EXCSETUP_NEXCB BIT(4) #define AD7746_EXCSETUP_EXCA BIT(3) #define AD7746_EXCSETUP_NEXCA BIT(2) -#define AD7746_EXCSETUP_EXCLVL(x) (((x) & 0x3) << 0) +#define AD7746_EXCSETUP_EXCLVL_MASK GENMASK(1, 0) /* Config Register Bit Designations (AD7746_REG_CFG) */ -#define AD7746_CONF_VTFS_SHIFT 6 -#define AD7746_CONF_CAPFS_SHIFT 3 #define AD7746_CONF_VTFS_MASK GENMASK(7, 6) #define AD7746_CONF_CAPFS_MASK GENMASK(5, 3) -#define AD7746_CONF_MODE_IDLE (0 << 0) -#define AD7746_CONF_MODE_CONT_CONV (1 << 0) -#define AD7746_CONF_MODE_SINGLE_CONV (2 << 0) -#define AD7746_CONF_MODE_PWRDN (3 << 0) -#define AD7746_CONF_MODE_OFFS_CAL (5 << 0) -#define AD7746_CONF_MODE_GAIN_CAL (6 << 0) +#define AD7746_CONF_MODE_MASK GENMASK(2, 0) +#define AD7746_CONF_MODE_IDLE 0 +#define AD7746_CONF_MODE_CONT_CONV 1 +#define AD7746_CONF_MODE_SINGLE_CONV 2 +#define AD7746_CONF_MODE_PWRDN 3 +#define AD7746_CONF_MODE_OFFS_CAL 5 +#define AD7746_CONF_MODE_GAIN_CAL 6 /* CAPDAC Register Bit Designations (AD7746_REG_CAPDACx) */ #define AD7746_CAPDAC_DACEN BIT(7) -#define AD7746_CAPDAC_DACP(x) ((x) & 0x7F) +#define AD7746_CAPDAC_DACP_MASK GENMASK(6, 0) struct ad7746_chip_info { struct i2c_client *client; @@ -109,6 +110,52 @@ enum ad7746_chan { CIN2_DIFF, }; +struct ad7746_chan_info { + u8 addr; + union { + u8 vtmd; + struct { /* CAP SETUP fields */ + unsigned int cin2 : 1; + unsigned int capdiff : 1; + }; + }; +}; + +static const struct ad7746_chan_info ad7746_chan_info[] = { + [VIN] = { + .addr = AD7746_REG_VT_DATA_HIGH, + .vtmd = AD7746_VTSETUP_VTMD_EXT_VIN, + }, + [VIN_VDD] = { + .addr = AD7746_REG_VT_DATA_HIGH, + .vtmd = AD7746_VTSETUP_VTMD_VDD_MON, + }, + [TEMP_INT] = { + .addr = AD7746_REG_VT_DATA_HIGH, + .vtmd = AD7746_VTSETUP_VTMD_INT_TEMP, + }, + [TEMP_EXT] = { + .addr = AD7746_REG_VT_DATA_HIGH, + .vtmd = AD7746_VTSETUP_VTMD_EXT_TEMP, + }, + [CIN1] = { + .addr = AD7746_REG_CAP_DATA_HIGH, + }, + [CIN1_DIFF] = { + .addr = AD7746_REG_CAP_DATA_HIGH, + .capdiff = 1, + }, + [CIN2] = { + .addr = AD7746_REG_CAP_DATA_HIGH, + .cin2 = 1, + }, + [CIN2_DIFF] = { + .addr = AD7746_REG_CAP_DATA_HIGH, + .cin2 = 1, + .capdiff = 1, + }, +}; + static const struct iio_chan_spec ad7746_channels[] = { [VIN] = { .type = IIO_VOLTAGE, @@ -116,8 +163,7 @@ static const struct iio_chan_spec ad7746_channels[] = { .channel = 0, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), - .address = AD7746_REG_VT_DATA_HIGH << 8 | - AD7746_VTSETUP_VTMD_EXT_VIN, + .address = VIN, }, [VIN_VDD] = { .type = IIO_VOLTAGE, @@ -126,24 +172,21 @@ static const struct iio_chan_spec ad7746_channels[] = { .extend_name = "supply", .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), - .address = AD7746_REG_VT_DATA_HIGH << 8 | - AD7746_VTSETUP_VTMD_VDD_MON, + .address = VIN_VDD, }, [TEMP_INT] = { .type = IIO_TEMP, .indexed = 1, .channel = 0, .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), - .address = AD7746_REG_VT_DATA_HIGH << 8 | - AD7746_VTSETUP_VTMD_INT_TEMP, + .address = TEMP_INT, }, [TEMP_EXT] = { .type = IIO_TEMP, .indexed = 1, .channel = 1, .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), - .address = AD7746_REG_VT_DATA_HIGH << 8 | - AD7746_VTSETUP_VTMD_EXT_TEMP, + .address = TEMP_EXT, }, [CIN1] = { .type = IIO_CAPACITANCE, @@ -153,7 +196,7 @@ static const struct iio_chan_spec ad7746_channels[] = { BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), - .address = AD7746_REG_CAP_DATA_HIGH << 8, + .address = CIN1, }, [CIN1_DIFF] = { .type = IIO_CAPACITANCE, @@ -165,8 +208,7 @@ static const struct iio_chan_spec ad7746_channels[] = { BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), - .address = AD7746_REG_CAP_DATA_HIGH << 8 | - AD7746_CAPSETUP_CAPDIFF + .address = CIN1_DIFF, }, [CIN2] = { .type = IIO_CAPACITANCE, @@ -176,8 +218,7 @@ static const struct iio_chan_spec ad7746_channels[] = { BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), - .address = AD7746_REG_CAP_DATA_HIGH << 8 | - AD7746_CAPSETUP_CIN2, + .address = CIN2, }, [CIN2_DIFF] = { .type = IIO_CAPACITANCE, @@ -189,8 +230,7 @@ static const struct iio_chan_spec ad7746_channels[] = { BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), - .address = AD7746_REG_CAP_DATA_HIGH << 8 | - AD7746_CAPSETUP_CAPDIFF | AD7746_CAPSETUP_CIN2, + .address = CIN2_DIFF, } }; @@ -226,10 +266,13 @@ static int ad7746_select_channel(struct iio_dev *indio_dev, switch (chan->type) { case IIO_CAPACITANCE: - cap_setup = (chan->address & 0xFF) | AD7746_CAPSETUP_CAPEN; + cap_setup = FIELD_PREP(AD7746_CAPSETUP_CIN2, + ad7746_chan_info[chan->address].cin2) | + FIELD_PREP(AD7746_CAPSETUP_CAPDIFF, + ad7746_chan_info[chan->address].capdiff) | + FIELD_PREP(AD7746_CAPSETUP_CAPEN, 1); vt_setup = chip->vt_setup & ~AD7746_VTSETUP_VTEN; - idx = (chip->config & AD7746_CONF_CAPFS_MASK) >> - AD7746_CONF_CAPFS_SHIFT; + idx = FIELD_GET(AD7746_CONF_CAPFS_MASK, chip->config); delay = ad7746_cap_filter_rate_table[idx][1]; ret = ad7746_set_capdac(chip, chan->channel); @@ -241,10 +284,11 @@ static int ad7746_select_channel(struct iio_dev *indio_dev, break; case IIO_VOLTAGE: case IIO_TEMP: - vt_setup = (chan->address & 0xFF) | AD7746_VTSETUP_VTEN; + vt_setup = FIELD_PREP(AD7746_VTSETUP_VTMD_MASK, + ad7746_chan_info[chan->address].vtmd) | + FIELD_PREP(AD7746_VTSETUP_VTEN, 1); cap_setup = chip->cap_setup & ~AD7746_CAPSETUP_CAPEN; - idx = (chip->config & AD7746_CONF_VTFS_MASK) >> - AD7746_CONF_VTFS_SHIFT; + idx = FIELD_GET(AD7746_CONF_VTFS_MASK, chip->config); delay = ad7746_cap_filter_rate_table[idx][1]; break; default: @@ -327,7 +371,8 @@ static ssize_t ad7746_start_offset_calib(struct device *dev, return ret; return ad7746_start_calib(dev, attr, buf, len, - AD7746_CONF_MODE_OFFS_CAL); + FIELD_PREP(AD7746_CONF_MODE_MASK, + AD7746_CONF_MODE_OFFS_CAL)); } static ssize_t ad7746_start_gain_calib(struct device *dev, @@ -342,7 +387,8 @@ static ssize_t ad7746_start_gain_calib(struct device *dev, return ret; return ad7746_start_calib(dev, attr, buf, len, - AD7746_CONF_MODE_GAIN_CAL); + FIELD_PREP(AD7746_CONF_MODE_MASK, + AD7746_CONF_MODE_GAIN_CAL)); } static IIO_DEVICE_ATTR(in_capacitance0_calibbias_calibration, @@ -369,7 +415,7 @@ static int ad7746_store_cap_filter_rate_setup(struct ad7746_chip_info *chip, i = ARRAY_SIZE(ad7746_cap_filter_rate_table) - 1; chip->config &= ~AD7746_CONF_CAPFS_MASK; - chip->config |= i << AD7746_CONF_CAPFS_SHIFT; + chip->config |= FIELD_PREP(AD7746_CONF_CAPFS_MASK, i); return 0; } @@ -387,7 +433,7 @@ static int ad7746_store_vt_filter_rate_setup(struct ad7746_chip_info *chip, i = ARRAY_SIZE(ad7746_vt_filter_rate_table) - 1; chip->config &= ~AD7746_CONF_VTFS_MASK; - chip->config |= i << AD7746_CONF_VTFS_SHIFT; + chip->config |= FIELD_PREP(AD7746_CONF_VTFS_MASK, i); return 0; } @@ -470,7 +516,7 @@ static int ad7746_write_raw(struct iio_dev *indio_dev, val /= 338646; mutex_lock(&chip->lock); chip->capdac[chan->channel][chan->differential] = val > 0 ? - AD7746_CAPDAC_DACP(val) | AD7746_CAPDAC_DACEN : 0; + FIELD_PREP(AD7746_CAPDAC_DACP_MASK, val) | AD7746_CAPDAC_DACEN : 0; ret = ad7746_set_capdac(chip, chan->channel); if (ret < 0) { @@ -519,14 +565,16 @@ static int ad7746_read_channel(struct iio_dev *indio_dev, return ret; delay = ret; - regval = chip->config | AD7746_CONF_MODE_SINGLE_CONV; + regval = chip->config | FIELD_PREP(AD7746_CONF_MODE_MASK, + AD7746_CONF_MODE_SINGLE_CONV); ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG, regval); if (ret < 0) return ret; msleep(delay); /* Now read the actual register */ - ret = i2c_smbus_read_i2c_block_data(chip->client, chan->address >> 8, + ret = i2c_smbus_read_i2c_block_data(chip->client, + ad7746_chan_info[chan->address].addr, sizeof(data), data); if (ret < 0) return ret; @@ -600,8 +648,8 @@ static int ad7746_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_OFFSET: - *val = AD7746_CAPDAC_DACP(chip->capdac[chan->channel] - [chan->differential]) * 338646; + *val = FIELD_GET(AD7746_CAPDAC_DACP_MASK, + chip->capdac[chan->channel][chan->differential]) * 338646; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: @@ -624,13 +672,11 @@ static int ad7746_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SAMP_FREQ: switch (chan->type) { case IIO_CAPACITANCE: - idx = (chip->config & AD7746_CONF_CAPFS_MASK) >> - AD7746_CONF_CAPFS_SHIFT; + idx = FIELD_GET(AD7746_CONF_CAPFS_MASK, chip->config); *val = ad7746_cap_filter_rate_table[idx][0]; return IIO_VAL_INT; case IIO_VOLTAGE: - idx = (chip->config & AD7746_CONF_VTFS_MASK) >> - AD7746_CONF_VTFS_SHIFT; + idx = FIELD_GET(AD7746_CONF_VTFS_MASK, chip->config); *val = ad7746_vt_filter_rate_table[idx][0]; return IIO_VAL_INT; default: @@ -696,16 +742,16 @@ static int ad7746_probe(struct i2c_client *client, if (!ret) { switch (vdd_permille) { case 125: - regval |= AD7746_EXCSETUP_EXCLVL(0); + regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 0); break; case 250: - regval |= AD7746_EXCSETUP_EXCLVL(1); + regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 1); break; case 375: - regval |= AD7746_EXCSETUP_EXCLVL(2); + regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 2); break; case 500: - regval |= AD7746_EXCSETUP_EXCLVL(3); + regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 3); break; default: break; -- cgit v1.2.3 From 90e7853ce051bca3690d3518e523f427e55c4806 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 26 Jun 2022 13:29:30 +0100 Subject: staging: iio: cdc: ad7746: Drop unused i2c_set_clientdata() As the comment states, this was only used in remove() and now there is no explicit remove() function to make use of it. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220626122938.582107-10-jic23@kernel.org --- drivers/staging/iio/cdc/ad7746.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c index 3e4448f3f3dd..81be645f08c5 100644 --- a/drivers/staging/iio/cdc/ad7746.c +++ b/drivers/staging/iio/cdc/ad7746.c @@ -708,8 +708,6 @@ static int ad7746_probe(struct i2c_client *client, return -ENOMEM; chip = iio_priv(indio_dev); mutex_init(&chip->lock); - /* this is only used for device removal purposes */ - i2c_set_clientdata(client, indio_dev); chip->client = client; chip->capdac_set = -1; -- cgit v1.2.3 From 431e9147b4667d67399fe5db4f8203d55811951b Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 26 Jun 2022 13:29:31 +0100 Subject: staging: iio: cdc: ad7746: Use _raw and _scale for temperature channels. Performing the maths to rescale a 24 bit raw reading within the driver was resulting in precision losses. So make that userspace's problem by exporting the scale and letting the maths be done in userspace with appropriate precision. Issue identified using roadtester testing framework. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220626122938.582107-11-jic23@kernel.org --- drivers/staging/iio/cdc/ad7746.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c index 81be645f08c5..3e8e7836a583 100644 --- a/drivers/staging/iio/cdc/ad7746.c +++ b/drivers/staging/iio/cdc/ad7746.c @@ -178,14 +178,16 @@ static const struct iio_chan_spec ad7746_channels[] = { .type = IIO_TEMP, .indexed = 1, .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), .address = TEMP_INT, }, [TEMP_EXT] = { .type = IIO_TEMP, .indexed = 1, .channel = 1, - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), .address = TEMP_EXT, }, [CIN1] = { @@ -581,18 +583,6 @@ static int ad7746_read_channel(struct iio_dev *indio_dev, *val = get_unaligned_be24(data) - 0x800000; - switch (chan->type) { - case IIO_TEMP: - /* - * temperature in milli degrees Celsius - * T = ((*val / 2048) - 4096) * 1000 - */ - *val = (*val * 125) / 256; - break; - default: - break; - } - return 0; } @@ -607,7 +597,6 @@ static int ad7746_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - case IIO_CHAN_INFO_PROCESSED: mutex_lock(&chip->lock); ret = ad7746_read_channel(indio_dev, chan, val); mutex_unlock(&chip->lock); @@ -666,6 +655,10 @@ static int ad7746_read_raw(struct iio_dev *indio_dev, *val *= 6; *val2 = 23; return IIO_VAL_FRACTIONAL_LOG2; + case IIO_TEMP: + *val = 125; + *val2 = 8; + return IIO_VAL_FRACTIONAL_LOG2; default: return -EINVAL; } -- cgit v1.2.3 From 5c64990b99aa91622cf044a9a2f7a78de8f770a2 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 26 Jun 2022 13:29:32 +0100 Subject: iio: core: Introduce _zeropoint for differential channels Address an ABI gap for device where the offset of both lines in a differential pair may be controlled so as to allow a wider range of inputs, but without having any direct effect of the differential measurement. _offset cannot be used as to remain in line with existing usage, userspace would be expected to apply it as (_raw + _offset) * _scale whereas _zeropoint is not. i.e. If we were computing the differential in software it would be. ((postive_raw + _zeropoint) - (negative_raw + zeropoint) + _offset) * _scale = ((postive_raw - negative_raw) + _offset) * _scale = (differential_raw + _offset) * _scale Similarly calibbias is expected to tweak the measurement seen, not the adjust the two lines of the differential pair. Needed for in_capacitanceX-capacitanceY_zeropoint for the AD7746 CDC driver. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220626122938.582107-12-jic23@kernel.org --- Documentation/ABI/testing/sysfs-bus-iio | 19 +++++++++++++++++++ drivers/iio/industrialio-core.c | 1 + include/linux/iio/types.h | 1 + 3 files changed, 21 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 979f2e2b44f6..80e8a38d1ee2 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -207,6 +207,25 @@ Description: is required is a consistent labeling. Units after application of scale and offset are nanofarads. +What: /sys/.../iio:deviceX/in_capacitanceY-capacitanceZ_zeropoint +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + For differential channels, this an offset that is applied + equally to both inputs. As the reading is of the difference + between the two inputs, this should not be applied to the _raw + reading by userspace (unlike _offset) and unlike calibbias + it does not affect the differential value measured because + the effect of _zeropoint cancels out across the two inputs + that make up the differential pair. It's purpose is to bring + the individual signals, before the differential is measured, + within the measurement range of the device. The naming is + chosen because if the separate inputs that make the + differential pair are drawn on a graph in their + _raw units, this is the value that the zero point on the + measurement axis represents. It is expressed with the + same scaling as _raw. + What: /sys/bus/iio/devices/iio:deviceX/in_temp_raw What: /sys/bus/iio/devices/iio:deviceX/in_tempX_raw What: /sys/bus/iio/devices/iio:deviceX/in_temp_x_raw diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 40ebc63b7919..67d3d01d2dac 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -168,6 +168,7 @@ static const char * const iio_chan_info_postfix[] = { [IIO_CHAN_INFO_OVERSAMPLING_RATIO] = "oversampling_ratio", [IIO_CHAN_INFO_THERMOCOUPLE_TYPE] = "thermocouple_type", [IIO_CHAN_INFO_CALIBAMBIENT] = "calibambient", + [IIO_CHAN_INFO_ZEROPOINT] = "zeropoint", }; /** * iio_device_id() - query the unique ID for the device diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index a7aa91f3a8dc..27143b03909d 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -63,6 +63,7 @@ enum iio_chan_info_enum { IIO_CHAN_INFO_OVERSAMPLING_RATIO, IIO_CHAN_INFO_THERMOCOUPLE_TYPE, IIO_CHAN_INFO_CALIBAMBIENT, + IIO_CHAN_INFO_ZEROPOINT, }; #endif /* _IIO_TYPES_H_ */ -- cgit v1.2.3 From 2d72ead25abb72996a7e92608224d2503a3bac9c Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 26 Jun 2022 13:29:33 +0100 Subject: staging: iio: cdc: ad7746: Switch from _offset to _zeropoint for differential channels. As this offset is applied equally to both lines of the differential pair, _ofset should not be used. Use the new ABI _zeropoint instead to avoid userspace software applying this value when calculating real value = (_raw + _offset) * _scale Also add a comment to explain why an offset of 0x800000 is applied within the driver rather than exposed to userspace. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220626122938.582107-13-jic23@kernel.org --- drivers/staging/iio/cdc/ad7746.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c index 3e8e7836a583..d323ae4d9992 100644 --- a/drivers/staging/iio/cdc/ad7746.c +++ b/drivers/staging/iio/cdc/ad7746.c @@ -207,7 +207,7 @@ static const struct iio_chan_spec ad7746_channels[] = { .channel = 0, .channel2 = 2, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET), + BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_ZEROPOINT), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), .address = CIN1_DIFF, @@ -229,7 +229,7 @@ static const struct iio_chan_spec ad7746_channels[] = { .channel = 1, .channel2 = 3, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET), + BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_ZEROPOINT), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), .address = CIN2_DIFF, @@ -506,6 +506,7 @@ static int ad7746_write_raw(struct iio_dev *indio_dev, return 0; case IIO_CHAN_INFO_OFFSET: + case IIO_CHAN_INFO_ZEROPOINT: if (val < 0 || val > 43008000) /* 21pF */ return -EINVAL; @@ -581,6 +582,10 @@ static int ad7746_read_channel(struct iio_dev *indio_dev, if (ret < 0) return ret; + /* + * Offset applied internally becaue the _offset userspace interface is + * needed for the CAP DACs which apply a controllable offset. + */ *val = get_unaligned_be24(data) - 0x800000; return 0; @@ -637,6 +642,7 @@ static int ad7746_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_OFFSET: + case IIO_CHAN_INFO_ZEROPOINT: *val = FIELD_GET(AD7746_CAPDAC_DACP_MASK, chip->capdac[chan->channel][chan->differential]) * 338646; -- cgit v1.2.3 From 4b717201a00576a2aad4d02ae61df363279bd934 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 26 Jun 2022 13:29:34 +0100 Subject: staging: iio: cdc: ad7746: Use read_avail() rather than opencoding. Switch over to the IIO core handling for _available attributes making them available for in kernel users and enforcing correct naming etc automatically. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220626122938.582107-14-jic23@kernel.org --- drivers/staging/iio/cdc/ad7746.c | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c index d323ae4d9992..02fd29a9f8ce 100644 --- a/drivers/staging/iio/cdc/ad7746.c +++ b/drivers/staging/iio/cdc/ad7746.c @@ -163,6 +163,7 @@ static const struct iio_chan_spec ad7746_channels[] = { .channel = 0, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), .address = VIN, }, [VIN_VDD] = { @@ -172,6 +173,7 @@ static const struct iio_chan_spec ad7746_channels[] = { .extend_name = "supply", .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), .address = VIN_VDD, }, [TEMP_INT] = { @@ -198,6 +200,7 @@ static const struct iio_chan_spec ad7746_channels[] = { BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), .address = CIN1, }, [CIN1_DIFF] = { @@ -210,6 +213,7 @@ static const struct iio_chan_spec ad7746_channels[] = { BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_ZEROPOINT), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), .address = CIN1_DIFF, }, [CIN2] = { @@ -220,6 +224,7 @@ static const struct iio_chan_spec ad7746_channels[] = { BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), .address = CIN2, }, [CIN2_DIFF] = { @@ -232,6 +237,7 @@ static const struct iio_chan_spec ad7746_channels[] = { BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_ZEROPOINT), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), .address = CIN2_DIFF, } }; @@ -440,18 +446,12 @@ static int ad7746_store_vt_filter_rate_setup(struct ad7746_chip_info *chip, return 0; } -static IIO_CONST_ATTR(in_voltage_sampling_frequency_available, "50 31 16 8"); -static IIO_CONST_ATTR(in_capacitance_sampling_frequency_available, - "91 84 50 26 16 13 11 9"); - static struct attribute *ad7746_attributes[] = { &iio_dev_attr_in_capacitance0_calibbias_calibration.dev_attr.attr, &iio_dev_attr_in_capacitance0_calibscale_calibration.dev_attr.attr, &iio_dev_attr_in_capacitance1_calibscale_calibration.dev_attr.attr, &iio_dev_attr_in_capacitance1_calibbias_calibration.dev_attr.attr, &iio_dev_attr_in_voltage0_calibscale_calibration.dev_attr.attr, - &iio_const_attr_in_voltage_sampling_frequency_available.dev_attr.attr, - &iio_const_attr_in_capacitance_sampling_frequency_available.dev_attr.attr, NULL, }; @@ -554,6 +554,32 @@ static int ad7746_write_raw(struct iio_dev *indio_dev, } } +static const int ad7746_v_samp_freq[] = { 50, 31, 16, 8, }; +static const int ad7746_cap_samp_freq[] = { 91, 84, 50, 26, 16, 13, 11, 9, }; + +static int ad7746_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, const int **vals, + int *type, int *length, long mask) +{ + if (mask != IIO_CHAN_INFO_SAMP_FREQ) + return -EINVAL; + + switch (chan->type) { + case IIO_VOLTAGE: + *vals = ad7746_v_samp_freq; + *length = ARRAY_SIZE(ad7746_v_samp_freq); + break; + case IIO_CAPACITANCE: + *vals = ad7746_cap_samp_freq; + *length = ARRAY_SIZE(ad7746_cap_samp_freq); + break; + default: + return -EINVAL; + } + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; +} + static int ad7746_read_channel(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) @@ -689,6 +715,7 @@ static int ad7746_read_raw(struct iio_dev *indio_dev, static const struct iio_info ad7746_info = { .attrs = &ad7746_attribute_group, .read_raw = ad7746_read_raw, + .read_avail = ad7746_read_avail, .write_raw = ad7746_write_raw, }; -- cgit v1.2.3 From 6d6c760954dc3075378ce925f8a511c64cbd908f Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 26 Jun 2022 13:29:35 +0100 Subject: staging: iio: ad7746: White space cleanup Tidy up some trivial whitespace issues. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220626122938.582107-15-jic23@kernel.org --- drivers/staging/iio/cdc/ad7746.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c index 02fd29a9f8ce..b266f5328140 100644 --- a/drivers/staging/iio/cdc/ad7746.c +++ b/drivers/staging/iio/cdc/ad7746.c @@ -21,9 +21,7 @@ #include #include -/* - * AD7746 Register Definition - */ +/* AD7746 Register Definition */ #define AD7746_REG_STATUS 0 #define AD7746_REG_CAP_DATA_HIGH 1 @@ -244,12 +242,12 @@ static const struct iio_chan_spec ad7746_channels[] = { /* Values are Update Rate (Hz), Conversion Time (ms) + 1*/ static const unsigned char ad7746_vt_filter_rate_table[][2] = { - {50, 20 + 1}, {31, 32 + 1}, {16, 62 + 1}, {8, 122 + 1}, + { 50, 20 + 1 }, { 31, 32 + 1 }, { 16, 62 + 1 }, { 8, 122 + 1 }, }; static const unsigned char ad7746_cap_filter_rate_table[][2] = { - {91, 11 + 1}, {84, 12 + 1}, {50, 20 + 1}, {26, 38 + 1}, - {16, 62 + 1}, {13, 77 + 1}, {11, 92 + 1}, {9, 110 + 1}, + { 91, 11 + 1 }, { 84, 12 + 1 }, { 50, 20 + 1 }, { 26, 38 + 1 }, + { 16, 62 + 1 }, { 13, 77 + 1 }, { 11, 92 + 1 }, { 9, 110 + 1 }, }; static int ad7746_set_capdac(struct ad7746_chip_info *chip, int channel) @@ -732,6 +730,7 @@ static int ad7746_probe(struct i2c_client *client, indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); if (!indio_dev) return -ENOMEM; + chip = iio_priv(indio_dev); mutex_init(&chip->lock); @@ -782,8 +781,8 @@ static int ad7746_probe(struct i2c_client *client, } } - ret = i2c_smbus_write_byte_data(chip->client, - AD7746_REG_EXC_SETUP, regval); + ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_EXC_SETUP, + regval); if (ret < 0) return ret; @@ -796,7 +795,6 @@ static const struct i2c_device_id ad7746_id[] = { { "ad7747", 7747 }, {} }; - MODULE_DEVICE_TABLE(i2c, ad7746_id); static const struct of_device_id ad7746_of_match[] = { @@ -805,7 +803,6 @@ static const struct of_device_id ad7746_of_match[] = { { .compatible = "adi,ad7747" }, { }, }; - MODULE_DEVICE_TABLE(of, ad7746_of_match); static struct i2c_driver ad7746_driver = { -- cgit v1.2.3 From cc21231ef0995a6507f2976e40fa473b82e84a44 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 26 Jun 2022 13:29:36 +0100 Subject: iio: cdc: ad7746: Add device specific ABI documentation. The datasheet description of offset calibration is complex, so for that on just refer the reader to the device datasheet. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220626122938.582107-16-jic23@kernel.org --- Documentation/ABI/testing/sysfs-bus-iio-cdc-ad7746 | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-cdc-ad7746 diff --git a/Documentation/ABI/testing/sysfs-bus-iio-cdc-ad7746 b/Documentation/ABI/testing/sysfs-bus-iio-cdc-ad7746 new file mode 100644 index 000000000000..02ca8941dce1 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-cdc-ad7746 @@ -0,0 +1,11 @@ +What: /sys/.../iio:deviceX/in_capacitableY_calibbias_calibration +What: /sys/.../iio:deviceX/in_capacitableY_calibscale_calibration +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Write 1 to trigger a calibration of the calibbias or + calibscale. For calibscale, a full scale capacitance should + be connected to the capacitance input and a + calibscale_calibration then started. For calibbias see + the device datasheet section on "capacitive system offset + calibration". -- cgit v1.2.3 From 40b5c4d5b5a67e79eef86a653cdc8b10b4d73f11 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 7 Aug 2022 15:01:44 +0100 Subject: iio: cdc: ad7746: Move driver out of staging. All known major issues with this driver resolved so time to move it out of staging. This also allows us to remove the now empty staging/iio/cdc directory and build files. Note this cleanup work was done using the roadtest framework. https://lore.kernel.org/all/20220311162445.346685-1-vincent.whitchurch@axis.com/ Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko --- drivers/iio/cdc/Kconfig | 10 + drivers/iio/cdc/Makefile | 1 + drivers/iio/cdc/ad7746.c | 820 +++++++++++++++++++++++++++++++++++++++ drivers/staging/iio/Kconfig | 1 - drivers/staging/iio/Makefile | 1 - drivers/staging/iio/cdc/Kconfig | 17 - drivers/staging/iio/cdc/Makefile | 6 - drivers/staging/iio/cdc/ad7746.c | 820 --------------------------------------- 8 files changed, 831 insertions(+), 845 deletions(-) create mode 100644 drivers/iio/cdc/ad7746.c delete mode 100644 drivers/staging/iio/cdc/Kconfig delete mode 100644 drivers/staging/iio/cdc/Makefile delete mode 100644 drivers/staging/iio/cdc/ad7746.c diff --git a/drivers/iio/cdc/Kconfig b/drivers/iio/cdc/Kconfig index 5e3319a3ff48..e0a5ce66a984 100644 --- a/drivers/iio/cdc/Kconfig +++ b/drivers/iio/cdc/Kconfig @@ -14,4 +14,14 @@ config AD7150 To compile this driver as a module, choose M here: the module will be called ad7150. +config AD7746 + tristate "Analog Devices AD7745, AD7746 AD7747 capacitive sensor driver" + depends on I2C + help + Say yes here to build support for Analog Devices capacitive sensors. + (AD7745, AD7746, AD7747) Provides direct access via sysfs. + + To compile this driver as a module, choose M here: the + module will be called ad7746. + endmenu diff --git a/drivers/iio/cdc/Makefile b/drivers/iio/cdc/Makefile index ee490637b032..41db756d8020 100644 --- a/drivers/iio/cdc/Makefile +++ b/drivers/iio/cdc/Makefile @@ -4,3 +4,4 @@ # obj-$(CONFIG_AD7150) += ad7150.o +obj-$(CONFIG_AD7746) += ad7746.o diff --git a/drivers/iio/cdc/ad7746.c b/drivers/iio/cdc/ad7746.c new file mode 100644 index 000000000000..b266f5328140 --- /dev/null +++ b/drivers/iio/cdc/ad7746.c @@ -0,0 +1,820 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AD7746 capacitive sensor driver supporting AD7745, AD7746 and AD7747 + * + * Copyright 2011 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +/* AD7746 Register Definition */ + +#define AD7746_REG_STATUS 0 +#define AD7746_REG_CAP_DATA_HIGH 1 +#define AD7746_REG_VT_DATA_HIGH 4 +#define AD7746_REG_CAP_SETUP 7 +#define AD7746_REG_VT_SETUP 8 +#define AD7746_REG_EXC_SETUP 9 +#define AD7746_REG_CFG 10 +#define AD7746_REG_CAPDACA 11 +#define AD7746_REG_CAPDACB 12 +#define AD7746_REG_CAP_OFFH 13 +#define AD7746_REG_CAP_GAINH 15 +#define AD7746_REG_VOLT_GAINH 17 + +/* Status Register Bit Designations (AD7746_REG_STATUS) */ +#define AD7746_STATUS_EXCERR BIT(3) +#define AD7746_STATUS_RDY BIT(2) +#define AD7746_STATUS_RDYVT BIT(1) +#define AD7746_STATUS_RDYCAP BIT(0) + +/* Capacitive Channel Setup Register Bit Designations (AD7746_REG_CAP_SETUP) */ +#define AD7746_CAPSETUP_CAPEN BIT(7) +#define AD7746_CAPSETUP_CIN2 BIT(6) /* AD7746 only */ +#define AD7746_CAPSETUP_CAPDIFF BIT(5) +#define AD7746_CAPSETUP_CACHOP BIT(0) + +/* Voltage/Temperature Setup Register Bit Designations (AD7746_REG_VT_SETUP) */ +#define AD7746_VTSETUP_VTEN BIT(7) +#define AD7746_VTSETUP_VTMD_MASK GENMASK(6, 5) +#define AD7746_VTSETUP_VTMD_INT_TEMP 0 +#define AD7746_VTSETUP_VTMD_EXT_TEMP 1 +#define AD7746_VTSETUP_VTMD_VDD_MON 2 +#define AD7746_VTSETUP_VTMD_EXT_VIN 3 +#define AD7746_VTSETUP_EXTREF BIT(4) +#define AD7746_VTSETUP_VTSHORT BIT(1) +#define AD7746_VTSETUP_VTCHOP BIT(0) + +/* Excitation Setup Register Bit Designations (AD7746_REG_EXC_SETUP) */ +#define AD7746_EXCSETUP_CLKCTRL BIT(7) +#define AD7746_EXCSETUP_EXCON BIT(6) +#define AD7746_EXCSETUP_EXCB BIT(5) +#define AD7746_EXCSETUP_NEXCB BIT(4) +#define AD7746_EXCSETUP_EXCA BIT(3) +#define AD7746_EXCSETUP_NEXCA BIT(2) +#define AD7746_EXCSETUP_EXCLVL_MASK GENMASK(1, 0) + +/* Config Register Bit Designations (AD7746_REG_CFG) */ +#define AD7746_CONF_VTFS_MASK GENMASK(7, 6) +#define AD7746_CONF_CAPFS_MASK GENMASK(5, 3) +#define AD7746_CONF_MODE_MASK GENMASK(2, 0) +#define AD7746_CONF_MODE_IDLE 0 +#define AD7746_CONF_MODE_CONT_CONV 1 +#define AD7746_CONF_MODE_SINGLE_CONV 2 +#define AD7746_CONF_MODE_PWRDN 3 +#define AD7746_CONF_MODE_OFFS_CAL 5 +#define AD7746_CONF_MODE_GAIN_CAL 6 + +/* CAPDAC Register Bit Designations (AD7746_REG_CAPDACx) */ +#define AD7746_CAPDAC_DACEN BIT(7) +#define AD7746_CAPDAC_DACP_MASK GENMASK(6, 0) + +struct ad7746_chip_info { + struct i2c_client *client; + struct mutex lock; /* protect sensor state */ + /* + * Capacitive channel digital filter setup; + * conversion time/update rate setup per channel + */ + u8 config; + u8 cap_setup; + u8 vt_setup; + u8 capdac[2][2]; + s8 capdac_set; +}; + +enum ad7746_chan { + VIN, + VIN_VDD, + TEMP_INT, + TEMP_EXT, + CIN1, + CIN1_DIFF, + CIN2, + CIN2_DIFF, +}; + +struct ad7746_chan_info { + u8 addr; + union { + u8 vtmd; + struct { /* CAP SETUP fields */ + unsigned int cin2 : 1; + unsigned int capdiff : 1; + }; + }; +}; + +static const struct ad7746_chan_info ad7746_chan_info[] = { + [VIN] = { + .addr = AD7746_REG_VT_DATA_HIGH, + .vtmd = AD7746_VTSETUP_VTMD_EXT_VIN, + }, + [VIN_VDD] = { + .addr = AD7746_REG_VT_DATA_HIGH, + .vtmd = AD7746_VTSETUP_VTMD_VDD_MON, + }, + [TEMP_INT] = { + .addr = AD7746_REG_VT_DATA_HIGH, + .vtmd = AD7746_VTSETUP_VTMD_INT_TEMP, + }, + [TEMP_EXT] = { + .addr = AD7746_REG_VT_DATA_HIGH, + .vtmd = AD7746_VTSETUP_VTMD_EXT_TEMP, + }, + [CIN1] = { + .addr = AD7746_REG_CAP_DATA_HIGH, + }, + [CIN1_DIFF] = { + .addr = AD7746_REG_CAP_DATA_HIGH, + .capdiff = 1, + }, + [CIN2] = { + .addr = AD7746_REG_CAP_DATA_HIGH, + .cin2 = 1, + }, + [CIN2_DIFF] = { + .addr = AD7746_REG_CAP_DATA_HIGH, + .cin2 = 1, + .capdiff = 1, + }, +}; + +static const struct iio_chan_spec ad7746_channels[] = { + [VIN] = { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .address = VIN, + }, + [VIN_VDD] = { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 1, + .extend_name = "supply", + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .address = VIN_VDD, + }, + [TEMP_INT] = { + .type = IIO_TEMP, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .address = TEMP_INT, + }, + [TEMP_EXT] = { + .type = IIO_TEMP, + .indexed = 1, + .channel = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .address = TEMP_EXT, + }, + [CIN1] = { + .type = IIO_CAPACITANCE, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .address = CIN1, + }, + [CIN1_DIFF] = { + .type = IIO_CAPACITANCE, + .differential = 1, + .indexed = 1, + .channel = 0, + .channel2 = 2, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_ZEROPOINT), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .address = CIN1_DIFF, + }, + [CIN2] = { + .type = IIO_CAPACITANCE, + .indexed = 1, + .channel = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .address = CIN2, + }, + [CIN2_DIFF] = { + .type = IIO_CAPACITANCE, + .differential = 1, + .indexed = 1, + .channel = 1, + .channel2 = 3, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_ZEROPOINT), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .address = CIN2_DIFF, + } +}; + +/* Values are Update Rate (Hz), Conversion Time (ms) + 1*/ +static const unsigned char ad7746_vt_filter_rate_table[][2] = { + { 50, 20 + 1 }, { 31, 32 + 1 }, { 16, 62 + 1 }, { 8, 122 + 1 }, +}; + +static const unsigned char ad7746_cap_filter_rate_table[][2] = { + { 91, 11 + 1 }, { 84, 12 + 1 }, { 50, 20 + 1 }, { 26, 38 + 1 }, + { 16, 62 + 1 }, { 13, 77 + 1 }, { 11, 92 + 1 }, { 9, 110 + 1 }, +}; + +static int ad7746_set_capdac(struct ad7746_chip_info *chip, int channel) +{ + int ret = i2c_smbus_write_byte_data(chip->client, + AD7746_REG_CAPDACA, + chip->capdac[channel][0]); + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(chip->client, + AD7746_REG_CAPDACB, + chip->capdac[channel][1]); +} + +static int ad7746_select_channel(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + struct ad7746_chip_info *chip = iio_priv(indio_dev); + u8 vt_setup, cap_setup; + int ret, delay, idx; + + switch (chan->type) { + case IIO_CAPACITANCE: + cap_setup = FIELD_PREP(AD7746_CAPSETUP_CIN2, + ad7746_chan_info[chan->address].cin2) | + FIELD_PREP(AD7746_CAPSETUP_CAPDIFF, + ad7746_chan_info[chan->address].capdiff) | + FIELD_PREP(AD7746_CAPSETUP_CAPEN, 1); + vt_setup = chip->vt_setup & ~AD7746_VTSETUP_VTEN; + idx = FIELD_GET(AD7746_CONF_CAPFS_MASK, chip->config); + delay = ad7746_cap_filter_rate_table[idx][1]; + + ret = ad7746_set_capdac(chip, chan->channel); + if (ret < 0) + return ret; + + if (chip->capdac_set != chan->channel) + chip->capdac_set = chan->channel; + break; + case IIO_VOLTAGE: + case IIO_TEMP: + vt_setup = FIELD_PREP(AD7746_VTSETUP_VTMD_MASK, + ad7746_chan_info[chan->address].vtmd) | + FIELD_PREP(AD7746_VTSETUP_VTEN, 1); + cap_setup = chip->cap_setup & ~AD7746_CAPSETUP_CAPEN; + idx = FIELD_GET(AD7746_CONF_VTFS_MASK, chip->config); + delay = ad7746_cap_filter_rate_table[idx][1]; + break; + default: + return -EINVAL; + } + + if (chip->cap_setup != cap_setup) { + ret = i2c_smbus_write_byte_data(chip->client, + AD7746_REG_CAP_SETUP, + cap_setup); + if (ret < 0) + return ret; + + chip->cap_setup = cap_setup; + } + + if (chip->vt_setup != vt_setup) { + ret = i2c_smbus_write_byte_data(chip->client, + AD7746_REG_VT_SETUP, + vt_setup); + if (ret < 0) + return ret; + + chip->vt_setup = vt_setup; + } + + return delay; +} + +static inline ssize_t ad7746_start_calib(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len, + u8 regval) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7746_chip_info *chip = iio_priv(indio_dev); + int ret, timeout = 10; + bool doit; + + ret = kstrtobool(buf, &doit); + if (ret < 0) + return ret; + + if (!doit) + return 0; + + mutex_lock(&chip->lock); + regval |= chip->config; + ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG, regval); + if (ret < 0) + goto unlock; + + do { + msleep(20); + ret = i2c_smbus_read_byte_data(chip->client, AD7746_REG_CFG); + if (ret < 0) + goto unlock; + + } while ((ret == regval) && timeout--); + + mutex_unlock(&chip->lock); + + return len; + +unlock: + mutex_unlock(&chip->lock); + return ret; +} + +static ssize_t ad7746_start_offset_calib(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + int ret = ad7746_select_channel(indio_dev, + &ad7746_channels[to_iio_dev_attr(attr)->address]); + if (ret < 0) + return ret; + + return ad7746_start_calib(dev, attr, buf, len, + FIELD_PREP(AD7746_CONF_MODE_MASK, + AD7746_CONF_MODE_OFFS_CAL)); +} + +static ssize_t ad7746_start_gain_calib(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + int ret = ad7746_select_channel(indio_dev, + &ad7746_channels[to_iio_dev_attr(attr)->address]); + if (ret < 0) + return ret; + + return ad7746_start_calib(dev, attr, buf, len, + FIELD_PREP(AD7746_CONF_MODE_MASK, + AD7746_CONF_MODE_GAIN_CAL)); +} + +static IIO_DEVICE_ATTR(in_capacitance0_calibbias_calibration, + 0200, NULL, ad7746_start_offset_calib, CIN1); +static IIO_DEVICE_ATTR(in_capacitance1_calibbias_calibration, + 0200, NULL, ad7746_start_offset_calib, CIN2); +static IIO_DEVICE_ATTR(in_capacitance0_calibscale_calibration, + 0200, NULL, ad7746_start_gain_calib, CIN1); +static IIO_DEVICE_ATTR(in_capacitance1_calibscale_calibration, + 0200, NULL, ad7746_start_gain_calib, CIN2); +static IIO_DEVICE_ATTR(in_voltage0_calibscale_calibration, + 0200, NULL, ad7746_start_gain_calib, VIN); + +static int ad7746_store_cap_filter_rate_setup(struct ad7746_chip_info *chip, + int val) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ad7746_cap_filter_rate_table); i++) + if (val >= ad7746_cap_filter_rate_table[i][0]) + break; + + if (i >= ARRAY_SIZE(ad7746_cap_filter_rate_table)) + i = ARRAY_SIZE(ad7746_cap_filter_rate_table) - 1; + + chip->config &= ~AD7746_CONF_CAPFS_MASK; + chip->config |= FIELD_PREP(AD7746_CONF_CAPFS_MASK, i); + + return 0; +} + +static int ad7746_store_vt_filter_rate_setup(struct ad7746_chip_info *chip, + int val) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ad7746_vt_filter_rate_table); i++) + if (val >= ad7746_vt_filter_rate_table[i][0]) + break; + + if (i >= ARRAY_SIZE(ad7746_vt_filter_rate_table)) + i = ARRAY_SIZE(ad7746_vt_filter_rate_table) - 1; + + chip->config &= ~AD7746_CONF_VTFS_MASK; + chip->config |= FIELD_PREP(AD7746_CONF_VTFS_MASK, i); + + return 0; +} + +static struct attribute *ad7746_attributes[] = { + &iio_dev_attr_in_capacitance0_calibbias_calibration.dev_attr.attr, + &iio_dev_attr_in_capacitance0_calibscale_calibration.dev_attr.attr, + &iio_dev_attr_in_capacitance1_calibscale_calibration.dev_attr.attr, + &iio_dev_attr_in_capacitance1_calibbias_calibration.dev_attr.attr, + &iio_dev_attr_in_voltage0_calibscale_calibration.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ad7746_attribute_group = { + .attrs = ad7746_attributes, +}; + +static int ad7746_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad7746_chip_info *chip = iio_priv(indio_dev); + int ret, reg; + + switch (mask) { + case IIO_CHAN_INFO_CALIBSCALE: + if (val != 1) + return -EINVAL; + + val = (val2 * 1024) / 15625; + + switch (chan->type) { + case IIO_CAPACITANCE: + reg = AD7746_REG_CAP_GAINH; + break; + case IIO_VOLTAGE: + reg = AD7746_REG_VOLT_GAINH; + break; + default: + return -EINVAL; + } + + mutex_lock(&chip->lock); + ret = i2c_smbus_write_word_swapped(chip->client, reg, val); + mutex_unlock(&chip->lock); + if (ret < 0) + return ret; + + return 0; + case IIO_CHAN_INFO_CALIBBIAS: + if (val < 0 || val > 0xFFFF) + return -EINVAL; + + mutex_lock(&chip->lock); + ret = i2c_smbus_write_word_swapped(chip->client, + AD7746_REG_CAP_OFFH, val); + mutex_unlock(&chip->lock); + if (ret < 0) + return ret; + + return 0; + case IIO_CHAN_INFO_OFFSET: + case IIO_CHAN_INFO_ZEROPOINT: + if (val < 0 || val > 43008000) /* 21pF */ + return -EINVAL; + + /* + * CAPDAC Scale = 21pF_typ / 127 + * CIN Scale = 8.192pF / 2^24 + * Offset Scale = CAPDAC Scale / CIN Scale = 338646 + */ + + val /= 338646; + mutex_lock(&chip->lock); + chip->capdac[chan->channel][chan->differential] = val > 0 ? + FIELD_PREP(AD7746_CAPDAC_DACP_MASK, val) | AD7746_CAPDAC_DACEN : 0; + + ret = ad7746_set_capdac(chip, chan->channel); + if (ret < 0) { + mutex_unlock(&chip->lock); + return ret; + } + + chip->capdac_set = chan->channel; + mutex_unlock(&chip->lock); + + return 0; + case IIO_CHAN_INFO_SAMP_FREQ: + if (val2) + return -EINVAL; + + switch (chan->type) { + case IIO_CAPACITANCE: + mutex_lock(&chip->lock); + ret = ad7746_store_cap_filter_rate_setup(chip, val); + mutex_unlock(&chip->lock); + return ret; + case IIO_VOLTAGE: + mutex_lock(&chip->lock); + ret = ad7746_store_vt_filter_rate_setup(chip, val); + mutex_unlock(&chip->lock); + return ret; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static const int ad7746_v_samp_freq[] = { 50, 31, 16, 8, }; +static const int ad7746_cap_samp_freq[] = { 91, 84, 50, 26, 16, 13, 11, 9, }; + +static int ad7746_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, const int **vals, + int *type, int *length, long mask) +{ + if (mask != IIO_CHAN_INFO_SAMP_FREQ) + return -EINVAL; + + switch (chan->type) { + case IIO_VOLTAGE: + *vals = ad7746_v_samp_freq; + *length = ARRAY_SIZE(ad7746_v_samp_freq); + break; + case IIO_CAPACITANCE: + *vals = ad7746_cap_samp_freq; + *length = ARRAY_SIZE(ad7746_cap_samp_freq); + break; + default: + return -EINVAL; + } + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; +} + +static int ad7746_read_channel(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct ad7746_chip_info *chip = iio_priv(indio_dev); + int ret, delay; + u8 data[3]; + u8 regval; + + ret = ad7746_select_channel(indio_dev, chan); + if (ret < 0) + return ret; + delay = ret; + + regval = chip->config | FIELD_PREP(AD7746_CONF_MODE_MASK, + AD7746_CONF_MODE_SINGLE_CONV); + ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG, regval); + if (ret < 0) + return ret; + + msleep(delay); + /* Now read the actual register */ + ret = i2c_smbus_read_i2c_block_data(chip->client, + ad7746_chan_info[chan->address].addr, + sizeof(data), data); + if (ret < 0) + return ret; + + /* + * Offset applied internally becaue the _offset userspace interface is + * needed for the CAP DACs which apply a controllable offset. + */ + *val = get_unaligned_be24(data) - 0x800000; + + return 0; +} + +static int ad7746_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, + long mask) +{ + struct ad7746_chip_info *chip = iio_priv(indio_dev); + int ret, idx; + u8 reg; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&chip->lock); + ret = ad7746_read_channel(indio_dev, chan, val); + mutex_unlock(&chip->lock); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBSCALE: + switch (chan->type) { + case IIO_CAPACITANCE: + reg = AD7746_REG_CAP_GAINH; + break; + case IIO_VOLTAGE: + reg = AD7746_REG_VOLT_GAINH; + break; + default: + return -EINVAL; + } + + mutex_lock(&chip->lock); + ret = i2c_smbus_read_word_swapped(chip->client, reg); + mutex_unlock(&chip->lock); + if (ret < 0) + return ret; + /* 1 + gain_val / 2^16 */ + *val = 1; + *val2 = (15625 * ret) / 1024; + + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_CALIBBIAS: + mutex_lock(&chip->lock); + ret = i2c_smbus_read_word_swapped(chip->client, + AD7746_REG_CAP_OFFH); + mutex_unlock(&chip->lock); + if (ret < 0) + return ret; + *val = ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + case IIO_CHAN_INFO_ZEROPOINT: + *val = FIELD_GET(AD7746_CAPDAC_DACP_MASK, + chip->capdac[chan->channel][chan->differential]) * 338646; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_CAPACITANCE: + /* 8.192pf / 2^24 */ + *val = 0; + *val2 = 488; + return IIO_VAL_INT_PLUS_NANO; + case IIO_VOLTAGE: + /* 1170mV / 2^23 */ + *val = 1170; + if (chan->channel == 1) + *val *= 6; + *val2 = 23; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_TEMP: + *val = 125; + *val2 = 8; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + switch (chan->type) { + case IIO_CAPACITANCE: + idx = FIELD_GET(AD7746_CONF_CAPFS_MASK, chip->config); + *val = ad7746_cap_filter_rate_table[idx][0]; + return IIO_VAL_INT; + case IIO_VOLTAGE: + idx = FIELD_GET(AD7746_CONF_VTFS_MASK, chip->config); + *val = ad7746_vt_filter_rate_table[idx][0]; + return IIO_VAL_INT; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static const struct iio_info ad7746_info = { + .attrs = &ad7746_attribute_group, + .read_raw = ad7746_read_raw, + .read_avail = ad7746_read_avail, + .write_raw = ad7746_write_raw, +}; + +static int ad7746_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct ad7746_chip_info *chip; + struct iio_dev *indio_dev; + unsigned char regval = 0; + unsigned int vdd_permille; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + + chip = iio_priv(indio_dev); + mutex_init(&chip->lock); + + chip->client = client; + chip->capdac_set = -1; + + indio_dev->name = id->name; + indio_dev->info = &ad7746_info; + indio_dev->channels = ad7746_channels; + if (id->driver_data == 7746) + indio_dev->num_channels = ARRAY_SIZE(ad7746_channels); + else + indio_dev->num_channels = ARRAY_SIZE(ad7746_channels) - 2; + indio_dev->modes = INDIO_DIRECT_MODE; + + if (device_property_read_bool(dev, "adi,exca-output-en")) { + if (device_property_read_bool(dev, "adi,exca-output-invert")) + regval |= AD7746_EXCSETUP_NEXCA; + else + regval |= AD7746_EXCSETUP_EXCA; + } + + if (device_property_read_bool(dev, "adi,excb-output-en")) { + if (device_property_read_bool(dev, "adi,excb-output-invert")) + regval |= AD7746_EXCSETUP_NEXCB; + else + regval |= AD7746_EXCSETUP_EXCB; + } + + ret = device_property_read_u32(dev, "adi,excitation-vdd-permille", + &vdd_permille); + if (!ret) { + switch (vdd_permille) { + case 125: + regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 0); + break; + case 250: + regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 1); + break; + case 375: + regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 2); + break; + case 500: + regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 3); + break; + default: + break; + } + } + + ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_EXC_SETUP, + regval); + if (ret < 0) + return ret; + + return devm_iio_device_register(indio_dev->dev.parent, indio_dev); +} + +static const struct i2c_device_id ad7746_id[] = { + { "ad7745", 7745 }, + { "ad7746", 7746 }, + { "ad7747", 7747 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ad7746_id); + +static const struct of_device_id ad7746_of_match[] = { + { .compatible = "adi,ad7745" }, + { .compatible = "adi,ad7746" }, + { .compatible = "adi,ad7747" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ad7746_of_match); + +static struct i2c_driver ad7746_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = ad7746_of_match, + }, + .probe = ad7746_probe, + .id_table = ad7746_id, +}; +module_i2c_driver(ad7746_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD7746/5/7 capacitive sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig index a8e970db179d..afd05bf3345e 100644 --- a/drivers/staging/iio/Kconfig +++ b/drivers/staging/iio/Kconfig @@ -8,7 +8,6 @@ menu "IIO staging drivers" source "drivers/staging/iio/accel/Kconfig" source "drivers/staging/iio/adc/Kconfig" source "drivers/staging/iio/addac/Kconfig" -source "drivers/staging/iio/cdc/Kconfig" source "drivers/staging/iio/frequency/Kconfig" source "drivers/staging/iio/impedance-analyzer/Kconfig" source "drivers/staging/iio/meter/Kconfig" diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile index b15904b99581..5ed56fe57e14 100644 --- a/drivers/staging/iio/Makefile +++ b/drivers/staging/iio/Makefile @@ -6,7 +6,6 @@ obj-y += accel/ obj-y += adc/ obj-y += addac/ -obj-y += cdc/ obj-y += frequency/ obj-y += impedance-analyzer/ obj-y += meter/ diff --git a/drivers/staging/iio/cdc/Kconfig b/drivers/staging/iio/cdc/Kconfig deleted file mode 100644 index a7386bbbcb79..000000000000 --- a/drivers/staging/iio/cdc/Kconfig +++ /dev/null @@ -1,17 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# CDC drivers -# -menu "Capacitance to digital converters" - -config AD7746 - tristate "Analog Devices AD7745, AD7746 AD7747 capacitive sensor driver" - depends on I2C - help - Say yes here to build support for Analog Devices capacitive sensors. - (AD7745, AD7746, AD7747) Provides direct access via sysfs. - - To compile this driver as a module, choose M here: the - module will be called ad7746. - -endmenu diff --git a/drivers/staging/iio/cdc/Makefile b/drivers/staging/iio/cdc/Makefile deleted file mode 100644 index afb7499a7090..000000000000 --- a/drivers/staging/iio/cdc/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for industrial I/O CDC drivers -# - -obj-$(CONFIG_AD7746) += ad7746.o diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c deleted file mode 100644 index b266f5328140..000000000000 --- a/drivers/staging/iio/cdc/ad7746.c +++ /dev/null @@ -1,820 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * AD7746 capacitive sensor driver supporting AD7745, AD7746 and AD7747 - * - * Copyright 2011 Analog Devices Inc. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -/* AD7746 Register Definition */ - -#define AD7746_REG_STATUS 0 -#define AD7746_REG_CAP_DATA_HIGH 1 -#define AD7746_REG_VT_DATA_HIGH 4 -#define AD7746_REG_CAP_SETUP 7 -#define AD7746_REG_VT_SETUP 8 -#define AD7746_REG_EXC_SETUP 9 -#define AD7746_REG_CFG 10 -#define AD7746_REG_CAPDACA 11 -#define AD7746_REG_CAPDACB 12 -#define AD7746_REG_CAP_OFFH 13 -#define AD7746_REG_CAP_GAINH 15 -#define AD7746_REG_VOLT_GAINH 17 - -/* Status Register Bit Designations (AD7746_REG_STATUS) */ -#define AD7746_STATUS_EXCERR BIT(3) -#define AD7746_STATUS_RDY BIT(2) -#define AD7746_STATUS_RDYVT BIT(1) -#define AD7746_STATUS_RDYCAP BIT(0) - -/* Capacitive Channel Setup Register Bit Designations (AD7746_REG_CAP_SETUP) */ -#define AD7746_CAPSETUP_CAPEN BIT(7) -#define AD7746_CAPSETUP_CIN2 BIT(6) /* AD7746 only */ -#define AD7746_CAPSETUP_CAPDIFF BIT(5) -#define AD7746_CAPSETUP_CACHOP BIT(0) - -/* Voltage/Temperature Setup Register Bit Designations (AD7746_REG_VT_SETUP) */ -#define AD7746_VTSETUP_VTEN BIT(7) -#define AD7746_VTSETUP_VTMD_MASK GENMASK(6, 5) -#define AD7746_VTSETUP_VTMD_INT_TEMP 0 -#define AD7746_VTSETUP_VTMD_EXT_TEMP 1 -#define AD7746_VTSETUP_VTMD_VDD_MON 2 -#define AD7746_VTSETUP_VTMD_EXT_VIN 3 -#define AD7746_VTSETUP_EXTREF BIT(4) -#define AD7746_VTSETUP_VTSHORT BIT(1) -#define AD7746_VTSETUP_VTCHOP BIT(0) - -/* Excitation Setup Register Bit Designations (AD7746_REG_EXC_SETUP) */ -#define AD7746_EXCSETUP_CLKCTRL BIT(7) -#define AD7746_EXCSETUP_EXCON BIT(6) -#define AD7746_EXCSETUP_EXCB BIT(5) -#define AD7746_EXCSETUP_NEXCB BIT(4) -#define AD7746_EXCSETUP_EXCA BIT(3) -#define AD7746_EXCSETUP_NEXCA BIT(2) -#define AD7746_EXCSETUP_EXCLVL_MASK GENMASK(1, 0) - -/* Config Register Bit Designations (AD7746_REG_CFG) */ -#define AD7746_CONF_VTFS_MASK GENMASK(7, 6) -#define AD7746_CONF_CAPFS_MASK GENMASK(5, 3) -#define AD7746_CONF_MODE_MASK GENMASK(2, 0) -#define AD7746_CONF_MODE_IDLE 0 -#define AD7746_CONF_MODE_CONT_CONV 1 -#define AD7746_CONF_MODE_SINGLE_CONV 2 -#define AD7746_CONF_MODE_PWRDN 3 -#define AD7746_CONF_MODE_OFFS_CAL 5 -#define AD7746_CONF_MODE_GAIN_CAL 6 - -/* CAPDAC Register Bit Designations (AD7746_REG_CAPDACx) */ -#define AD7746_CAPDAC_DACEN BIT(7) -#define AD7746_CAPDAC_DACP_MASK GENMASK(6, 0) - -struct ad7746_chip_info { - struct i2c_client *client; - struct mutex lock; /* protect sensor state */ - /* - * Capacitive channel digital filter setup; - * conversion time/update rate setup per channel - */ - u8 config; - u8 cap_setup; - u8 vt_setup; - u8 capdac[2][2]; - s8 capdac_set; -}; - -enum ad7746_chan { - VIN, - VIN_VDD, - TEMP_INT, - TEMP_EXT, - CIN1, - CIN1_DIFF, - CIN2, - CIN2_DIFF, -}; - -struct ad7746_chan_info { - u8 addr; - union { - u8 vtmd; - struct { /* CAP SETUP fields */ - unsigned int cin2 : 1; - unsigned int capdiff : 1; - }; - }; -}; - -static const struct ad7746_chan_info ad7746_chan_info[] = { - [VIN] = { - .addr = AD7746_REG_VT_DATA_HIGH, - .vtmd = AD7746_VTSETUP_VTMD_EXT_VIN, - }, - [VIN_VDD] = { - .addr = AD7746_REG_VT_DATA_HIGH, - .vtmd = AD7746_VTSETUP_VTMD_VDD_MON, - }, - [TEMP_INT] = { - .addr = AD7746_REG_VT_DATA_HIGH, - .vtmd = AD7746_VTSETUP_VTMD_INT_TEMP, - }, - [TEMP_EXT] = { - .addr = AD7746_REG_VT_DATA_HIGH, - .vtmd = AD7746_VTSETUP_VTMD_EXT_TEMP, - }, - [CIN1] = { - .addr = AD7746_REG_CAP_DATA_HIGH, - }, - [CIN1_DIFF] = { - .addr = AD7746_REG_CAP_DATA_HIGH, - .capdiff = 1, - }, - [CIN2] = { - .addr = AD7746_REG_CAP_DATA_HIGH, - .cin2 = 1, - }, - [CIN2_DIFF] = { - .addr = AD7746_REG_CAP_DATA_HIGH, - .cin2 = 1, - .capdiff = 1, - }, -}; - -static const struct iio_chan_spec ad7746_channels[] = { - [VIN] = { - .type = IIO_VOLTAGE, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), - .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), - .address = VIN, - }, - [VIN_VDD] = { - .type = IIO_VOLTAGE, - .indexed = 1, - .channel = 1, - .extend_name = "supply", - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), - .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), - .address = VIN_VDD, - }, - [TEMP_INT] = { - .type = IIO_TEMP, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), - .address = TEMP_INT, - }, - [TEMP_EXT] = { - .type = IIO_TEMP, - .indexed = 1, - .channel = 1, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), - .address = TEMP_EXT, - }, - [CIN1] = { - .type = IIO_CAPACITANCE, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET), - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) | - BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), - .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), - .address = CIN1, - }, - [CIN1_DIFF] = { - .type = IIO_CAPACITANCE, - .differential = 1, - .indexed = 1, - .channel = 0, - .channel2 = 2, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_ZEROPOINT), - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) | - BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), - .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), - .address = CIN1_DIFF, - }, - [CIN2] = { - .type = IIO_CAPACITANCE, - .indexed = 1, - .channel = 1, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET), - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) | - BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), - .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), - .address = CIN2, - }, - [CIN2_DIFF] = { - .type = IIO_CAPACITANCE, - .differential = 1, - .indexed = 1, - .channel = 1, - .channel2 = 3, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_ZEROPOINT), - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) | - BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), - .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), - .address = CIN2_DIFF, - } -}; - -/* Values are Update Rate (Hz), Conversion Time (ms) + 1*/ -static const unsigned char ad7746_vt_filter_rate_table[][2] = { - { 50, 20 + 1 }, { 31, 32 + 1 }, { 16, 62 + 1 }, { 8, 122 + 1 }, -}; - -static const unsigned char ad7746_cap_filter_rate_table[][2] = { - { 91, 11 + 1 }, { 84, 12 + 1 }, { 50, 20 + 1 }, { 26, 38 + 1 }, - { 16, 62 + 1 }, { 13, 77 + 1 }, { 11, 92 + 1 }, { 9, 110 + 1 }, -}; - -static int ad7746_set_capdac(struct ad7746_chip_info *chip, int channel) -{ - int ret = i2c_smbus_write_byte_data(chip->client, - AD7746_REG_CAPDACA, - chip->capdac[channel][0]); - if (ret < 0) - return ret; - - return i2c_smbus_write_byte_data(chip->client, - AD7746_REG_CAPDACB, - chip->capdac[channel][1]); -} - -static int ad7746_select_channel(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan) -{ - struct ad7746_chip_info *chip = iio_priv(indio_dev); - u8 vt_setup, cap_setup; - int ret, delay, idx; - - switch (chan->type) { - case IIO_CAPACITANCE: - cap_setup = FIELD_PREP(AD7746_CAPSETUP_CIN2, - ad7746_chan_info[chan->address].cin2) | - FIELD_PREP(AD7746_CAPSETUP_CAPDIFF, - ad7746_chan_info[chan->address].capdiff) | - FIELD_PREP(AD7746_CAPSETUP_CAPEN, 1); - vt_setup = chip->vt_setup & ~AD7746_VTSETUP_VTEN; - idx = FIELD_GET(AD7746_CONF_CAPFS_MASK, chip->config); - delay = ad7746_cap_filter_rate_table[idx][1]; - - ret = ad7746_set_capdac(chip, chan->channel); - if (ret < 0) - return ret; - - if (chip->capdac_set != chan->channel) - chip->capdac_set = chan->channel; - break; - case IIO_VOLTAGE: - case IIO_TEMP: - vt_setup = FIELD_PREP(AD7746_VTSETUP_VTMD_MASK, - ad7746_chan_info[chan->address].vtmd) | - FIELD_PREP(AD7746_VTSETUP_VTEN, 1); - cap_setup = chip->cap_setup & ~AD7746_CAPSETUP_CAPEN; - idx = FIELD_GET(AD7746_CONF_VTFS_MASK, chip->config); - delay = ad7746_cap_filter_rate_table[idx][1]; - break; - default: - return -EINVAL; - } - - if (chip->cap_setup != cap_setup) { - ret = i2c_smbus_write_byte_data(chip->client, - AD7746_REG_CAP_SETUP, - cap_setup); - if (ret < 0) - return ret; - - chip->cap_setup = cap_setup; - } - - if (chip->vt_setup != vt_setup) { - ret = i2c_smbus_write_byte_data(chip->client, - AD7746_REG_VT_SETUP, - vt_setup); - if (ret < 0) - return ret; - - chip->vt_setup = vt_setup; - } - - return delay; -} - -static inline ssize_t ad7746_start_calib(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len, - u8 regval) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ad7746_chip_info *chip = iio_priv(indio_dev); - int ret, timeout = 10; - bool doit; - - ret = kstrtobool(buf, &doit); - if (ret < 0) - return ret; - - if (!doit) - return 0; - - mutex_lock(&chip->lock); - regval |= chip->config; - ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG, regval); - if (ret < 0) - goto unlock; - - do { - msleep(20); - ret = i2c_smbus_read_byte_data(chip->client, AD7746_REG_CFG); - if (ret < 0) - goto unlock; - - } while ((ret == regval) && timeout--); - - mutex_unlock(&chip->lock); - - return len; - -unlock: - mutex_unlock(&chip->lock); - return ret; -} - -static ssize_t ad7746_start_offset_calib(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - int ret = ad7746_select_channel(indio_dev, - &ad7746_channels[to_iio_dev_attr(attr)->address]); - if (ret < 0) - return ret; - - return ad7746_start_calib(dev, attr, buf, len, - FIELD_PREP(AD7746_CONF_MODE_MASK, - AD7746_CONF_MODE_OFFS_CAL)); -} - -static ssize_t ad7746_start_gain_calib(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - int ret = ad7746_select_channel(indio_dev, - &ad7746_channels[to_iio_dev_attr(attr)->address]); - if (ret < 0) - return ret; - - return ad7746_start_calib(dev, attr, buf, len, - FIELD_PREP(AD7746_CONF_MODE_MASK, - AD7746_CONF_MODE_GAIN_CAL)); -} - -static IIO_DEVICE_ATTR(in_capacitance0_calibbias_calibration, - 0200, NULL, ad7746_start_offset_calib, CIN1); -static IIO_DEVICE_ATTR(in_capacitance1_calibbias_calibration, - 0200, NULL, ad7746_start_offset_calib, CIN2); -static IIO_DEVICE_ATTR(in_capacitance0_calibscale_calibration, - 0200, NULL, ad7746_start_gain_calib, CIN1); -static IIO_DEVICE_ATTR(in_capacitance1_calibscale_calibration, - 0200, NULL, ad7746_start_gain_calib, CIN2); -static IIO_DEVICE_ATTR(in_voltage0_calibscale_calibration, - 0200, NULL, ad7746_start_gain_calib, VIN); - -static int ad7746_store_cap_filter_rate_setup(struct ad7746_chip_info *chip, - int val) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ad7746_cap_filter_rate_table); i++) - if (val >= ad7746_cap_filter_rate_table[i][0]) - break; - - if (i >= ARRAY_SIZE(ad7746_cap_filter_rate_table)) - i = ARRAY_SIZE(ad7746_cap_filter_rate_table) - 1; - - chip->config &= ~AD7746_CONF_CAPFS_MASK; - chip->config |= FIELD_PREP(AD7746_CONF_CAPFS_MASK, i); - - return 0; -} - -static int ad7746_store_vt_filter_rate_setup(struct ad7746_chip_info *chip, - int val) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ad7746_vt_filter_rate_table); i++) - if (val >= ad7746_vt_filter_rate_table[i][0]) - break; - - if (i >= ARRAY_SIZE(ad7746_vt_filter_rate_table)) - i = ARRAY_SIZE(ad7746_vt_filter_rate_table) - 1; - - chip->config &= ~AD7746_CONF_VTFS_MASK; - chip->config |= FIELD_PREP(AD7746_CONF_VTFS_MASK, i); - - return 0; -} - -static struct attribute *ad7746_attributes[] = { - &iio_dev_attr_in_capacitance0_calibbias_calibration.dev_attr.attr, - &iio_dev_attr_in_capacitance0_calibscale_calibration.dev_attr.attr, - &iio_dev_attr_in_capacitance1_calibscale_calibration.dev_attr.attr, - &iio_dev_attr_in_capacitance1_calibbias_calibration.dev_attr.attr, - &iio_dev_attr_in_voltage0_calibscale_calibration.dev_attr.attr, - NULL, -}; - -static const struct attribute_group ad7746_attribute_group = { - .attrs = ad7746_attributes, -}; - -static int ad7746_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) -{ - struct ad7746_chip_info *chip = iio_priv(indio_dev); - int ret, reg; - - switch (mask) { - case IIO_CHAN_INFO_CALIBSCALE: - if (val != 1) - return -EINVAL; - - val = (val2 * 1024) / 15625; - - switch (chan->type) { - case IIO_CAPACITANCE: - reg = AD7746_REG_CAP_GAINH; - break; - case IIO_VOLTAGE: - reg = AD7746_REG_VOLT_GAINH; - break; - default: - return -EINVAL; - } - - mutex_lock(&chip->lock); - ret = i2c_smbus_write_word_swapped(chip->client, reg, val); - mutex_unlock(&chip->lock); - if (ret < 0) - return ret; - - return 0; - case IIO_CHAN_INFO_CALIBBIAS: - if (val < 0 || val > 0xFFFF) - return -EINVAL; - - mutex_lock(&chip->lock); - ret = i2c_smbus_write_word_swapped(chip->client, - AD7746_REG_CAP_OFFH, val); - mutex_unlock(&chip->lock); - if (ret < 0) - return ret; - - return 0; - case IIO_CHAN_INFO_OFFSET: - case IIO_CHAN_INFO_ZEROPOINT: - if (val < 0 || val > 43008000) /* 21pF */ - return -EINVAL; - - /* - * CAPDAC Scale = 21pF_typ / 127 - * CIN Scale = 8.192pF / 2^24 - * Offset Scale = CAPDAC Scale / CIN Scale = 338646 - */ - - val /= 338646; - mutex_lock(&chip->lock); - chip->capdac[chan->channel][chan->differential] = val > 0 ? - FIELD_PREP(AD7746_CAPDAC_DACP_MASK, val) | AD7746_CAPDAC_DACEN : 0; - - ret = ad7746_set_capdac(chip, chan->channel); - if (ret < 0) { - mutex_unlock(&chip->lock); - return ret; - } - - chip->capdac_set = chan->channel; - mutex_unlock(&chip->lock); - - return 0; - case IIO_CHAN_INFO_SAMP_FREQ: - if (val2) - return -EINVAL; - - switch (chan->type) { - case IIO_CAPACITANCE: - mutex_lock(&chip->lock); - ret = ad7746_store_cap_filter_rate_setup(chip, val); - mutex_unlock(&chip->lock); - return ret; - case IIO_VOLTAGE: - mutex_lock(&chip->lock); - ret = ad7746_store_vt_filter_rate_setup(chip, val); - mutex_unlock(&chip->lock); - return ret; - default: - return -EINVAL; - } - default: - return -EINVAL; - } -} - -static const int ad7746_v_samp_freq[] = { 50, 31, 16, 8, }; -static const int ad7746_cap_samp_freq[] = { 91, 84, 50, 26, 16, 13, 11, 9, }; - -static int ad7746_read_avail(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, const int **vals, - int *type, int *length, long mask) -{ - if (mask != IIO_CHAN_INFO_SAMP_FREQ) - return -EINVAL; - - switch (chan->type) { - case IIO_VOLTAGE: - *vals = ad7746_v_samp_freq; - *length = ARRAY_SIZE(ad7746_v_samp_freq); - break; - case IIO_CAPACITANCE: - *vals = ad7746_cap_samp_freq; - *length = ARRAY_SIZE(ad7746_cap_samp_freq); - break; - default: - return -EINVAL; - } - *type = IIO_VAL_INT; - return IIO_AVAIL_LIST; -} - -static int ad7746_read_channel(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val) -{ - struct ad7746_chip_info *chip = iio_priv(indio_dev); - int ret, delay; - u8 data[3]; - u8 regval; - - ret = ad7746_select_channel(indio_dev, chan); - if (ret < 0) - return ret; - delay = ret; - - regval = chip->config | FIELD_PREP(AD7746_CONF_MODE_MASK, - AD7746_CONF_MODE_SINGLE_CONV); - ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG, regval); - if (ret < 0) - return ret; - - msleep(delay); - /* Now read the actual register */ - ret = i2c_smbus_read_i2c_block_data(chip->client, - ad7746_chan_info[chan->address].addr, - sizeof(data), data); - if (ret < 0) - return ret; - - /* - * Offset applied internally becaue the _offset userspace interface is - * needed for the CAP DACs which apply a controllable offset. - */ - *val = get_unaligned_be24(data) - 0x800000; - - return 0; -} - -static int ad7746_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) -{ - struct ad7746_chip_info *chip = iio_priv(indio_dev); - int ret, idx; - u8 reg; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - mutex_lock(&chip->lock); - ret = ad7746_read_channel(indio_dev, chan, val); - mutex_unlock(&chip->lock); - if (ret < 0) - return ret; - - return IIO_VAL_INT; - case IIO_CHAN_INFO_CALIBSCALE: - switch (chan->type) { - case IIO_CAPACITANCE: - reg = AD7746_REG_CAP_GAINH; - break; - case IIO_VOLTAGE: - reg = AD7746_REG_VOLT_GAINH; - break; - default: - return -EINVAL; - } - - mutex_lock(&chip->lock); - ret = i2c_smbus_read_word_swapped(chip->client, reg); - mutex_unlock(&chip->lock); - if (ret < 0) - return ret; - /* 1 + gain_val / 2^16 */ - *val = 1; - *val2 = (15625 * ret) / 1024; - - return IIO_VAL_INT_PLUS_MICRO; - case IIO_CHAN_INFO_CALIBBIAS: - mutex_lock(&chip->lock); - ret = i2c_smbus_read_word_swapped(chip->client, - AD7746_REG_CAP_OFFH); - mutex_unlock(&chip->lock); - if (ret < 0) - return ret; - *val = ret; - - return IIO_VAL_INT; - case IIO_CHAN_INFO_OFFSET: - case IIO_CHAN_INFO_ZEROPOINT: - *val = FIELD_GET(AD7746_CAPDAC_DACP_MASK, - chip->capdac[chan->channel][chan->differential]) * 338646; - - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - switch (chan->type) { - case IIO_CAPACITANCE: - /* 8.192pf / 2^24 */ - *val = 0; - *val2 = 488; - return IIO_VAL_INT_PLUS_NANO; - case IIO_VOLTAGE: - /* 1170mV / 2^23 */ - *val = 1170; - if (chan->channel == 1) - *val *= 6; - *val2 = 23; - return IIO_VAL_FRACTIONAL_LOG2; - case IIO_TEMP: - *val = 125; - *val2 = 8; - return IIO_VAL_FRACTIONAL_LOG2; - default: - return -EINVAL; - } - case IIO_CHAN_INFO_SAMP_FREQ: - switch (chan->type) { - case IIO_CAPACITANCE: - idx = FIELD_GET(AD7746_CONF_CAPFS_MASK, chip->config); - *val = ad7746_cap_filter_rate_table[idx][0]; - return IIO_VAL_INT; - case IIO_VOLTAGE: - idx = FIELD_GET(AD7746_CONF_VTFS_MASK, chip->config); - *val = ad7746_vt_filter_rate_table[idx][0]; - return IIO_VAL_INT; - default: - return -EINVAL; - } - default: - return -EINVAL; - } -} - -static const struct iio_info ad7746_info = { - .attrs = &ad7746_attribute_group, - .read_raw = ad7746_read_raw, - .read_avail = ad7746_read_avail, - .write_raw = ad7746_write_raw, -}; - -static int ad7746_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct device *dev = &client->dev; - struct ad7746_chip_info *chip; - struct iio_dev *indio_dev; - unsigned char regval = 0; - unsigned int vdd_permille; - int ret; - - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); - if (!indio_dev) - return -ENOMEM; - - chip = iio_priv(indio_dev); - mutex_init(&chip->lock); - - chip->client = client; - chip->capdac_set = -1; - - indio_dev->name = id->name; - indio_dev->info = &ad7746_info; - indio_dev->channels = ad7746_channels; - if (id->driver_data == 7746) - indio_dev->num_channels = ARRAY_SIZE(ad7746_channels); - else - indio_dev->num_channels = ARRAY_SIZE(ad7746_channels) - 2; - indio_dev->modes = INDIO_DIRECT_MODE; - - if (device_property_read_bool(dev, "adi,exca-output-en")) { - if (device_property_read_bool(dev, "adi,exca-output-invert")) - regval |= AD7746_EXCSETUP_NEXCA; - else - regval |= AD7746_EXCSETUP_EXCA; - } - - if (device_property_read_bool(dev, "adi,excb-output-en")) { - if (device_property_read_bool(dev, "adi,excb-output-invert")) - regval |= AD7746_EXCSETUP_NEXCB; - else - regval |= AD7746_EXCSETUP_EXCB; - } - - ret = device_property_read_u32(dev, "adi,excitation-vdd-permille", - &vdd_permille); - if (!ret) { - switch (vdd_permille) { - case 125: - regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 0); - break; - case 250: - regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 1); - break; - case 375: - regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 2); - break; - case 500: - regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 3); - break; - default: - break; - } - } - - ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_EXC_SETUP, - regval); - if (ret < 0) - return ret; - - return devm_iio_device_register(indio_dev->dev.parent, indio_dev); -} - -static const struct i2c_device_id ad7746_id[] = { - { "ad7745", 7745 }, - { "ad7746", 7746 }, - { "ad7747", 7747 }, - {} -}; -MODULE_DEVICE_TABLE(i2c, ad7746_id); - -static const struct of_device_id ad7746_of_match[] = { - { .compatible = "adi,ad7745" }, - { .compatible = "adi,ad7746" }, - { .compatible = "adi,ad7747" }, - { }, -}; -MODULE_DEVICE_TABLE(of, ad7746_of_match); - -static struct i2c_driver ad7746_driver = { - .driver = { - .name = KBUILD_MODNAME, - .of_match_table = ad7746_of_match, - }, - .probe = ad7746_probe, - .id_table = ad7746_id, -}; -module_i2c_driver(ad7746_driver); - -MODULE_AUTHOR("Michael Hennerich "); -MODULE_DESCRIPTION("Analog Devices AD7746/5/7 capacitive sensor driver"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From becbe550a36e027440ca0ccef2195e67c054e5bb Mon Sep 17 00:00:00 2001 From: Francesco Dolcini Date: Tue, 12 Jul 2022 18:33:44 +0200 Subject: dt-bindings: iio: adc: stmpe: Remove node name requirement STMPE driver does not require a specific node name anymore, only the compatible is checked, update binding according to this. Signed-off-by: Francesco Dolcini Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220712163345.445811-5-francesco.dolcini@toradex.com Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/st,stmpe-adc.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/adc/st,stmpe-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stmpe-adc.yaml index 9049c699152f..333744a2159c 100644 --- a/Documentation/devicetree/bindings/iio/adc/st,stmpe-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/st,stmpe-adc.yaml @@ -13,8 +13,7 @@ description: This ADC forms part of an ST microelectronics STMPE multifunction device . The ADC is shared with the STMPE touchscreen. As a result some ADC related settings are specified in the parent node. - The node name myst be stmpe_adc and should be a child node of the stmpe node - to which it belongs. + The node should be a child node of the stmpe node to which it belongs. properties: compatible: -- cgit v1.2.3 From 89aba5759891acb859ab6d34453a8b933e38ace5 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 7 Aug 2022 15:54:56 +0100 Subject: iio: test: Mark file local structure arrays static. Warning cleanup: drivers/iio/test/iio-test-rescale.c:32:30: warning: symbol 'scale_cases' was not declared. Should it be static? drivers/iio/test/iio-test-rescale.c:480:30: warning: symbol 'offset_cases' was not declared. Should it be static? Signed-off-by: Jonathan Cameron Cc: Liam Beguin Reviewed-by: Liam Beguin Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220807145457.646062-2-jic23@kernel.org --- drivers/iio/test/iio-test-rescale.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/test/iio-test-rescale.c b/drivers/iio/test/iio-test-rescale.c index cc782ccff880..31ee55a6faed 100644 --- a/drivers/iio/test/iio-test-rescale.c +++ b/drivers/iio/test/iio-test-rescale.c @@ -29,7 +29,7 @@ struct rescale_tc_data { const char *expected_off; }; -const struct rescale_tc_data scale_cases[] = { +static const struct rescale_tc_data scale_cases[] = { /* * Typical use cases */ @@ -477,7 +477,7 @@ const struct rescale_tc_data scale_cases[] = { }, }; -const struct rescale_tc_data offset_cases[] = { +static const struct rescale_tc_data offset_cases[] = { /* * Typical use cases */ -- cgit v1.2.3 From 282d16b628e4979eee692f5f93a936e5d613c926 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 7 Aug 2022 15:54:57 +0100 Subject: iio: light: cm32181: Mark the dev_pm_ops static. Only accessed from the local file. Warning: drivers/iio/light/cm32181.c:508:1: warning: symbol 'cm32181_pm_ops' was not declared. Should it be static? Fixes: 68c1b3dd5c48 ("iio: light: cm32181: Add PM support") Signed-off-by: Jonathan Cameron Cc: Kai-Heng Feng Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220807145457.646062-3-jic23@kernel.org --- drivers/iio/light/cm32181.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/light/cm32181.c b/drivers/iio/light/cm32181.c index edbe6a3138d0..001055d09750 100644 --- a/drivers/iio/light/cm32181.c +++ b/drivers/iio/light/cm32181.c @@ -505,7 +505,7 @@ static int cm32181_resume(struct device *dev) cm32181->conf_regs[CM32181_REG_ADDR_CMD]); } -DEFINE_SIMPLE_DEV_PM_OPS(cm32181_pm_ops, cm32181_suspend, cm32181_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(cm32181_pm_ops, cm32181_suspend, cm32181_resume); static const struct of_device_id cm32181_of_match[] = { { .compatible = "capella,cm3218" }, -- cgit v1.2.3 From e48668a38bf420c660b07851985e6922fcf4b194 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 7 Aug 2022 16:12:16 +0100 Subject: staging: iio: frequency: ad9834: Fix alignment for DMA safety ____cacheline_aligned is an insufficient guarantee for non-coherent DMA on platforms with 128 byte cachelines above L1. Switch to the updated IIO_DMA_MINALIGN definition. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220807151218.656881-3-jic23@kernel.org --- drivers/staging/iio/frequency/ad9834.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c index 94b131ef8a22..2b4267a87e65 100644 --- a/drivers/staging/iio/frequency/ad9834.c +++ b/drivers/staging/iio/frequency/ad9834.c @@ -83,7 +83,7 @@ struct ad9834_state { * DMA (thus cache coherency maintenance) requires the * transfer buffers to live in their own cache lines. */ - __be16 data ____cacheline_aligned; + __be16 data __aligned(IIO_DMA_MINALIGN); __be16 freq_data[2]; }; -- cgit v1.2.3 From 48a1319164d9339ad50a25085cad6b879fef9fbe Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 7 Aug 2022 16:12:17 +0100 Subject: staging: iio: meter: ade7854: Fix alignment for DMA safety ____cacheline_aligned is an insufficient guarantee for non-coherent DMA on platforms with 128 byte cachelines above L1. Switch to the updated IIO_DMA_MINALIGN definition. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Cc: Rodrigo Siqueira Link: https://lore.kernel.org/r/20220807151218.656881-4-jic23@kernel.org --- drivers/staging/iio/meter/ade7854.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/iio/meter/ade7854.h b/drivers/staging/iio/meter/ade7854.h index a51e6e3183d3..7a49f8f1016f 100644 --- a/drivers/staging/iio/meter/ade7854.h +++ b/drivers/staging/iio/meter/ade7854.h @@ -162,7 +162,7 @@ struct ade7854_state { int bits); int irq; struct mutex buf_lock; - u8 tx[ADE7854_MAX_TX] ____cacheline_aligned; + u8 tx[ADE7854_MAX_TX] __aligned(IIO_DMA_MINALIGN); u8 rx[ADE7854_MAX_RX]; }; -- cgit v1.2.3 From 4c0babbd978a98dfbdacbe078817ea9c953b3298 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 7 Aug 2022 16:12:18 +0100 Subject: staging: iio: resolver: ad2s1210: Fix alignment for DMA safety ____cacheline_aligned is an insufficient guarantee for non-coherent DMA on platforms with 128 byte cachelines above L1. Switch to the updated IIO_DMA_MINALIGN definition. As the tx[] an rx[] buffers are only used in the same SPI exchanges, we should be safe with them on the same cacheline. Hence only mark the first one __aligned(IIO_DMA_MINALIGN). Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220807151218.656881-5-jic23@kernel.org --- drivers/staging/iio/resolver/ad2s1210.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index c0b2716d0511..e4cf42438487 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -94,8 +94,8 @@ struct ad2s1210_state { bool hysteresis; u8 resolution; enum ad2s1210_mode mode; - u8 rx[2] ____cacheline_aligned; - u8 tx[2] ____cacheline_aligned; + u8 rx[2] __aligned(IIO_DMA_MINALIGN); + u8 tx[2]; }; static const int ad2s1210_mode_vals[4][2] = { -- cgit v1.2.3 From 1c4986f7e1fdbc44df66b37ee80722a2fcb02163 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 7 Aug 2022 17:21:21 +0100 Subject: iio: adc: mt6360: Drop an incorrect __maybe_unused marking. Given the struct platform_driver has one of it's elements assigned to point to the of_device_id table, it is never going to be unused. Drop the marking. Signed-off-by: Jonathan Cameron Cc: Gene Chen Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220807162121.862894-1-jic23@kernel.org --- drivers/iio/adc/mt6360-adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/mt6360-adc.c b/drivers/iio/adc/mt6360-adc.c index 35260d9e4e47..3710473e526f 100644 --- a/drivers/iio/adc/mt6360-adc.c +++ b/drivers/iio/adc/mt6360-adc.c @@ -353,7 +353,7 @@ static int mt6360_adc_probe(struct platform_device *pdev) return devm_iio_device_register(&pdev->dev, indio_dev); } -static const struct of_device_id __maybe_unused mt6360_adc_of_id[] = { +static const struct of_device_id mt6360_adc_of_id[] = { { .compatible = "mediatek,mt6360-adc", }, {} }; -- cgit v1.2.3 From c3b4afb1825b120481798512dd6a647804c5e521 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 7 Aug 2022 19:45:33 +0100 Subject: iio: magn: hmc5843: Drop excessive indentation of assignments of hmc5843_driver This formatting is odd, so fix it to be more standard. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220807184534.1037363-2-jic23@kernel.org --- drivers/iio/magnetometer/hmc5843_spi.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/iio/magnetometer/hmc5843_spi.c b/drivers/iio/magnetometer/hmc5843_spi.c index 8403f09aba39..310027a53342 100644 --- a/drivers/iio/magnetometer/hmc5843_spi.c +++ b/drivers/iio/magnetometer/hmc5843_spi.c @@ -86,13 +86,13 @@ static const struct spi_device_id hmc5843_id[] = { MODULE_DEVICE_TABLE(spi, hmc5843_id); static struct spi_driver hmc5843_driver = { - .driver = { - .name = "hmc5843", - .pm = HMC5843_PM_OPS, - }, - .id_table = hmc5843_id, - .probe = hmc5843_spi_probe, - .remove = hmc5843_spi_remove, + .driver = { + .name = "hmc5843", + .pm = HMC5843_PM_OPS, + }, + .id_table = hmc5843_id, + .probe = hmc5843_spi_probe, + .remove = hmc5843_spi_remove, }; module_spi_driver(hmc5843_driver); -- cgit v1.2.3 From 71041f73dc685ccaa7d150aa5eecc02609117739 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 7 Aug 2022 19:45:34 +0100 Subject: iio: magn: hmc5843: Move struct dev_pm_ops out of header Having this structure defined static in the header lead to unnecessary duplication and required additional symbol exports. Use the EXPORT_NS_SIMPLE_DEV_PM_OPS() to clean this up in the same fashion as many other drivers do this. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220807184534.1037363-3-jic23@kernel.org --- drivers/iio/magnetometer/hmc5843.h | 13 +------------ drivers/iio/magnetometer/hmc5843_core.c | 8 ++++---- drivers/iio/magnetometer/hmc5843_i2c.c | 2 +- drivers/iio/magnetometer/hmc5843_spi.c | 2 +- 4 files changed, 7 insertions(+), 18 deletions(-) diff --git a/drivers/iio/magnetometer/hmc5843.h b/drivers/iio/magnetometer/hmc5843.h index 9120c8bbf3dd..60fbb5431c88 100644 --- a/drivers/iio/magnetometer/hmc5843.h +++ b/drivers/iio/magnetometer/hmc5843.h @@ -52,16 +52,5 @@ int hmc5843_common_probe(struct device *dev, struct regmap *regmap, enum hmc5843_ids id, const char *name); void hmc5843_common_remove(struct device *dev); -int hmc5843_common_suspend(struct device *dev); -int hmc5843_common_resume(struct device *dev); - -#ifdef CONFIG_PM_SLEEP -static __maybe_unused SIMPLE_DEV_PM_OPS(hmc5843_pm_ops, - hmc5843_common_suspend, - hmc5843_common_resume); -#define HMC5843_PM_OPS (&hmc5843_pm_ops) -#else -#define HMC5843_PM_OPS NULL -#endif - +extern const struct dev_pm_ops hmc5843_pm_ops; #endif /* HMC5843_CORE_H */ diff --git a/drivers/iio/magnetometer/hmc5843_core.c b/drivers/iio/magnetometer/hmc5843_core.c index 4a63b2da9df0..c5521d61da29 100644 --- a/drivers/iio/magnetometer/hmc5843_core.c +++ b/drivers/iio/magnetometer/hmc5843_core.c @@ -603,19 +603,19 @@ static const struct iio_info hmc5843_info = { static const unsigned long hmc5843_scan_masks[] = {0x7, 0}; -int hmc5843_common_suspend(struct device *dev) +static int hmc5843_common_suspend(struct device *dev) { return hmc5843_set_mode(iio_priv(dev_get_drvdata(dev)), HMC5843_MODE_SLEEP); } -EXPORT_SYMBOL_NS(hmc5843_common_suspend, IIO_HMC5843); -int hmc5843_common_resume(struct device *dev) +static int hmc5843_common_resume(struct device *dev) { return hmc5843_set_mode(iio_priv(dev_get_drvdata(dev)), HMC5843_MODE_CONVERSION_CONTINUOUS); } -EXPORT_SYMBOL_NS(hmc5843_common_resume, IIO_HMC5843); +EXPORT_NS_SIMPLE_DEV_PM_OPS(hmc5843_pm_ops, hmc5843_common_suspend, + hmc5843_common_resume, IIO_HMC5843); int hmc5843_common_probe(struct device *dev, struct regmap *regmap, enum hmc5843_ids id, const char *name) diff --git a/drivers/iio/magnetometer/hmc5843_i2c.c b/drivers/iio/magnetometer/hmc5843_i2c.c index 8d2ff8fc204d..825a881d37fb 100644 --- a/drivers/iio/magnetometer/hmc5843_i2c.c +++ b/drivers/iio/magnetometer/hmc5843_i2c.c @@ -93,7 +93,7 @@ MODULE_DEVICE_TABLE(of, hmc5843_of_match); static struct i2c_driver hmc5843_driver = { .driver = { .name = "hmc5843", - .pm = HMC5843_PM_OPS, + .pm = pm_sleep_ptr(&hmc5843_pm_ops), .of_match_table = hmc5843_of_match, }, .id_table = hmc5843_id, diff --git a/drivers/iio/magnetometer/hmc5843_spi.c b/drivers/iio/magnetometer/hmc5843_spi.c index 310027a53342..c42d2e2a6a6c 100644 --- a/drivers/iio/magnetometer/hmc5843_spi.c +++ b/drivers/iio/magnetometer/hmc5843_spi.c @@ -88,7 +88,7 @@ MODULE_DEVICE_TABLE(spi, hmc5843_id); static struct spi_driver hmc5843_driver = { .driver = { .name = "hmc5843", - .pm = HMC5843_PM_OPS, + .pm = pm_sleep_ptr(&hmc5843_pm_ops), }, .id_table = hmc5843_id, .probe = hmc5843_spi_probe, -- cgit v1.2.3 From 8bbce0954fa1c695272421d047c53447fa709535 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 8 Aug 2022 22:47:28 +0200 Subject: iio: adc: ad7124: Benefit from devm_clk_get_enabled() to simplify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make use of devm_clk_get_enabled() to replace some code that effectively open codes this new function. Reviewed-by: Andy Shevchenko Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220808204740.307667-1-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7124.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index c5b785d8b241..4088786e1026 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -936,11 +936,6 @@ static void ad7124_reg_disable(void *r) regulator_disable(r); } -static void ad7124_clk_disable(void *c) -{ - clk_disable_unprepare(c); -} - static int ad7124_probe(struct spi_device *spi) { const struct ad7124_chip_info *info; @@ -993,18 +988,10 @@ static int ad7124_probe(struct spi_device *spi) return ret; } - st->mclk = devm_clk_get(&spi->dev, "mclk"); + st->mclk = devm_clk_get_enabled(&spi->dev, "mclk"); if (IS_ERR(st->mclk)) return PTR_ERR(st->mclk); - ret = clk_prepare_enable(st->mclk); - if (ret < 0) - return ret; - - ret = devm_add_action_or_reset(&spi->dev, ad7124_clk_disable, st->mclk); - if (ret) - return ret; - ret = ad7124_soft_reset(st); if (ret < 0) return ret; -- cgit v1.2.3 From 25f7e79515e76204ca03daaf77dab1e90ef97b33 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 8 Aug 2022 22:47:29 +0200 Subject: iio: adc: ad7768-1: Benefit from devm_clk_get_enabled() to simplify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make use of devm_clk_get_enabled() to replace some code that effectively open codes this new function. Reviewed-by: Andy Shevchenko Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220808204740.307667-2-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7768-1.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index 652db768ef37..70a25949142c 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -539,13 +539,6 @@ static void ad7768_regulator_disable(void *data) regulator_disable(st->vref); } -static void ad7768_clk_disable(void *data) -{ - struct ad7768_state *st = data; - - clk_disable_unprepare(st->mclk); -} - static int ad7768_set_channel_label(struct iio_dev *indio_dev, int num_channels) { @@ -600,18 +593,10 @@ static int ad7768_probe(struct spi_device *spi) if (ret) return ret; - st->mclk = devm_clk_get(&spi->dev, "mclk"); + st->mclk = devm_clk_get_enabled(&spi->dev, "mclk"); if (IS_ERR(st->mclk)) return PTR_ERR(st->mclk); - ret = clk_prepare_enable(st->mclk); - if (ret < 0) - return ret; - - ret = devm_add_action_or_reset(&spi->dev, ad7768_clk_disable, st); - if (ret) - return ret; - st->mclk_freq = clk_get_rate(st->mclk); mutex_init(&st->lock); -- cgit v1.2.3 From cdd07b3ab94a020570132558442a26e74b70bc42 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 8 Aug 2022 22:47:30 +0200 Subject: iio: adc: ad9467: Benefit from devm_clk_get_enabled() to simplify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make use of devm_clk_get_enabled() to replace some code that effectively open codes this new function. Reviewed-by: Andy Shevchenko Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220808204740.307667-3-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad9467.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c index 5a5f33f7bc8f..7534572f7475 100644 --- a/drivers/iio/adc/ad9467.c +++ b/drivers/iio/adc/ad9467.c @@ -378,13 +378,6 @@ static int ad9467_preenable_setup(struct adi_axi_adc_conv *conv) return ad9467_outputmode_set(st->spi, st->output_mode); } -static void ad9467_clk_disable(void *data) -{ - struct ad9467_state *st = data; - - clk_disable_unprepare(st->clk); -} - static int ad9467_probe(struct spi_device *spi) { const struct ad9467_chip_info *info; @@ -404,18 +397,10 @@ static int ad9467_probe(struct spi_device *spi) st = adi_axi_adc_conv_priv(conv); st->spi = spi; - st->clk = devm_clk_get(&spi->dev, "adc-clk"); + st->clk = devm_clk_get_enabled(&spi->dev, "adc-clk"); if (IS_ERR(st->clk)) return PTR_ERR(st->clk); - ret = clk_prepare_enable(st->clk); - if (ret < 0) - return ret; - - ret = devm_add_action_or_reset(&spi->dev, ad9467_clk_disable, st); - if (ret) - return ret; - st->pwrdown_gpio = devm_gpiod_get_optional(&spi->dev, "powerdown", GPIOD_OUT_LOW); if (IS_ERR(st->pwrdown_gpio)) -- cgit v1.2.3 From 51f2f910a551b3e8a23ea1d21b836716658908fe Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 8 Aug 2022 22:47:31 +0200 Subject: iio: adc: ingenic-adc: Benefit from devm_clk_get_prepared() to simplify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make use of devm_clk_get_prepared() to replace some code that effectively open codes this new function. Reviewed-by: Andy Shevchenko Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220808204740.307667-4-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ingenic-adc.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/drivers/iio/adc/ingenic-adc.c b/drivers/iio/adc/ingenic-adc.c index 9e08f3abeea6..a7325dbbb99a 100644 --- a/drivers/iio/adc/ingenic-adc.c +++ b/drivers/iio/adc/ingenic-adc.c @@ -734,11 +734,6 @@ static int ingenic_adc_fwnode_xlate(struct iio_dev *iio_dev, return -EINVAL; } -static void ingenic_adc_clk_cleanup(void *data) -{ - clk_unprepare(data); -} - static const struct iio_info ingenic_adc_info = { .write_raw = ingenic_adc_write_raw, .read_raw = ingenic_adc_read_raw, @@ -858,13 +853,13 @@ static int ingenic_adc_probe(struct platform_device *pdev) if (IS_ERR(adc->base)) return PTR_ERR(adc->base); - adc->clk = devm_clk_get(dev, "adc"); + adc->clk = devm_clk_get_prepared(dev, "adc"); if (IS_ERR(adc->clk)) { dev_err(dev, "Unable to get clock\n"); return PTR_ERR(adc->clk); } - ret = clk_prepare_enable(adc->clk); + ret = clk_enable(adc->clk); if (ret) { dev_err(dev, "Failed to enable clock\n"); return ret; @@ -893,12 +888,6 @@ static int ingenic_adc_probe(struct platform_device *pdev) usleep_range(2000, 3000); /* Must wait at least 2ms. */ clk_disable(adc->clk); - ret = devm_add_action_or_reset(dev, ingenic_adc_clk_cleanup, adc->clk); - if (ret) { - dev_err(dev, "Unable to add action\n"); - return ret; - } - iio_dev->name = "jz-adc"; iio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; iio_dev->setup_ops = &ingenic_buffer_setup_ops; -- cgit v1.2.3 From 4004912e0ce545074b6177c43a70bd36c9c28006 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 8 Aug 2022 22:47:32 +0200 Subject: iio: adc: lpc18xx: Benefit from devm_clk_get_enabled() to simplify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make use of devm_clk_get_enabled() to replace some code that effectively open codes this new function. Reviewed-by: Andy Shevchenko Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220808204740.307667-5-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/lpc18xx_adc.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/iio/adc/lpc18xx_adc.c b/drivers/iio/adc/lpc18xx_adc.c index 42e6cd6fa6f7..450a243d1f7c 100644 --- a/drivers/iio/adc/lpc18xx_adc.c +++ b/drivers/iio/adc/lpc18xx_adc.c @@ -121,11 +121,6 @@ static void lpc18xx_clear_cr_reg(void *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); @@ -151,7 +146,7 @@ static int lpc18xx_adc_probe(struct platform_device *pdev) if (IS_ERR(adc->base)) return PTR_ERR(adc->base); - adc->clk = devm_clk_get(&pdev->dev, NULL); + adc->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(adc->clk)) return dev_err_probe(&pdev->dev, PTR_ERR(adc->clk), "error getting clock\n"); @@ -177,17 +172,6 @@ static int lpc18xx_adc_probe(struct platform_device *pdev) if (ret) return ret; - ret = clk_prepare_enable(adc->clk); - if (ret) { - dev_err(&pdev->dev, "unable to enable clock\n"); - return ret; - } - - ret = devm_add_action_or_reset(&pdev->dev, lpc18xx_clk_disable, - adc->clk); - if (ret) - return ret; - rate = clk_get_rate(adc->clk); clkdiv = DIV_ROUND_UP(rate, LPC18XX_ADC_CLK_TARGET); -- cgit v1.2.3 From dcb9bd105c9e6a69848c68575fa174ce6d7e9549 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 8 Aug 2022 22:47:34 +0200 Subject: iio: adc: ti-ads131e08: Benefit from devm_clk_get_enabled() to simplify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make use of devm_clk_get_enabled() to replace some code that effectively open codes this new function. Reviewed-by: Andy Shevchenko Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220808204740.307667-7-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads131e08.c | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c index 32237cacc9a3..5235a93f28bc 100644 --- a/drivers/iio/adc/ti-ads131e08.c +++ b/drivers/iio/adc/ti-ads131e08.c @@ -797,13 +797,6 @@ static void ads131e08_regulator_disable(void *data) regulator_disable(st->vref_reg); } -static void ads131e08_clk_disable(void *data) -{ - struct ads131e08_state *st = data; - - clk_disable_unprepare(st->adc_clk); -} - static int ads131e08_probe(struct spi_device *spi) { const struct ads131e08_info *info; @@ -896,21 +889,11 @@ static int ads131e08_probe(struct spi_device *spi) st->vref_reg = NULL; } - st->adc_clk = devm_clk_get(&spi->dev, "adc-clk"); + st->adc_clk = devm_clk_get_enabled(&spi->dev, "adc-clk"); if (IS_ERR(st->adc_clk)) return dev_err_probe(&spi->dev, PTR_ERR(st->adc_clk), "failed to get the ADC clock\n"); - ret = clk_prepare_enable(st->adc_clk); - if (ret) { - dev_err(&spi->dev, "failed to prepare/enable the ADC clock\n"); - return ret; - } - - ret = devm_add_action_or_reset(&spi->dev, ads131e08_clk_disable, st); - if (ret) - return ret; - adc_clk_hz = clk_get_rate(st->adc_clk); if (!adc_clk_hz) { dev_err(&spi->dev, "failed to get the ADC clock rate\n"); -- cgit v1.2.3 From 40c0c1312c1b558463ddbf494d1f4569449cf11e Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 8 Aug 2022 22:47:35 +0200 Subject: iio: adc: xilinx-ams: Benefit from devm_clk_get_enabled() to simplify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make use of devm_clk_get_enabled() to replace some code that effectively open codes this new function. Reviewed-by: Andy Shevchenko Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220808204740.307667-8-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/xilinx-ams.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/drivers/iio/adc/xilinx-ams.c b/drivers/iio/adc/xilinx-ams.c index 9cd2713146e5..5b4bdf3a26bb 100644 --- a/drivers/iio/adc/xilinx-ams.c +++ b/drivers/iio/adc/xilinx-ams.c @@ -1351,11 +1351,6 @@ static const struct of_device_id ams_of_match_table[] = { }; MODULE_DEVICE_TABLE(of, ams_of_match_table); -static void ams_clk_disable_unprepare(void *data) -{ - clk_disable_unprepare(data); -} - static int ams_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; @@ -1380,18 +1375,10 @@ static int ams_probe(struct platform_device *pdev) if (IS_ERR(ams->base)) return PTR_ERR(ams->base); - ams->clk = devm_clk_get(&pdev->dev, NULL); + ams->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(ams->clk)) return PTR_ERR(ams->clk); - ret = clk_prepare_enable(ams->clk); - if (ret < 0) - return ret; - - ret = devm_add_action_or_reset(&pdev->dev, ams_clk_disable_unprepare, ams->clk); - if (ret < 0) - return ret; - ret = devm_delayed_work_autocancel(&pdev->dev, &ams->ams_unmask_work, ams_unmask_worker); if (ret < 0) -- cgit v1.2.3 From 72336966eedbea3e264be46968beabd29e907ca8 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 8 Aug 2022 22:47:36 +0200 Subject: iio: adc: xilinx-xadc: Benefit from devm_clk_get_enabled() to simplify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make use of devm_clk_get_enabled() to replace some code that effectively open codes this new function. Reviewed-by: Andy Shevchenko Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220808204740.307667-9-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/xilinx-xadc-core.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index 1b247722ba25..292f2892d223 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -1296,13 +1296,6 @@ static const char * const xadc_type_names[] = { [XADC_TYPE_US] = "xilinx-system-monitor", }; -static void xadc_clk_disable_unprepare(void *data) -{ - struct clk *clk = data; - - clk_disable_unprepare(clk); -} - static void xadc_cancel_delayed_work(void *data) { struct delayed_work *work = data; @@ -1374,19 +1367,10 @@ static int xadc_probe(struct platform_device *pdev) } } - xadc->clk = devm_clk_get(dev, NULL); + xadc->clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(xadc->clk)) return PTR_ERR(xadc->clk); - ret = clk_prepare_enable(xadc->clk); - if (ret) - return ret; - - ret = devm_add_action_or_reset(dev, - xadc_clk_disable_unprepare, xadc->clk); - if (ret) - return ret; - /* * Make sure not to exceed the maximum samplerate since otherwise the * resulting interrupt storm will soft-lock the system. -- cgit v1.2.3 From 3ea5b370afd4babbdff48775701bdd839dbb0407 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 8 Aug 2022 22:47:37 +0200 Subject: iio: frequency: adf4371: Benefit from devm_clk_get_enabled() to simplify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make use of devm_clk_get_enabled() to replace some code that effectively open codes this new function. Reviewed-by: Andy Shevchenko Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220808204740.307667-10-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/adf4371.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/drivers/iio/frequency/adf4371.c b/drivers/iio/frequency/adf4371.c index 135c8cedc33d..b27088464826 100644 --- a/drivers/iio/frequency/adf4371.c +++ b/drivers/iio/frequency/adf4371.c @@ -540,13 +540,6 @@ static int adf4371_setup(struct adf4371_state *st) return regmap_bulk_write(st->regmap, ADF4371_REG(0x30), st->buf, 5); } -static void adf4371_clk_disable(void *data) -{ - struct adf4371_state *st = data; - - clk_disable_unprepare(st->clkin); -} - static int adf4371_probe(struct spi_device *spi) { const struct spi_device_id *id = spi_get_device_id(spi); @@ -579,18 +572,10 @@ static int adf4371_probe(struct spi_device *spi) indio_dev->channels = st->chip_info->channels; indio_dev->num_channels = st->chip_info->num_channels; - st->clkin = devm_clk_get(&spi->dev, "clkin"); + st->clkin = devm_clk_get_enabled(&spi->dev, "clkin"); if (IS_ERR(st->clkin)) return PTR_ERR(st->clkin); - ret = clk_prepare_enable(st->clkin); - if (ret < 0) - return ret; - - ret = devm_add_action_or_reset(&spi->dev, adf4371_clk_disable, st); - if (ret) - return ret; - st->clkin_freq = clk_get_rate(st->clkin); ret = adf4371_setup(st); -- cgit v1.2.3 From 129a90cf81d510551e73aaaa1efb9befb0c17e52 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 8 Aug 2022 22:47:39 +0200 Subject: iio: frequency: adrf6780: Benefit from devm_clk_get_enabled() to simplify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make use of devm_clk_get_enabled() to replace some code that effectively open codes this new function. Reviewed-by: Andy Shevchenko Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220808204740.307667-12-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/adrf6780.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/drivers/iio/frequency/adrf6780.c b/drivers/iio/frequency/adrf6780.c index 21878bad0909..b4defb82f37e 100644 --- a/drivers/iio/frequency/adrf6780.c +++ b/drivers/iio/frequency/adrf6780.c @@ -441,11 +441,6 @@ static void adrf6780_properties_parse(struct adrf6780_state *st) 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 */ @@ -473,20 +468,11 @@ static int adrf6780_probe(struct spi_device *spi) adrf6780_properties_parse(st); - st->clkin = devm_clk_get(&spi->dev, "lo_in"); + st->clkin = devm_clk_get_enabled(&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); -- cgit v1.2.3 From 33dae107b6a9dfe92eba97b4a1df7402f9637f11 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 8 Aug 2022 22:47:40 +0200 Subject: iio: imu: adis16475: Benefit from devm_clk_get_enabled() to simplify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make use of devm_clk_get_enabled() to replace some code that effectively open codes this new function. Reviewed-by: Andy Shevchenko Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220808204740.307667-13-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16475.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c index ff2b0fab840a..aec55f7e1f26 100644 --- a/drivers/iio/imu/adis16475.c +++ b/drivers/iio/imu/adis16475.c @@ -1120,11 +1120,6 @@ check_burst32: return IRQ_HANDLED; } -static void adis16475_disable_clk(void *data) -{ - clk_disable_unprepare((struct clk *)data); -} - static int adis16475_config_sync_mode(struct adis16475 *st) { int ret; @@ -1150,19 +1145,11 @@ static int adis16475_config_sync_mode(struct adis16475 *st) /* All the other modes require external input signal */ if (sync->sync_mode != ADIS16475_SYNC_OUTPUT) { - struct clk *clk = devm_clk_get(dev, NULL); + struct clk *clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(clk)) return PTR_ERR(clk); - ret = clk_prepare_enable(clk); - if (ret) - return ret; - - ret = devm_add_action_or_reset(dev, adis16475_disable_clk, clk); - if (ret) - return ret; - st->clk_freq = clk_get_rate(clk); if (st->clk_freq < sync->min_rate || st->clk_freq > sync->max_rate) { -- cgit v1.2.3 From 21a60fce89c63cc674910d3dbb12177366f41643 Mon Sep 17 00:00:00 2001 From: Crt Mori Date: Mon, 8 Aug 2022 16:49:08 +0200 Subject: iio: temperature: mlx90632 Add supply regulator to sensor Provide possibility to toggle power supply to the sensor so that user can optimize their setup and not have the sensor constantly powered. Signed-off-by: Crt Mori Link: https://lore.kernel.org/r/20220808144908.1559069-1-cmo@melexis.com Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/mlx90632.c | 61 ++++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/drivers/iio/temperature/mlx90632.c b/drivers/iio/temperature/mlx90632.c index 7ee7ff8047a4..549c0ab5c2be 100644 --- a/drivers/iio/temperature/mlx90632.c +++ b/drivers/iio/temperature/mlx90632.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -128,6 +129,7 @@ * calculations * @object_ambient_temperature: Ambient temperature at object (might differ of * the ambient temperature of sensor. + * @regulator: Regulator of the device */ struct mlx90632_data { struct i2c_client *client; @@ -136,6 +138,7 @@ struct mlx90632_data { u16 emissivity; u8 mtyp; u32 object_ambient_temperature; + struct regulator *regulator; }; static const struct regmap_range mlx90632_volatile_reg_range[] = { @@ -207,6 +210,15 @@ static s32 mlx90632_pwr_continuous(struct regmap *regmap) MLX90632_PWR_STATUS_CONTINUOUS); } +/** + * mlx90632_reset_delay() - Give the mlx90632 some time to reset properly + * If this is not done, the following I2C command(s) will not be accepted. + */ +static void mlx90632_reset_delay(void) +{ + usleep_range(150, 200); +} + /** * mlx90632_perform_measurement() - Trigger and retrieve current measurement cycle * @data: pointer to mlx90632_data object containing regmap information @@ -248,11 +260,7 @@ static int mlx90632_set_meas_type(struct regmap *regmap, u8 type) if (ret < 0) return ret; - /* - * Give the mlx90632 some time to reset properly before sending a new I2C command - * if this is not done, the following I2C command(s) will not be accepted. - */ - usleep_range(150, 200); + mlx90632_reset_delay(); ret = regmap_write_bits(regmap, MLX90632_REG_CONTROL, (MLX90632_CFG_MTYP_MASK | MLX90632_CFG_PWR_MASK), @@ -841,6 +849,32 @@ static int mlx90632_wakeup(struct mlx90632_data *data) return mlx90632_pwr_continuous(data->regmap); } +static void mlx90632_disable_regulator(void *_data) +{ + struct mlx90632_data *data = _data; + int ret; + + ret = regulator_disable(data->regulator); + if (ret < 0) + dev_err(regmap_get_device(data->regmap), + "Failed to disable power regulator: %d\n", ret); +} + +static int mlx90632_enable_regulator(struct mlx90632_data *data) +{ + int ret; + + ret = regulator_enable(data->regulator); + if (ret < 0) { + dev_err(regmap_get_device(data->regmap), "Failed to enable power regulator!\n"); + return ret; + } + + mlx90632_reset_delay(); + + return ret; +} + static int mlx90632_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -876,6 +910,23 @@ static int mlx90632_probe(struct i2c_client *client, indio_dev->channels = mlx90632_channels; indio_dev->num_channels = ARRAY_SIZE(mlx90632_channels); + mlx90632->regulator = devm_regulator_get(&client->dev, "vdd"); + if (IS_ERR(mlx90632->regulator)) + return dev_err_probe(&client->dev, PTR_ERR(mlx90632->regulator), + "failed to get vdd regulator"); + + ret = mlx90632_enable_regulator(mlx90632); + if (ret < 0) + return ret; + + ret = devm_add_action_or_reset(&client->dev, mlx90632_disable_regulator, + mlx90632); + if (ret < 0) { + dev_err(&client->dev, "Failed to setup regulator cleanup action %d\n", + ret); + return ret; + } + ret = mlx90632_wakeup(mlx90632); if (ret < 0) { dev_err(&client->dev, "Wakeup failed: %d\n", ret); -- cgit v1.2.3 From ef6d997667cbb964dbc27339fefbe65ceabea13a Mon Sep 17 00:00:00 2001 From: Crt Mori Date: Mon, 8 Aug 2022 16:49:36 +0200 Subject: dt-bindings: iio: mlx90632 Add supply regulator documentation Document the newly added vdd supply option. Signed-off-by: Crt Mori Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220808144936.1559158-1-cmo@melexis.com Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/temperature/melexis,mlx90632.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/temperature/melexis,mlx90632.yaml b/Documentation/devicetree/bindings/iio/temperature/melexis,mlx90632.yaml index b547ddcd544a..4a55e7f25ae7 100644 --- a/Documentation/devicetree/bindings/iio/temperature/melexis,mlx90632.yaml +++ b/Documentation/devicetree/bindings/iio/temperature/melexis,mlx90632.yaml @@ -35,6 +35,9 @@ properties: maxItems: 1 description: Default is 0x3a, but can be reprogrammed. + vdd-supply: + description: provide VDD power to the sensor. + required: - compatible - reg @@ -50,6 +53,7 @@ examples: temp-sensor@3a { compatible = "melexis,mlx90632"; reg = <0x3a>; + vdd-supply = <&ldo4_reg>; }; }; ... -- cgit v1.2.3 From e137fafc8985cf152a4bb6f18ae83ebb06816df1 Mon Sep 17 00:00:00 2001 From: Jakob Hauser Date: Fri, 12 Aug 2022 23:54:06 +0200 Subject: iio: magnetometer: yas530: Change data type of hard_offsets to signed The "hard_offsets" are currently unsigned u8 but they should be signed as they can get negative. They are signed in function yas5xx_meaure_offsets() and in the Yamaha drivers [1][2]. [1] https://github.com/NovaFusion/android_kernel_samsung_golden/blob/cm-12.1/drivers/sensor/compass/yas.h#L156 [2] https://github.com/msm8916-mainline/android_kernel_qcom_msm8916/blob/GT-I9195I/drivers/iio/magnetometer/yas_mag_drv-yas532.c#L91 Fixes: de8860b1ed47 ("iio: magnetometer: Add driver for Yamaha YAS530") Signed-off-by: Jakob Hauser Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/40f052bf6491457d0c5c0ed4c3534dc6fa251c3c.1660337264.git.jahau@rocketmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/yamaha-yas530.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index aeaa4da6923b..d1f16729c60e 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -132,7 +132,7 @@ struct yas5xx { unsigned int version; char name[16]; struct yas5xx_calibration calibration; - u8 hard_offsets[3]; + s8 hard_offsets[3]; struct iio_mount_matrix orientation; struct regmap *map; struct regulator_bulk_data regs[2]; -- cgit v1.2.3 From 4efdfbc16cceffbcb29e53a5be55d83a4c76a9c0 Mon Sep 17 00:00:00 2001 From: Jakob Hauser Date: Fri, 12 Aug 2022 23:54:07 +0200 Subject: iio: magnetometer: yas530: Change range of data in volatile register In function yas5xx_volatile_reg(), register "YAS5XX_MEASURE_DATA + 8" shouldn't be volatile as we count from 0 to 7 here. Instead of lowering the number from 8 to 7, the operator "<=" is replaced by "<". The size of the measure data array is 8, therefore it's more natural to use 8 as a constant. This change is of low importance as the "+ 8" register isn't called. Signed-off-by: Jakob Hauser Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/dabba10feb80171350525ac874f944076c46e084.1660337264.git.jahau@rocketmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/yamaha-yas530.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index d1f16729c60e..76bff4818461 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -527,7 +527,7 @@ static bool yas5xx_volatile_reg(struct device *dev, unsigned int reg) { return reg == YAS5XX_ACTUATE_INIT_COIL || reg == YAS5XX_MEASURE || - (reg >= YAS5XX_MEASURE_DATA && reg <= YAS5XX_MEASURE_DATA + 8); + (reg >= YAS5XX_MEASURE_DATA && reg < YAS5XX_MEASURE_DATA + 8); } /* TODO: enable regmap cache, using mark dirty and sync at runtime resume */ -- cgit v1.2.3 From 413cf691633c985c3b49d005aa07b0e9c70c14a1 Mon Sep 17 00:00:00 2001 From: Jakob Hauser Date: Fri, 12 Aug 2022 23:54:08 +0200 Subject: iio: magnetometer: yas530: Correct scaling of magnetic axes Looks like YAS530 raw values return picotesla and YAS532 nanotesla. Adapt comments and scaling. Signed-off-by: Jakob Hauser Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/053ab05cb9a0f6b0536ab5e0de57009f513c6f81.1660337264.git.jahau@rocketmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/yamaha-yas530.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index 76bff4818461..199d83013e6f 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -310,8 +310,6 @@ static s32 yas5xx_linearize(struct yas5xx *yas5xx, u16 val, int axis) * @yo: Y axis out * @zo: Z axis out * @return: 0 on success or error code - * - * Returned values are in nanotesla according to some code. */ static int yas5xx_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, s32 *zo) { @@ -417,14 +415,27 @@ static int yas5xx_read_raw(struct iio_dev *indio_dev, *val = 1; return IIO_VAL_INT; } - /* - * The axis values are in nanotesla according to the vendor - * drivers, but is clearly in microtesla according to - * experiments. Since 1 uT = 0.01 Gauss, we need to divide - * by 100000000 (10^8) to get to Gauss from the raw value. - */ - *val = 1; - *val2 = 100000000; + switch (yas5xx->devid) { + case YAS530_DEVICE_ID: + /* + * Raw values of YAS530 are in picotesla. Divide by + * 100000000 (10^8) to get Gauss. + */ + *val = 1; + *val2 = 100000000; + break; + case YAS532_DEVICE_ID: + /* + * Raw values of YAS532 are in nanotesla. Divide by + * 100000 (10^5) to get Gauss. + */ + *val = 1; + *val2 = 100000; + break; + default: + dev_err(yas5xx->dev, "unknown device type\n"); + return -EINVAL; + } return IIO_VAL_FRACTIONAL; default: /* Unknown request */ -- cgit v1.2.3 From 8239f904f97c94b25a9983aa243090bb393abe81 Mon Sep 17 00:00:00 2001 From: Jakob Hauser Date: Fri, 12 Aug 2022 23:54:09 +0200 Subject: iio: magnetometer: yas530: Correct temperature handling The raw temperature value is a number of counts from a certain starting point. The resolution of the temperature counts is different for the YAS variants. Temperature compensation for YAS532 version AC seems to be handled differently. It uses the deviation from 20 degree Celsius [1] whereas YAS530 and older versions of YAS532 apply solely the t value as a multiplier [2][3]. In funtion yas5xx_read_raw(), add case IIO_CHAN_INFO_PROCESSED. Remove scale of temperature as this isn't applied. Additionally correct sign of temperature channel in iio_chan_spec. It's already defined that way in the yas5xx_get_measure() function. [1] https://github.com/msm8916-mainline/android_kernel_qcom_msm8916/blob/GT-I9195I/drivers/iio/magnetometer/yas_mag_drv-yas532.c#L442 [2] https://github.com/NovaFusion/android_kernel_samsung_golden/blob/cm-12.1/drivers/sensor/compass/yas_mag_driver-yas530.c#L881-L883 [3] https://github.com/LineageOS/android_kernel_samsung_msm8930-common/blob/lineage-18.1/drivers/sensors/geomagnetic/yas_mag_driver-yas53x.c#L856-L858 Signed-off-by: Jakob Hauser Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/eb45f328c7a81eb622c6a8cfc5c468ea58bbdace.1660337264.git.jahau@rocketmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/yamaha-yas530.c | 99 ++++++++++++++++++++++++-------- 1 file changed, 76 insertions(+), 23 deletions(-) diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index 199d83013e6f..6296ea3140ef 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -77,6 +77,7 @@ #define YAS530_DATA_BITS 12 #define YAS530_DATA_CENTER BIT(YAS530_DATA_BITS - 1) #define YAS530_DATA_OVERFLOW (BIT(YAS530_DATA_BITS) - 1) +#define YAS530_20DEGREES 182 /* Counts starting at -62 °C */ #define YAS532_DEVICE_ID 0x02 /* YAS532/YAS533 (MS-3R/F) */ #define YAS532_VERSION_AB 0 /* YAS532/533 AB (MS-3R/F AB) */ @@ -88,7 +89,7 @@ #define YAS532_DATA_BITS 13 #define YAS532_DATA_CENTER BIT(YAS532_DATA_BITS - 1) #define YAS532_DATA_OVERFLOW (BIT(YAS532_DATA_BITS) - 1) -#define YAS532_20DEGREES 390 /* Looks like Kelvin */ +#define YAS532_20DEGREES 390 /* Counts starting at -50 °C */ /* These variant IDs are known from code dumps */ #define YAS537_DEVICE_ID 0x07 /* YAS537 (MS-3T) */ @@ -314,7 +315,7 @@ static s32 yas5xx_linearize(struct yas5xx *yas5xx, u16 val, int axis) static int yas5xx_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, s32 *zo) { struct yas5xx_calibration *c = &yas5xx->calibration; - u16 t, x, y1, y2; + u16 t_ref, t, x, y1, y2; /* These are "signed x, signed y1 etc */ s32 sx, sy1, sy2, sy, sz; int ret; @@ -329,16 +330,46 @@ static int yas5xx_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, sy1 = yas5xx_linearize(yas5xx, y1, 1); sy2 = yas5xx_linearize(yas5xx, y2, 2); - /* - * Temperature compensation for x, y1, y2 respectively: - * - * Cx * t - * x' = x - ------ - * 100 - */ - sx = sx - (c->Cx * t) / 100; - sy1 = sy1 - (c->Cy1 * t) / 100; - sy2 = sy2 - (c->Cy2 * t) / 100; + /* Set the temperature reference value (unit: counts) */ + switch (yas5xx->devid) { + case YAS530_DEVICE_ID: + t_ref = YAS530_20DEGREES; + break; + case YAS532_DEVICE_ID: + t_ref = YAS532_20DEGREES; + break; + default: + dev_err(yas5xx->dev, "unknown device type\n"); + return -EINVAL; + } + + /* Temperature compensation for x, y1, y2 respectively */ + if (yas5xx->devid == YAS532_DEVICE_ID && + yas5xx->version == YAS532_VERSION_AC) { + /* + * YAS532 version AC uses the temperature deviation as a + * multiplier. + * + * Cx * (t - t_ref) + * x' = x - ---------------- + * 100 + */ + sx = sx - (c->Cx * (t - t_ref)) / 100; + sy1 = sy1 - (c->Cy1 * (t - t_ref)) / 100; + sy2 = sy2 - (c->Cy2 * (t - t_ref)) / 100; + } else { + /* + * YAS530 and YAS532 version AB use solely the t value as a + * multiplier. + * + * Cx * t + * x' = x - ------ + * 100 + */ + sx = sx - (c->Cx * t) / 100; + sy1 = sy1 - (c->Cy1 * t) / 100; + sy2 = sy2 - (c->Cy2 * t) / 100; + } /* * Break y1 and y2 into y and z, y1 and y2 are apparently encoding @@ -347,11 +378,37 @@ static int yas5xx_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, sy = sy1 - sy2; sz = -sy1 - sy2; - /* - * FIXME: convert to Celsius? Just guessing this is given - * as 1/10:s of degrees so multiply by 100 to get millicentigrades. - */ - *to = t * 100; + /* Process temperature readout */ + switch (yas5xx->devid) { + case YAS530_DEVICE_ID: + /* + * Raw temperature value t is the number of counts starting + * at -62 °C. Reference value t_ref is the number of counts + * between -62 °C and 20 °C (82 °C range). + * + * Temperature in °C would be (82 / t_ref * t) - 62. + * + * Contrary to this, perform multiplication first and division + * second due to calculating with integers. + * + * To get a nicer result, calculate with 1/10:s degrees Celsius + * and finally multiply by 100 to return millidegrees Celsius. + */ + *to = ((820 * t / t_ref) - 620) * 100; + break; + case YAS532_DEVICE_ID: + /* + * Actually same procedure for YAS532 but the starting point is + * at -50 °C. Reference value t_ref is the number of counts + * between -50 °C and 20 °C (70 °C range). + */ + *to = ((700 * t / t_ref) - 500) * 100; + break; + default: + dev_err(yas5xx->dev, "unknown device type\n"); + return -EINVAL; + } + /* * Calibrate [x,y,z] with some formulas like this: * @@ -384,6 +441,7 @@ static int yas5xx_read_raw(struct iio_dev *indio_dev, int ret; switch (mask) { + case IIO_CHAN_INFO_PROCESSED: case IIO_CHAN_INFO_RAW: pm_runtime_get_sync(yas5xx->dev); ret = yas5xx_get_measure(yas5xx, &t, &x, &y, &z); @@ -410,11 +468,6 @@ static int yas5xx_read_raw(struct iio_dev *indio_dev, } return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - if (chan->address == 0) { - /* Temperature is unscaled */ - *val = 1; - return IIO_VAL_INT; - } switch (yas5xx->devid) { case YAS530_DEVICE_ID: /* @@ -516,7 +569,7 @@ static const struct iio_chan_spec yas5xx_channels[] = { .address = 0, .scan_index = 0, .scan_type = { - .sign = 'u', + .sign = 's', .realbits = 32, .storagebits = 32, .endianness = IIO_CPU, -- cgit v1.2.3 From 0ca09faadef13bd6f7a59fa9f6858ccb88dac539 Mon Sep 17 00:00:00 2001 From: Jakob Hauser Date: Fri, 12 Aug 2022 23:54:10 +0200 Subject: iio: magnetometer: yas530: Change data type of calibration coefficients This is a preparation for adding YAS537 variant. YAS537 uses other data types on the calibration coefficients [1] than YAS530 [2] and YAS532 [3]. On YAS537, at least for a4 and a7 this could matter because 8-bit unsigned data from the register gets stored into a signed data type, therefore this should be 8-bit as well. For YAS530/532, on the other hand, it doesn't seem to matter. The size of a2-a9 and k is smaller than 8-bit at extraction, also the applied math is low. And Cx/Cy1/Cy2, now being defined as signed 16-bit, are extracted as unsigned 8-bit and undergo only minor math. [1] https://github.com/msm8916-mainline/android_kernel_qcom_msm8916/blob/GT-I9195I/drivers/iio/magnetometer/yas_mag_drv-yas537.c#L76-L78 [2] https://github.com/NovaFusion/android_kernel_samsung_golden/blob/cm-12.1/drivers/sensor/compass/yas_mag_driver-yas530.c#L526-L527 [3] https://github.com/msm8916-mainline/android_kernel_qcom_msm8916/blob/GT-I9195I/drivers/iio/magnetometer/yas_mag_drv-yas532.c#L76-L77 Signed-off-by: Jakob Hauser Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/f1e53bd6672aebe59f9b236b41374482edf888f8.1660337264.git.jahau@rocketmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/yamaha-yas530.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index 6296ea3140ef..a9d7cf3ad77f 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -103,9 +103,11 @@ struct yas5xx_calibration { s32 r[3]; u32 f[3]; /* Temperature compensation calibration */ - s32 Cx, Cy1, Cy2; + s16 Cx, Cy1, Cy2; /* Misc calibration coefficients */ - s32 a2, a3, a4, a5, a6, a7, a8, a9, k; + s8 a2, a3, a4, a6, a7, a8; + s16 a5, a9; + u8 k; /* clock divider */ u8 dck; }; -- cgit v1.2.3 From 6e3bfa97c5b8a9ea702ad0b8a28e703b6d561ac1 Mon Sep 17 00:00:00 2001 From: Jakob Hauser Date: Fri, 12 Aug 2022 23:54:11 +0200 Subject: iio: magnetometer: yas530: Rename functions and registers This is a preparation for adding the YAS537 variant. Functions that are used only by YAS530, YAS532 and YAS533 are renamed from yas5xx to yas530. Same for the registers. To avoid part listing in function and registers names, the name of the first variant is used. Where appropriate, comments were added that these functions are used by more than one variant. Functions that will be used by all variants including YAS537 remain in the naming scheme yas5xx. Or YAS5XX for registers, respectively. Signed-off-by: Jakob Hauser Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/344d3b2f5e050eab79ce9962c24781486774d9fb.1660337264.git.jahau@rocketmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/yamaha-yas530.c | 126 +++++++++++++++++-------------- 1 file changed, 70 insertions(+), 56 deletions(-) diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index a9d7cf3ad77f..40cd6bbc9952 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -40,20 +40,22 @@ #include -/* This register map covers YAS530 and YAS532 but differs in YAS 537 and YAS539 */ +/* Commonly used registers */ #define YAS5XX_DEVICE_ID 0x80 -#define YAS5XX_ACTUATE_INIT_COIL 0x81 -#define YAS5XX_MEASURE 0x82 -#define YAS5XX_CONFIG 0x83 -#define YAS5XX_MEASURE_INTERVAL 0x84 -#define YAS5XX_OFFSET_X 0x85 /* [-31 .. 31] */ -#define YAS5XX_OFFSET_Y1 0x86 /* [-31 .. 31] */ -#define YAS5XX_OFFSET_Y2 0x87 /* [-31 .. 31] */ -#define YAS5XX_TEST1 0x88 -#define YAS5XX_TEST2 0x89 -#define YAS5XX_CAL 0x90 #define YAS5XX_MEASURE_DATA 0xB0 +/* These registers are used by YAS530, YAS532 and YAS533 */ +#define YAS530_ACTUATE_INIT_COIL 0x81 +#define YAS530_MEASURE 0x82 +#define YAS530_CONFIG 0x83 +#define YAS530_MEASURE_INTERVAL 0x84 +#define YAS530_OFFSET_X 0x85 /* [-31 .. 31] */ +#define YAS530_OFFSET_Y1 0x86 /* [-31 .. 31] */ +#define YAS530_OFFSET_Y2 0x87 /* [-31 .. 31] */ +#define YAS530_TEST1 0x88 +#define YAS530_TEST2 0x89 +#define YAS530_CAL 0x90 + /* Bits in the YAS5xx config register */ #define YAS5XX_CONFIG_INTON BIT(0) /* Interrupt on? */ #define YAS5XX_CONFIG_INTHACT BIT(1) /* Interrupt active high? */ @@ -182,15 +184,17 @@ static u16 yas532_extract_axis(u8 *data) } /** - * yas5xx_measure() - Make a measure from the hardware + * yas530_measure() - Make a measure from the hardware * @yas5xx: The device state * @t: the raw temperature measurement * @x: the raw x axis measurement * @y1: the y1 axis measurement * @y2: the y2 axis measurement * @return: 0 on success or error code + * + * Used by YAS530, YAS532 and YAS533. */ -static int yas5xx_measure(struct yas5xx *yas5xx, u16 *t, u16 *x, u16 *y1, u16 *y2) +static int yas530_measure(struct yas5xx *yas5xx, u16 *t, u16 *x, u16 *y1, u16 *y2) { unsigned int busy; u8 data[8]; @@ -198,7 +202,7 @@ static int yas5xx_measure(struct yas5xx *yas5xx, u16 *t, u16 *x, u16 *y1, u16 *y u16 val; mutex_lock(&yas5xx->lock); - ret = regmap_write(yas5xx->map, YAS5XX_MEASURE, YAS5XX_MEASURE_START); + ret = regmap_write(yas5xx->map, YAS530_MEASURE, YAS5XX_MEASURE_START); if (ret < 0) goto out_unlock; @@ -264,7 +268,8 @@ out_unlock: return ret; } -static s32 yas5xx_linearize(struct yas5xx *yas5xx, u16 val, int axis) +/* Used by YAS530, YAS532 and YAS533 */ +static s32 yas530_linearize(struct yas5xx *yas5xx, u16 val, int axis) { struct yas5xx_calibration *c = &yas5xx->calibration; static const s32 yas532ac_coef[] = { @@ -306,15 +311,17 @@ static s32 yas5xx_linearize(struct yas5xx *yas5xx, u16 val, int axis) } /** - * yas5xx_get_measure() - Measure a sample of all axis and process + * yas530_get_measure() - Measure a sample of all axis and process * @yas5xx: The device state * @to: Temperature out * @xo: X axis out * @yo: Y axis out * @zo: Z axis out * @return: 0 on success or error code + * + * Used by YAS530, YAS532 and YAS533. */ -static int yas5xx_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, s32 *zo) +static int yas530_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, s32 *zo) { struct yas5xx_calibration *c = &yas5xx->calibration; u16 t_ref, t, x, y1, y2; @@ -323,14 +330,14 @@ static int yas5xx_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, int ret; /* We first get raw data that needs to be translated to [x,y,z] */ - ret = yas5xx_measure(yas5xx, &t, &x, &y1, &y2); + ret = yas530_measure(yas5xx, &t, &x, &y1, &y2); if (ret) return ret; /* Do some linearization if available */ - sx = yas5xx_linearize(yas5xx, x, 0); - sy1 = yas5xx_linearize(yas5xx, y1, 1); - sy2 = yas5xx_linearize(yas5xx, y2, 2); + sx = yas530_linearize(yas5xx, x, 0); + sy1 = yas530_linearize(yas5xx, y1, 1); + sy2 = yas530_linearize(yas5xx, y2, 2); /* Set the temperature reference value (unit: counts) */ switch (yas5xx->devid) { @@ -446,7 +453,7 @@ static int yas5xx_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_PROCESSED: case IIO_CHAN_INFO_RAW: pm_runtime_get_sync(yas5xx->dev); - ret = yas5xx_get_measure(yas5xx, &t, &x, &y, &z); + ret = yas530_get_measure(yas5xx, &t, &x, &y, &z); pm_runtime_mark_last_busy(yas5xx->dev); pm_runtime_put_autosuspend(yas5xx->dev); if (ret) @@ -505,7 +512,7 @@ static void yas5xx_fill_buffer(struct iio_dev *indio_dev) int ret; pm_runtime_get_sync(yas5xx->dev); - ret = yas5xx_get_measure(yas5xx, &t, &x, &y, &z); + ret = yas530_get_measure(yas5xx, &t, &x, &y, &z); pm_runtime_mark_last_busy(yas5xx->dev); pm_runtime_put_autosuspend(yas5xx->dev); if (ret) { @@ -591,8 +598,8 @@ static const struct iio_info yas5xx_info = { static bool yas5xx_volatile_reg(struct device *dev, unsigned int reg) { - return reg == YAS5XX_ACTUATE_INIT_COIL || - reg == YAS5XX_MEASURE || + return reg == YAS530_ACTUATE_INIT_COIL || + reg == YAS530_MEASURE || (reg >= YAS5XX_MEASURE_DATA && reg < YAS5XX_MEASURE_DATA + 8); } @@ -605,11 +612,13 @@ static const struct regmap_config yas5xx_regmap_config = { }; /** - * yas53x_extract_calibration() - extracts the a2-a9 and k calibration + * yas530_extract_calibration() - extracts the a2-a9 and k calibration * @data: the bitfield to use * @c: the calibration to populate + * + * Used by YAS530, YAS532 and YAS533. */ -static void yas53x_extract_calibration(u8 *data, struct yas5xx_calibration *c) +static void yas530_extract_calibration(u8 *data, struct yas5xx_calibration *c) { u64 val = get_unaligned_be64(data); @@ -647,12 +656,12 @@ static int yas530_get_calibration_data(struct yas5xx *yas5xx) int ret; /* Dummy read, first read is ALWAYS wrong */ - ret = regmap_bulk_read(yas5xx->map, YAS5XX_CAL, data, sizeof(data)); + ret = regmap_bulk_read(yas5xx->map, YAS530_CAL, data, sizeof(data)); if (ret) return ret; /* Actual calibration readout */ - ret = regmap_bulk_read(yas5xx->map, YAS5XX_CAL, data, sizeof(data)); + ret = regmap_bulk_read(yas5xx->map, YAS530_CAL, data, sizeof(data)); if (ret) return ret; dev_dbg(yas5xx->dev, "calibration data: %*ph\n", 14, data); @@ -664,7 +673,7 @@ static int yas530_get_calibration_data(struct yas5xx *yas5xx) c->Cx = data[0] * 6 - 768; c->Cy1 = data[1] * 6 - 768; c->Cy2 = data[2] * 6 - 768; - yas53x_extract_calibration(&data[3], c); + yas530_extract_calibration(&data[3], c); /* * Extract linearization: @@ -695,11 +704,11 @@ static int yas532_get_calibration_data(struct yas5xx *yas5xx) int ret; /* Dummy read, first read is ALWAYS wrong */ - ret = regmap_bulk_read(yas5xx->map, YAS5XX_CAL, data, sizeof(data)); + ret = regmap_bulk_read(yas5xx->map, YAS530_CAL, data, sizeof(data)); if (ret) return ret; /* Actual calibration readout */ - ret = regmap_bulk_read(yas5xx->map, YAS5XX_CAL, data, sizeof(data)); + ret = regmap_bulk_read(yas5xx->map, YAS530_CAL, data, sizeof(data)); if (ret) return ret; dev_dbg(yas5xx->dev, "calibration data: %*ph\n", 14, data); @@ -718,7 +727,7 @@ static int yas532_get_calibration_data(struct yas5xx *yas5xx) c->Cx = data[0] * 10 - 1280; c->Cy1 = data[1] * 10 - 1280; c->Cy2 = data[2] * 10 - 1280; - yas53x_extract_calibration(&data[3], c); + yas530_extract_calibration(&data[3], c); /* * Extract linearization: * Linearization layout in the 32 bits at byte 10: @@ -741,7 +750,8 @@ static int yas532_get_calibration_data(struct yas5xx *yas5xx) return 0; } -static void yas5xx_dump_calibration(struct yas5xx *yas5xx) +/* Used by YAS530, YAS532 and YAS533 */ +static void yas530_dump_calibration(struct yas5xx *yas5xx) { struct yas5xx_calibration *c = &yas5xx->calibration; @@ -764,20 +774,22 @@ static void yas5xx_dump_calibration(struct yas5xx *yas5xx) dev_dbg(yas5xx->dev, "dck = %d\n", c->dck); } -static int yas5xx_set_offsets(struct yas5xx *yas5xx, s8 ox, s8 oy1, s8 oy2) +/* Used by YAS530, YAS532 and YAS533 */ +static int yas530_set_offsets(struct yas5xx *yas5xx, s8 ox, s8 oy1, s8 oy2) { int ret; - ret = regmap_write(yas5xx->map, YAS5XX_OFFSET_X, ox); + ret = regmap_write(yas5xx->map, YAS530_OFFSET_X, ox); if (ret) return ret; - ret = regmap_write(yas5xx->map, YAS5XX_OFFSET_Y1, oy1); + ret = regmap_write(yas5xx->map, YAS530_OFFSET_Y1, oy1); if (ret) return ret; - return regmap_write(yas5xx->map, YAS5XX_OFFSET_Y2, oy2); + return regmap_write(yas5xx->map, YAS530_OFFSET_Y2, oy2); } -static s8 yas5xx_adjust_offset(s8 old, int bit, u16 center, u16 measure) +/* Used by YAS530, YAS532 and YAS533 */ +static s8 yas530_adjust_offset(s8 old, int bit, u16 center, u16 measure) { if (measure > center) return old + BIT(bit); @@ -786,7 +798,8 @@ static s8 yas5xx_adjust_offset(s8 old, int bit, u16 center, u16 measure) return old; } -static int yas5xx_meaure_offsets(struct yas5xx *yas5xx) +/* Used by YAS530, YAS532 and YAS533 */ +static int yas530_measure_offsets(struct yas5xx *yas5xx) { int ret; u16 center; @@ -795,7 +808,7 @@ static int yas5xx_meaure_offsets(struct yas5xx *yas5xx) int i; /* Actuate the init coil and measure offsets */ - ret = regmap_write(yas5xx->map, YAS5XX_ACTUATE_INIT_COIL, 0); + ret = regmap_write(yas5xx->map, YAS530_ACTUATE_INIT_COIL, 0); if (ret) return ret; @@ -829,26 +842,26 @@ static int yas5xx_meaure_offsets(struct yas5xx *yas5xx) oy2 = 0; for (i = 4; i >= 0; i--) { - ret = yas5xx_set_offsets(yas5xx, ox, oy1, oy2); + ret = yas530_set_offsets(yas5xx, ox, oy1, oy2); if (ret) return ret; - ret = yas5xx_measure(yas5xx, &t, &x, &y1, &y2); + ret = yas530_measure(yas5xx, &t, &x, &y1, &y2); if (ret) return ret; dev_dbg(yas5xx->dev, "measurement %d: x=%d, y1=%d, y2=%d\n", 5-i, x, y1, y2); - ox = yas5xx_adjust_offset(ox, i, center, x); - oy1 = yas5xx_adjust_offset(oy1, i, center, y1); - oy2 = yas5xx_adjust_offset(oy2, i, center, y2); + ox = yas530_adjust_offset(ox, i, center, x); + oy1 = yas530_adjust_offset(oy1, i, center, y1); + oy2 = yas530_adjust_offset(oy2, i, center, y2); } /* Needed for calibration algorithm */ yas5xx->hard_offsets[0] = ox; yas5xx->hard_offsets[1] = oy1; yas5xx->hard_offsets[2] = oy2; - ret = yas5xx_set_offsets(yas5xx, ox, oy1, oy2); + ret = yas530_set_offsets(yas5xx, ox, oy1, oy2); if (ret) return ret; @@ -857,27 +870,28 @@ static int yas5xx_meaure_offsets(struct yas5xx *yas5xx) return 0; } -static int yas5xx_power_on(struct yas5xx *yas5xx) +/* Used by YAS530, YAS532 and YAS533 */ +static int yas530_power_on(struct yas5xx *yas5xx) { unsigned int val; int ret; /* Zero the test registers */ - ret = regmap_write(yas5xx->map, YAS5XX_TEST1, 0); + ret = regmap_write(yas5xx->map, YAS530_TEST1, 0); if (ret) return ret; - ret = regmap_write(yas5xx->map, YAS5XX_TEST2, 0); + ret = regmap_write(yas5xx->map, YAS530_TEST2, 0); if (ret) return ret; /* Set up for no interrupts, calibrated clock divider */ val = FIELD_PREP(YAS5XX_CONFIG_CCK_MASK, yas5xx->calibration.dck); - ret = regmap_write(yas5xx->map, YAS5XX_CONFIG, val); + ret = regmap_write(yas5xx->map, YAS530_CONFIG, val); if (ret) return ret; /* Measure interval 0 (back-to-back?) */ - return regmap_write(yas5xx->map, YAS5XX_MEASURE_INTERVAL, 0); + return regmap_write(yas5xx->map, YAS530_MEASURE_INTERVAL, 0); } static int yas5xx_probe(struct i2c_client *i2c, @@ -959,11 +973,11 @@ static int yas5xx_probe(struct i2c_client *i2c, goto assert_reset; } - yas5xx_dump_calibration(yas5xx); - ret = yas5xx_power_on(yas5xx); + yas530_dump_calibration(yas5xx); + ret = yas530_power_on(yas5xx); if (ret) goto assert_reset; - ret = yas5xx_meaure_offsets(yas5xx); + ret = yas530_measure_offsets(yas5xx); if (ret) goto assert_reset; @@ -1062,7 +1076,7 @@ static int yas5xx_runtime_resume(struct device *dev) usleep_range(31000, 40000); gpiod_set_value_cansleep(yas5xx->reset, 0); - ret = yas5xx_power_on(yas5xx); + ret = yas530_power_on(yas5xx); if (ret) { dev_err(dev, "cannot power on\n"); goto out_reset; -- cgit v1.2.3 From bdef8dcfbb94dc16e9750cd109bb9b7d8edfa1ca Mon Sep 17 00:00:00 2001 From: Jakob Hauser Date: Fri, 12 Aug 2022 23:54:12 +0200 Subject: iio: magnetometer: yas530: Move printk %*ph parameters out from stack Use less stack by modifying %*ph parameters. While at it, in the function yas530_get_calibration_data(), the debug dump was extended to 16 elements as this is the size of the calibration data array of YAS530. Suggested-by: Andy Shevchenko Signed-off-by: Jakob Hauser Reviewed-by: Andy Shevchenko Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/93b50c20adb1b2acb4cddb1ab25755070edd7c07.1660337264.git.jahau@rocketmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/yamaha-yas530.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index 40cd6bbc9952..beb48f3a959c 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -664,7 +664,7 @@ static int yas530_get_calibration_data(struct yas5xx *yas5xx) ret = regmap_bulk_read(yas5xx->map, YAS530_CAL, data, sizeof(data)); if (ret) return ret; - dev_dbg(yas5xx->dev, "calibration data: %*ph\n", 14, data); + dev_dbg(yas5xx->dev, "calibration data: %16ph\n", data); add_device_randomness(data, sizeof(data)); yas5xx->version = data[15] & GENMASK(1, 0); @@ -711,7 +711,7 @@ static int yas532_get_calibration_data(struct yas5xx *yas5xx) ret = regmap_bulk_read(yas5xx->map, YAS530_CAL, data, sizeof(data)); if (ret) return ret; - dev_dbg(yas5xx->dev, "calibration data: %*ph\n", 14, data); + dev_dbg(yas5xx->dev, "calibration data: %14ph\n", data); /* Sanity check, is this all zeroes? */ if (memchr_inv(data, 0x00, 13) == NULL) { -- cgit v1.2.3 From 92d9c05ca7324aed6695b53d3ce11b3e1387f470 Mon Sep 17 00:00:00 2001 From: Jakob Hauser Date: Fri, 12 Aug 2022 23:54:13 +0200 Subject: iio: magnetometer: yas530: Apply documentation and style fixes This commit gathers several minor changes. In the device examples, "Xiaomi" is too generic, specific devices should be listed here. E.g. Xiaomi Redmi 2 seems to have YAS537 but it's not fully clear if this applies to all its variants. Samsung Galaxy S7 is often quoted in conjunction with YAS537. Removed defines for device IDs of YAS537 and YAS539, they are not needed so far. Signed-off-by: Jakob Hauser Reviewed-by: Andy Shevchenko Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/300e394a76eb30fa031ecb69b594e9f9a70dac42.1660337264.git.jahau@rocketmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/yamaha-yas530.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index beb48f3a959c..f81868de5995 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -10,7 +10,7 @@ * (YAS534 is a magnetic switch, not handled) * YAS535 MS-6C * YAS536 MS-3W - * YAS537 MS-3T (2015 Samsung Galaxy S6, Note 5, Xiaomi) + * YAS537 MS-3T (2015 Samsung Galaxy S6, Note 5, Galaxy S7) * YAS539 MS-3S (2018 Samsung Galaxy A7 SM-A750FN) * * Code functions found in the MPU3050 YAS530 and YAS532 drivers @@ -93,10 +93,6 @@ #define YAS532_DATA_OVERFLOW (BIT(YAS532_DATA_BITS) - 1) #define YAS532_20DEGREES 390 /* Counts starting at -50 °C */ -/* These variant IDs are known from code dumps */ -#define YAS537_DEVICE_ID 0x07 /* YAS537 (MS-3T) */ -#define YAS539_DEVICE_ID 0x08 /* YAS539 (MS-3S) */ - /* Turn off device regulators etc after 5 seconds of inactivity */ #define YAS5XX_AUTOSUSPEND_DELAY_MS 5000 @@ -325,7 +321,7 @@ static int yas530_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, { struct yas5xx_calibration *c = &yas5xx->calibration; u16 t_ref, t, x, y1, y2; - /* These are "signed x, signed y1 etc */ + /* These are signed x, signed y1 etc */ s32 sx, sy1, sy2, sy, sz; int ret; @@ -666,7 +662,10 @@ static int yas530_get_calibration_data(struct yas5xx *yas5xx) return ret; dev_dbg(yas5xx->dev, "calibration data: %16ph\n", data); + /* Contribute calibration data to the input pool for kernel entropy */ add_device_randomness(data, sizeof(data)); + + /* Extract version */ yas5xx->version = data[15] & GENMASK(1, 0); /* Extract the calibration from the bitfield */ @@ -693,6 +692,7 @@ static int yas530_get_calibration_data(struct yas5xx *yas5xx) c->r[0] = sign_extend32(FIELD_GET(GENMASK(28, 23), val), 5); c->r[1] = sign_extend32(FIELD_GET(GENMASK(20, 15), val), 5); c->r[2] = sign_extend32(FIELD_GET(GENMASK(12, 7), val), 5); + return 0; } @@ -714,12 +714,12 @@ static int yas532_get_calibration_data(struct yas5xx *yas5xx) dev_dbg(yas5xx->dev, "calibration data: %14ph\n", data); /* Sanity check, is this all zeroes? */ - if (memchr_inv(data, 0x00, 13) == NULL) { - if (!(data[13] & BIT(7))) - dev_warn(yas5xx->dev, "calibration is blank!\n"); - } + if (!memchr_inv(data, 0x00, 13) && !(data[13] & BIT(7))) + dev_warn(yas5xx->dev, "calibration is blank!\n"); + /* Contribute calibration data to the input pool for kernel entropy */ add_device_randomness(data, sizeof(data)); + /* Only one bit of version info reserved here as far as we know */ yas5xx->version = data[13] & BIT(0); @@ -728,6 +728,7 @@ static int yas532_get_calibration_data(struct yas5xx *yas5xx) c->Cy1 = data[1] * 10 - 1280; c->Cy2 = data[2] * 10 - 1280; yas530_extract_calibration(&data[3], c); + /* * Extract linearization: * Linearization layout in the 32 bits at byte 10: -- cgit v1.2.3 From a70f60e5b6b37e8b9f0967235bf89aebd2d9fbb9 Mon Sep 17 00:00:00 2001 From: Jakob Hauser Date: Fri, 12 Aug 2022 23:54:14 +0200 Subject: iio: magnetometer: yas530: Introduce "chip_info" structure Introduce the "chip_info" structure approach for better variant handling. The variant to be used is now chosen by the Device Tree (enum "chip_ids"), not by the chip ID in the register. However, there is a check to make sure they match (using integer "id_check"). Signed-off-by: Jakob Hauser Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/57236545107286771d351b95091bf56815d3717d.1660337264.git.jahau@rocketmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/yamaha-yas530.c | 98 ++++++++++++++++++++++++-------- 1 file changed, 74 insertions(+), 24 deletions(-) diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index f81868de5995..4fe7e8c820c3 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -96,6 +96,12 @@ /* Turn off device regulators etc after 5 seconds of inactivity */ #define YAS5XX_AUTOSUSPEND_DELAY_MS 5000 +enum chip_ids { + yas530, + yas532, + yas533, +}; + struct yas5xx_calibration { /* Linearization calibration x, y1, y2 */ s32 r[3]; @@ -110,12 +116,25 @@ struct yas5xx_calibration { u8 dck; }; +struct yas5xx; + +/** + * struct yas5xx_chip_info - device-specific data and function pointers + * @devid: device ID number + * @product_name: product name of the YAS variant + * @version_names: version letters or namings + */ +struct yas5xx_chip_info { + unsigned int devid; + char *product_name; + char *version_names[2]; +}; + /** * struct yas5xx - state container for the YAS5xx driver * @dev: parent device pointer - * @devid: device ID number + * @chip_info: device-specific data * @version: device version - * @name: device name * @calibration: calibration settings from the OTP storage * @hard_offsets: offsets for each axis measured with initcoil actuated * @orientation: mounting matrix, flipped axis etc @@ -129,9 +148,8 @@ struct yas5xx_calibration { */ struct yas5xx { struct device *dev; - unsigned int devid; + const struct yas5xx_chip_info *chip_info; unsigned int version; - char name[16]; struct yas5xx_calibration calibration; s8 hard_offsets[3]; struct iio_mount_matrix orientation; @@ -192,6 +210,7 @@ static u16 yas532_extract_axis(u8 *data) */ static int yas530_measure(struct yas5xx *yas5xx, u16 *t, u16 *x, u16 *y1, u16 *y2) { + const struct yas5xx_chip_info *ci = yas5xx->chip_info; unsigned int busy; u8 data[8]; int ret; @@ -222,7 +241,7 @@ static int yas530_measure(struct yas5xx *yas5xx, u16 *t, u16 *x, u16 *y1, u16 *y mutex_unlock(&yas5xx->lock); - switch (yas5xx->devid) { + switch (ci->devid) { case YAS530_DEVICE_ID: /* * The t value is 9 bits in big endian format @@ -267,6 +286,7 @@ out_unlock: /* Used by YAS530, YAS532 and YAS533 */ static s32 yas530_linearize(struct yas5xx *yas5xx, u16 val, int axis) { + const struct yas5xx_chip_info *ci = yas5xx->chip_info; struct yas5xx_calibration *c = &yas5xx->calibration; static const s32 yas532ac_coef[] = { YAS532_VERSION_AC_COEF_X, @@ -276,7 +296,7 @@ static s32 yas530_linearize(struct yas5xx *yas5xx, u16 val, int axis) s32 coef; /* Select coefficients */ - switch (yas5xx->devid) { + switch (ci->devid) { case YAS530_DEVICE_ID: if (yas5xx->version == YAS530_VERSION_A) coef = YAS530_VERSION_A_COEF; @@ -319,6 +339,7 @@ static s32 yas530_linearize(struct yas5xx *yas5xx, u16 val, int axis) */ static int yas530_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, s32 *zo) { + const struct yas5xx_chip_info *ci = yas5xx->chip_info; struct yas5xx_calibration *c = &yas5xx->calibration; u16 t_ref, t, x, y1, y2; /* These are signed x, signed y1 etc */ @@ -336,7 +357,7 @@ static int yas530_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, sy2 = yas530_linearize(yas5xx, y2, 2); /* Set the temperature reference value (unit: counts) */ - switch (yas5xx->devid) { + switch (ci->devid) { case YAS530_DEVICE_ID: t_ref = YAS530_20DEGREES; break; @@ -349,7 +370,7 @@ static int yas530_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, } /* Temperature compensation for x, y1, y2 respectively */ - if (yas5xx->devid == YAS532_DEVICE_ID && + if (ci->devid == YAS532_DEVICE_ID && yas5xx->version == YAS532_VERSION_AC) { /* * YAS532 version AC uses the temperature deviation as a @@ -384,7 +405,7 @@ static int yas530_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, sz = -sy1 - sy2; /* Process temperature readout */ - switch (yas5xx->devid) { + switch (ci->devid) { case YAS530_DEVICE_ID: /* * Raw temperature value t is the number of counts starting @@ -442,6 +463,7 @@ static int yas5xx_read_raw(struct iio_dev *indio_dev, long mask) { struct yas5xx *yas5xx = iio_priv(indio_dev); + const struct yas5xx_chip_info *ci = yas5xx->chip_info; s32 t, x, y, z; int ret; @@ -473,7 +495,7 @@ static int yas5xx_read_raw(struct iio_dev *indio_dev, } return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - switch (yas5xx->devid) { + switch (ci->devid) { case YAS530_DEVICE_ID: /* * Raw values of YAS530 are in picotesla. Divide by @@ -802,6 +824,7 @@ static s8 yas530_adjust_offset(s8 old, int bit, u16 center, u16 measure) /* Used by YAS530, YAS532 and YAS533 */ static int yas530_measure_offsets(struct yas5xx *yas5xx) { + const struct yas5xx_chip_info *ci = yas5xx->chip_info; int ret; u16 center; u16 t, x, y1, y2; @@ -814,7 +837,7 @@ static int yas530_measure_offsets(struct yas5xx *yas5xx) return ret; /* When the initcoil is active this should be around the center */ - switch (yas5xx->devid) { + switch (ci->devid) { case YAS530_DEVICE_ID: center = YAS530_DATA_CENTER; break; @@ -895,12 +918,32 @@ static int yas530_power_on(struct yas5xx *yas5xx) return regmap_write(yas5xx->map, YAS530_MEASURE_INTERVAL, 0); } +static const struct yas5xx_chip_info yas5xx_chip_info_tbl[] = { + [yas530] = { + .devid = YAS530_DEVICE_ID, + .product_name = "YAS530 MS-3E", + .version_names = { "A", "B" }, + }, + [yas532] = { + .devid = YAS532_DEVICE_ID, + .product_name = "YAS532 MS-3R", + .version_names = { "AB", "AC" }, + }, + [yas533] = { + .devid = YAS532_DEVICE_ID, + .product_name = "YAS533 MS-3F", + .version_names = { "AB", "AC" }, + }, +}; + static int yas5xx_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct iio_dev *indio_dev; struct device *dev = &i2c->dev; struct yas5xx *yas5xx; + const struct yas5xx_chip_info *ci; + int id_check; int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*yas5xx)); @@ -947,33 +990,40 @@ static int yas5xx_probe(struct i2c_client *i2c, goto assert_reset; } - ret = regmap_read(yas5xx->map, YAS5XX_DEVICE_ID, &yas5xx->devid); + yas5xx->chip_info = &yas5xx_chip_info_tbl[id->driver_data]; + ci = yas5xx->chip_info; + + ret = regmap_read(yas5xx->map, YAS5XX_DEVICE_ID, &id_check); if (ret) goto assert_reset; - switch (yas5xx->devid) { + if (id_check != ci->devid) { + ret = dev_err_probe(dev, -ENODEV, + "device ID %02x doesn't match %s\n", + id_check, id->name); + goto assert_reset; + } + + switch (ci->devid) { case YAS530_DEVICE_ID: ret = yas530_get_calibration_data(yas5xx); if (ret) goto assert_reset; - dev_info(dev, "detected YAS530 MS-3E %s", - yas5xx->version ? "B" : "A"); - strncpy(yas5xx->name, "yas530", sizeof(yas5xx->name)); break; case YAS532_DEVICE_ID: ret = yas532_get_calibration_data(yas5xx); if (ret) goto assert_reset; - dev_info(dev, "detected YAS532/YAS533 MS-3R/F %s", - yas5xx->version ? "AC" : "AB"); - strncpy(yas5xx->name, "yas532", sizeof(yas5xx->name)); break; default: ret = -ENODEV; - dev_err(dev, "unhandled device ID %02x\n", yas5xx->devid); + dev_err(dev, "unhandled device ID %02x\n", ci->devid); goto assert_reset; } + dev_info(dev, "detected %s %s\n", ci->product_name, + ci->version_names[yas5xx->version]); + yas530_dump_calibration(yas5xx); ret = yas530_power_on(yas5xx); if (ret) @@ -985,7 +1035,7 @@ static int yas5xx_probe(struct i2c_client *i2c, indio_dev->info = &yas5xx_info; indio_dev->available_scan_masks = yas5xx_scan_masks; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->name = yas5xx->name; + indio_dev->name = id->name; indio_dev->channels = yas5xx_channels; indio_dev->num_channels = ARRAY_SIZE(yas5xx_channels); @@ -1096,9 +1146,9 @@ static DEFINE_RUNTIME_DEV_PM_OPS(yas5xx_dev_pm_ops, yas5xx_runtime_suspend, yas5xx_runtime_resume, NULL); static const struct i2c_device_id yas5xx_id[] = { - {"yas530", }, - {"yas532", }, - {"yas533", }, + {"yas530", yas530 }, + {"yas532", yas532 }, + {"yas533", yas533 }, {} }; MODULE_DEVICE_TABLE(i2c, yas5xx_id); -- cgit v1.2.3 From dd9bd44f877d8935b7359f083626786cead98adb Mon Sep 17 00:00:00 2001 From: Jakob Hauser Date: Fri, 12 Aug 2022 23:54:15 +0200 Subject: iio: magnetometer: yas530: Add volatile registers to "chip_info" Add volatile registers to the "chip_info" structure to ease the handling of different YAS variants. Signed-off-by: Jakob Hauser Reviewed-by: Andy Shevchenko Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/aeba3877933ba9d2c920b459a9037d9186c15a4f.1660337264.git.jahau@rocketmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/yamaha-yas530.c | 38 +++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index 4fe7e8c820c3..fa317b975f8f 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -102,6 +102,11 @@ enum chip_ids { yas533, }; +static const int yas530_volatile_reg[] = { + YAS530_ACTUATE_INIT_COIL, + YAS530_MEASURE, +}; + struct yas5xx_calibration { /* Linearization calibration x, y1, y2 */ s32 r[3]; @@ -123,11 +128,15 @@ struct yas5xx; * @devid: device ID number * @product_name: product name of the YAS variant * @version_names: version letters or namings + * @volatile_reg: device-specific volatile registers + * @volatile_reg_qty: quantity of device-specific volatile registers */ struct yas5xx_chip_info { unsigned int devid; char *product_name; char *version_names[2]; + const int *volatile_reg; + int volatile_reg_qty; }; /** @@ -616,9 +625,26 @@ static const struct iio_info yas5xx_info = { static bool yas5xx_volatile_reg(struct device *dev, unsigned int reg) { - return reg == YAS530_ACTUATE_INIT_COIL || - reg == YAS530_MEASURE || - (reg >= YAS5XX_MEASURE_DATA && reg < YAS5XX_MEASURE_DATA + 8); + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct yas5xx *yas5xx = iio_priv(indio_dev); + const struct yas5xx_chip_info *ci = yas5xx->chip_info; + int reg_qty; + int i; + + if (reg >= YAS5XX_MEASURE_DATA && reg < YAS5XX_MEASURE_DATA + 8) + return true; + + /* + * YAS versions share different registers on the same address, + * need to differentiate. + */ + reg_qty = ci->volatile_reg_qty; + for (i = 0; i < reg_qty; i++) { + if (reg == ci->volatile_reg[i]) + return true; + } + + return false; } /* TODO: enable regmap cache, using mark dirty and sync at runtime resume */ @@ -923,16 +949,22 @@ static const struct yas5xx_chip_info yas5xx_chip_info_tbl[] = { .devid = YAS530_DEVICE_ID, .product_name = "YAS530 MS-3E", .version_names = { "A", "B" }, + .volatile_reg = yas530_volatile_reg, + .volatile_reg_qty = ARRAY_SIZE(yas530_volatile_reg), }, [yas532] = { .devid = YAS532_DEVICE_ID, .product_name = "YAS532 MS-3R", .version_names = { "AB", "AC" }, + .volatile_reg = yas530_volatile_reg, + .volatile_reg_qty = ARRAY_SIZE(yas530_volatile_reg), }, [yas533] = { .devid = YAS532_DEVICE_ID, .product_name = "YAS533 MS-3F", .version_names = { "AB", "AC" }, + .volatile_reg = yas530_volatile_reg, + .volatile_reg_qty = ARRAY_SIZE(yas530_volatile_reg), }, }; -- cgit v1.2.3 From 913fd409668b7fd54db5efd979417a9f94dcc79b Mon Sep 17 00:00:00 2001 From: Jakob Hauser Date: Sat, 13 Aug 2022 00:05:00 +0200 Subject: iio: magnetometer: yas530: Add IIO scaling to "chip_info" Add IIO scaling to the "chip_info" structure to ease the handling to different YAS variants. Signed-off-by: Jakob Hauser Reviewed-by: Andy Shevchenko Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/a12f892633bbee13a8856c231dc793ebbc5d3a03.1660337264.git.jahau@rocketmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/yamaha-yas530.c | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index fa317b975f8f..af5c090098fb 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -130,6 +130,7 @@ struct yas5xx; * @version_names: version letters or namings * @volatile_reg: device-specific volatile registers * @volatile_reg_qty: quantity of device-specific volatile registers + * @scaling_val2: scaling value for IIO_CHAN_INFO_SCALE */ struct yas5xx_chip_info { unsigned int devid; @@ -137,6 +138,7 @@ struct yas5xx_chip_info { char *version_names[2]; const int *volatile_reg; int volatile_reg_qty; + u32 scaling_val2; }; /** @@ -504,27 +506,8 @@ static int yas5xx_read_raw(struct iio_dev *indio_dev, } return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - switch (ci->devid) { - case YAS530_DEVICE_ID: - /* - * Raw values of YAS530 are in picotesla. Divide by - * 100000000 (10^8) to get Gauss. - */ - *val = 1; - *val2 = 100000000; - break; - case YAS532_DEVICE_ID: - /* - * Raw values of YAS532 are in nanotesla. Divide by - * 100000 (10^5) to get Gauss. - */ - *val = 1; - *val2 = 100000; - break; - default: - dev_err(yas5xx->dev, "unknown device type\n"); - return -EINVAL; - } + *val = 1; + *val2 = ci->scaling_val2; return IIO_VAL_FRACTIONAL; default: /* Unknown request */ @@ -951,6 +934,7 @@ static const struct yas5xx_chip_info yas5xx_chip_info_tbl[] = { .version_names = { "A", "B" }, .volatile_reg = yas530_volatile_reg, .volatile_reg_qty = ARRAY_SIZE(yas530_volatile_reg), + .scaling_val2 = 100000000, /* picotesla to Gauss */ }, [yas532] = { .devid = YAS532_DEVICE_ID, @@ -958,6 +942,7 @@ static const struct yas5xx_chip_info yas5xx_chip_info_tbl[] = { .version_names = { "AB", "AC" }, .volatile_reg = yas530_volatile_reg, .volatile_reg_qty = ARRAY_SIZE(yas530_volatile_reg), + .scaling_val2 = 100000, /* nanotesla to Gauss */ }, [yas533] = { .devid = YAS532_DEVICE_ID, @@ -965,6 +950,7 @@ static const struct yas5xx_chip_info yas5xx_chip_info_tbl[] = { .version_names = { "AB", "AC" }, .volatile_reg = yas530_volatile_reg, .volatile_reg_qty = ARRAY_SIZE(yas530_volatile_reg), + .scaling_val2 = 100000, /* nanotesla to Gauss */ }, }; -- cgit v1.2.3 From 2d6676ecbe6a39fb1e002b89781e4158e77a784e Mon Sep 17 00:00:00 2001 From: Jakob Hauser Date: Sat, 13 Aug 2022 00:05:01 +0200 Subject: iio: magnetometer: yas530: Add temperature calculation to "chip_info" Add temperature calculation to the "chip_info" structure to ease the handling of different YAS variants. Signed-off-by: Jakob Hauser Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/1a8bffdb7e807455620a73f2d61981e7f9aab8d5.1660337264.git.jahau@rocketmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/yamaha-yas530.c | 123 ++++++++++++++----------------- 1 file changed, 54 insertions(+), 69 deletions(-) diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index af5c090098fb..a5d3f0bff024 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -79,7 +79,6 @@ #define YAS530_DATA_BITS 12 #define YAS530_DATA_CENTER BIT(YAS530_DATA_BITS - 1) #define YAS530_DATA_OVERFLOW (BIT(YAS530_DATA_BITS) - 1) -#define YAS530_20DEGREES 182 /* Counts starting at -62 °C */ #define YAS532_DEVICE_ID 0x02 /* YAS532/YAS533 (MS-3R/F) */ #define YAS532_VERSION_AB 0 /* YAS532/533 AB (MS-3R/F AB) */ @@ -91,7 +90,6 @@ #define YAS532_DATA_BITS 13 #define YAS532_DATA_CENTER BIT(YAS532_DATA_BITS - 1) #define YAS532_DATA_OVERFLOW (BIT(YAS532_DATA_BITS) - 1) -#define YAS532_20DEGREES 390 /* Counts starting at -50 °C */ /* Turn off device regulators etc after 5 seconds of inactivity */ #define YAS5XX_AUTOSUSPEND_DELAY_MS 5000 @@ -131,6 +129,14 @@ struct yas5xx; * @volatile_reg: device-specific volatile registers * @volatile_reg_qty: quantity of device-specific volatile registers * @scaling_val2: scaling value for IIO_CHAN_INFO_SCALE + * @t_ref: number of counts at reference temperature 20 °C + * @min_temp_x10: starting point of temperature counting in 1/10:s degrees Celsius + * + * The "t_ref" value for YAS532/533 is known from the Android driver. + * For YAS530 it was approximately measured. + * + * The temperatures "min_temp_x10" are derived from the temperature resolutions + * given in the data sheets. */ struct yas5xx_chip_info { unsigned int devid; @@ -139,6 +145,8 @@ struct yas5xx_chip_info { const int *volatile_reg; int volatile_reg_qty; u32 scaling_val2; + u16 t_ref; + s16 min_temp_x10; }; /** @@ -337,6 +345,22 @@ static s32 yas530_linearize(struct yas5xx *yas5xx, u16 val, int axis) (yas5xx->hard_offsets[axis] - c->r[axis]) * coef; } +static s32 yas5xx_calc_temperature(struct yas5xx *yas5xx, u16 t) +{ + const struct yas5xx_chip_info *ci = yas5xx->chip_info; + s32 to; + u16 t_ref; + s16 min_temp_x10; + int ref_temp_x10; + + t_ref = ci->t_ref; + min_temp_x10 = ci->min_temp_x10; + ref_temp_x10 = 200; + + to = (min_temp_x10 + ((ref_temp_x10 - min_temp_x10) * t / t_ref)) * 100; + return to; +} + /** * yas530_get_measure() - Measure a sample of all axis and process * @yas5xx: The device state @@ -352,7 +376,7 @@ static int yas530_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, { const struct yas5xx_chip_info *ci = yas5xx->chip_info; struct yas5xx_calibration *c = &yas5xx->calibration; - u16 t_ref, t, x, y1, y2; + u16 t_ref, t_comp, t, x, y1, y2; /* These are signed x, signed y1 etc */ s32 sx, sy1, sy2, sy, sz; int ret; @@ -367,47 +391,30 @@ static int yas530_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, sy1 = yas530_linearize(yas5xx, y1, 1); sy2 = yas530_linearize(yas5xx, y2, 2); - /* Set the temperature reference value (unit: counts) */ - switch (ci->devid) { - case YAS530_DEVICE_ID: - t_ref = YAS530_20DEGREES; - break; - case YAS532_DEVICE_ID: - t_ref = YAS532_20DEGREES; - break; - default: - dev_err(yas5xx->dev, "unknown device type\n"); - return -EINVAL; - } - - /* Temperature compensation for x, y1, y2 respectively */ + /* + * Set the temperature for compensation (unit: counts): + * YAS532/YAS533 version AC uses the temperature deviation as a + * multiplier. YAS530 and YAS532 version AB use solely the t value. + */ + t_ref = ci->t_ref; if (ci->devid == YAS532_DEVICE_ID && yas5xx->version == YAS532_VERSION_AC) { - /* - * YAS532 version AC uses the temperature deviation as a - * multiplier. - * - * Cx * (t - t_ref) - * x' = x - ---------------- - * 100 - */ - sx = sx - (c->Cx * (t - t_ref)) / 100; - sy1 = sy1 - (c->Cy1 * (t - t_ref)) / 100; - sy2 = sy2 - (c->Cy2 * (t - t_ref)) / 100; + t_comp = t - t_ref; } else { - /* - * YAS530 and YAS532 version AB use solely the t value as a - * multiplier. - * - * Cx * t - * x' = x - ------ - * 100 - */ - sx = sx - (c->Cx * t) / 100; - sy1 = sy1 - (c->Cy1 * t) / 100; - sy2 = sy2 - (c->Cy2 * t) / 100; + t_comp = t; } + /* + * Temperature compensation for x, y1, y2 respectively: + * + * Cx * t_comp + * x' = x - ----------- + * 100 + */ + sx = sx - (c->Cx * t_comp) / 100; + sy1 = sy1 - (c->Cy1 * t_comp) / 100; + sy2 = sy2 - (c->Cy2 * t_comp) / 100; + /* * Break y1 and y2 into y and z, y1 and y2 are apparently encoding * y and z. @@ -415,36 +422,8 @@ static int yas530_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, sy = sy1 - sy2; sz = -sy1 - sy2; - /* Process temperature readout */ - switch (ci->devid) { - case YAS530_DEVICE_ID: - /* - * Raw temperature value t is the number of counts starting - * at -62 °C. Reference value t_ref is the number of counts - * between -62 °C and 20 °C (82 °C range). - * - * Temperature in °C would be (82 / t_ref * t) - 62. - * - * Contrary to this, perform multiplication first and division - * second due to calculating with integers. - * - * To get a nicer result, calculate with 1/10:s degrees Celsius - * and finally multiply by 100 to return millidegrees Celsius. - */ - *to = ((820 * t / t_ref) - 620) * 100; - break; - case YAS532_DEVICE_ID: - /* - * Actually same procedure for YAS532 but the starting point is - * at -50 °C. Reference value t_ref is the number of counts - * between -50 °C and 20 °C (70 °C range). - */ - *to = ((700 * t / t_ref) - 500) * 100; - break; - default: - dev_err(yas5xx->dev, "unknown device type\n"); - return -EINVAL; - } + /* Calculate temperature readout */ + *to = yas5xx_calc_temperature(yas5xx, t); /* * Calibrate [x,y,z] with some formulas like this: @@ -935,6 +914,8 @@ static const struct yas5xx_chip_info yas5xx_chip_info_tbl[] = { .volatile_reg = yas530_volatile_reg, .volatile_reg_qty = ARRAY_SIZE(yas530_volatile_reg), .scaling_val2 = 100000000, /* picotesla to Gauss */ + .t_ref = 182, /* counts */ + .min_temp_x10 = -620, /* 1/10:s degrees Celsius */ }, [yas532] = { .devid = YAS532_DEVICE_ID, @@ -943,6 +924,8 @@ static const struct yas5xx_chip_info yas5xx_chip_info_tbl[] = { .volatile_reg = yas530_volatile_reg, .volatile_reg_qty = ARRAY_SIZE(yas530_volatile_reg), .scaling_val2 = 100000, /* nanotesla to Gauss */ + .t_ref = 390, /* counts */ + .min_temp_x10 = -500, /* 1/10:s degrees Celsius */ }, [yas533] = { .devid = YAS532_DEVICE_ID, @@ -951,6 +934,8 @@ static const struct yas5xx_chip_info yas5xx_chip_info_tbl[] = { .volatile_reg = yas530_volatile_reg, .volatile_reg_qty = ARRAY_SIZE(yas530_volatile_reg), .scaling_val2 = 100000, /* nanotesla to Gauss */ + .t_ref = 390, /* counts */ + .min_temp_x10 = -500, /* 1/10:s degrees Celsius */ }, }; -- cgit v1.2.3 From 059ff0f9a10508c39f2c22d4144e88156bbf86ef Mon Sep 17 00:00:00 2001 From: Jakob Hauser Date: Sat, 13 Aug 2022 00:05:02 +0200 Subject: iio: magnetometer: yas530: Add function pointers to "chip_info" Add function pointers to the "chip_info" structure to ease the handling of different YAS variants. In the function yas5xx_probe(), the function call for "measure_offsets" was added as a conditional "if (ci->measure_offsets)". This is a preparatory step for YAS537, as this variant doesn't need an offset measurement. Signed-off-by: Jakob Hauser Reviewed-by: Andy Shevchenko Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/4bd3f96262e0132b7f9720521a801da3c18abd95.1660337264.git.jahau@rocketmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/yamaha-yas530.c | 66 ++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index a5d3f0bff024..18933d8937ae 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -131,6 +131,11 @@ struct yas5xx; * @scaling_val2: scaling value for IIO_CHAN_INFO_SCALE * @t_ref: number of counts at reference temperature 20 °C * @min_temp_x10: starting point of temperature counting in 1/10:s degrees Celsius + * @get_measure: function pointer to get a measurement + * @get_calibration_data: function pointer to get calibration data + * @dump_calibration: function pointer to dump calibration for debugging + * @measure_offsets: function pointer to measure the offsets + * @power_on: function pointer to power-on procedure * * The "t_ref" value for YAS532/533 is known from the Android driver. * For YAS530 it was approximately measured. @@ -147,12 +152,17 @@ struct yas5xx_chip_info { u32 scaling_val2; u16 t_ref; s16 min_temp_x10; + int (*get_measure)(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, s32 *zo); + int (*get_calibration_data)(struct yas5xx *yas5xx); + void (*dump_calibration)(struct yas5xx *yas5xx); + int (*measure_offsets)(struct yas5xx *yas5xx); + int (*power_on)(struct yas5xx *yas5xx); }; /** * struct yas5xx - state container for the YAS5xx driver * @dev: parent device pointer - * @chip_info: device-specific data + * @chip_info: device-specific data and function pointers * @version: device version * @calibration: calibration settings from the OTP storage * @hard_offsets: offsets for each axis measured with initcoil actuated @@ -461,7 +471,7 @@ static int yas5xx_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_PROCESSED: case IIO_CHAN_INFO_RAW: pm_runtime_get_sync(yas5xx->dev); - ret = yas530_get_measure(yas5xx, &t, &x, &y, &z); + ret = ci->get_measure(yas5xx, &t, &x, &y, &z); pm_runtime_mark_last_busy(yas5xx->dev); pm_runtime_put_autosuspend(yas5xx->dev); if (ret) @@ -497,11 +507,12 @@ static int yas5xx_read_raw(struct iio_dev *indio_dev, static void yas5xx_fill_buffer(struct iio_dev *indio_dev) { struct yas5xx *yas5xx = iio_priv(indio_dev); + const struct yas5xx_chip_info *ci = yas5xx->chip_info; s32 t, x, y, z; int ret; pm_runtime_get_sync(yas5xx->dev); - ret = yas530_get_measure(yas5xx, &t, &x, &y, &z); + ret = ci->get_measure(yas5xx, &t, &x, &y, &z); pm_runtime_mark_last_busy(yas5xx->dev); pm_runtime_put_autosuspend(yas5xx->dev); if (ret) { @@ -916,6 +927,11 @@ static const struct yas5xx_chip_info yas5xx_chip_info_tbl[] = { .scaling_val2 = 100000000, /* picotesla to Gauss */ .t_ref = 182, /* counts */ .min_temp_x10 = -620, /* 1/10:s degrees Celsius */ + .get_measure = yas530_get_measure, + .get_calibration_data = yas530_get_calibration_data, + .dump_calibration = yas530_dump_calibration, + .measure_offsets = yas530_measure_offsets, + .power_on = yas530_power_on, }, [yas532] = { .devid = YAS532_DEVICE_ID, @@ -926,6 +942,11 @@ static const struct yas5xx_chip_info yas5xx_chip_info_tbl[] = { .scaling_val2 = 100000, /* nanotesla to Gauss */ .t_ref = 390, /* counts */ .min_temp_x10 = -500, /* 1/10:s degrees Celsius */ + .get_measure = yas530_get_measure, + .get_calibration_data = yas532_get_calibration_data, + .dump_calibration = yas530_dump_calibration, + .measure_offsets = yas530_measure_offsets, + .power_on = yas530_power_on, }, [yas533] = { .devid = YAS532_DEVICE_ID, @@ -936,6 +957,11 @@ static const struct yas5xx_chip_info yas5xx_chip_info_tbl[] = { .scaling_val2 = 100000, /* nanotesla to Gauss */ .t_ref = 390, /* counts */ .min_temp_x10 = -500, /* 1/10:s degrees Celsius */ + .get_measure = yas530_get_measure, + .get_calibration_data = yas532_get_calibration_data, + .dump_calibration = yas530_dump_calibration, + .measure_offsets = yas530_measure_offsets, + .power_on = yas530_power_on, }, }; @@ -1007,34 +1033,25 @@ static int yas5xx_probe(struct i2c_client *i2c, goto assert_reset; } - switch (ci->devid) { - case YAS530_DEVICE_ID: - ret = yas530_get_calibration_data(yas5xx); - if (ret) - goto assert_reset; - break; - case YAS532_DEVICE_ID: - ret = yas532_get_calibration_data(yas5xx); - if (ret) - goto assert_reset; - break; - default: - ret = -ENODEV; - dev_err(dev, "unhandled device ID %02x\n", ci->devid); + ret = ci->get_calibration_data(yas5xx); + if (ret) goto assert_reset; - } dev_info(dev, "detected %s %s\n", ci->product_name, ci->version_names[yas5xx->version]); - yas530_dump_calibration(yas5xx); - ret = yas530_power_on(yas5xx); - if (ret) - goto assert_reset; - ret = yas530_measure_offsets(yas5xx); + ci->dump_calibration(yas5xx); + + ret = ci->power_on(yas5xx); if (ret) goto assert_reset; + if (ci->measure_offsets) { + ret = ci->measure_offsets(yas5xx); + if (ret) + goto assert_reset; + } + indio_dev->info = &yas5xx_info; indio_dev->available_scan_masks = yas5xx_scan_masks; indio_dev->modes = INDIO_DIRECT_MODE; @@ -1114,6 +1131,7 @@ static int yas5xx_runtime_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct yas5xx *yas5xx = iio_priv(indio_dev); + const struct yas5xx_chip_info *ci = yas5xx->chip_info; int ret; ret = regulator_bulk_enable(ARRAY_SIZE(yas5xx->regs), yas5xx->regs); @@ -1130,7 +1148,7 @@ static int yas5xx_runtime_resume(struct device *dev) usleep_range(31000, 40000); gpiod_set_value_cansleep(yas5xx->reset, 0); - ret = yas530_power_on(yas5xx); + ret = ci->power_on(yas5xx); if (ret) { dev_err(dev, "cannot power on\n"); goto out_reset; -- cgit v1.2.3 From 65f79b501030678393eae0ae03d60a8151fbef55 Mon Sep 17 00:00:00 2001 From: Jakob Hauser Date: Sat, 13 Aug 2022 00:05:03 +0200 Subject: iio: magnetometer: yas530: Add YAS537 variant Add support for the magnetometer Yamaha YAS537. The additions are based on comparison of Yamaha Android kernel drivers for YAS532 [1] and YAS537 [2]. In the Yamaha YAS537 Android driver, there is an overflow/underflow control implemented. For regular usage, this seems not necessary. A similar overflow/ underflow control of Yamaha YAS530/532 Android driver isn't integrated in the mainline driver. It is therefore skipped for YAS537 in the mainline too. Also in the Yamaha YAS537 Android driver, at the end of the reset_yas537() function, a measurement is saved in "last_after_rcoil". Later on, this is compared to current measurements. If the difference gets too big, a new reset is initialized. The difference in measurements needs to be quite big, it's hard to say if this is necessary for regular operation. Therefore this isn't integrated in the mainline driver either. [1] https://github.com/msm8916-mainline/android_kernel_qcom_msm8916/blob/GT-I9195I/drivers/iio/magnetometer/yas_mag_drv-yas532.c [2] https://github.com/msm8916-mainline/android_kernel_qcom_msm8916/blob/GT-I9195I/drivers/iio/magnetometer/yas_mag_drv-yas537.c Signed-off-by: Jakob Hauser Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/264c6488733a5c32089c9ab406a5bcb808c48fef.1660337264.git.jahau@rocketmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/Kconfig | 4 +- drivers/iio/magnetometer/yamaha-yas530.c | 422 ++++++++++++++++++++++++++++++- 2 files changed, 423 insertions(+), 3 deletions(-) diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index 07eb619bcfe8..b91fc5e6a26e 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -216,8 +216,8 @@ config YAMAHA_YAS530 select IIO_TRIGGERED_BUFFER help Say Y here to add support for the Yamaha YAS530 series of - 3-Axis Magnetometers. Right now YAS530, YAS532 and YAS533 are - fully supported. + 3-Axis Magnetometers. YAS530, YAS532, YAS533 and YAS537 are + supported. This driver can also be compiled as a module. To compile this driver as a module, choose M here: the module diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index 18933d8937ae..65bb34c24810 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -17,6 +17,9 @@ * named "inv_compass" in the Tegra Android kernel tree. * Copyright (C) 2012 InvenSense Corporation * + * Code functions for YAS537 based on Yamaha Android kernel driver. + * Copyright (c) 2014 Yamaha Corporation + * * Author: Linus Walleij */ #include @@ -32,6 +35,7 @@ #include #include #include +#include #include #include @@ -56,6 +60,23 @@ #define YAS530_TEST2 0x89 #define YAS530_CAL 0x90 +/* Registers used by YAS537 */ +#define YAS537_MEASURE 0x81 /* Originally YAS537_REG_CMDR */ +#define YAS537_CONFIG 0x82 /* Originally YAS537_REG_CONFR */ +#define YAS537_MEASURE_INTERVAL 0x83 /* Originally YAS537_REG_INTRVLR */ +#define YAS537_OFFSET_X 0x84 /* Originally YAS537_REG_OXR */ +#define YAS537_OFFSET_Y1 0x85 /* Originally YAS537_REG_OY1R */ +#define YAS537_OFFSET_Y2 0x86 /* Originally YAS537_REG_OY2R */ +#define YAS537_AVR 0x87 +#define YAS537_HCK 0x88 +#define YAS537_LCK 0x89 +#define YAS537_SRST 0x90 +#define YAS537_ADCCAL 0x91 +#define YAS537_MTC 0x93 +#define YAS537_OC 0x9E +#define YAS537_TRM 0x9F +#define YAS537_CAL 0xC0 + /* Bits in the YAS5xx config register */ #define YAS5XX_CONFIG_INTON BIT(0) /* Interrupt on? */ #define YAS5XX_CONFIG_INTHACT BIT(1) /* Interrupt active high? */ @@ -67,6 +88,7 @@ #define YAS5XX_MEASURE_LDTC BIT(1) #define YAS5XX_MEASURE_FORS BIT(2) #define YAS5XX_MEASURE_DLYMES BIT(4) +#define YAS5XX_MEASURE_CONT BIT(5) /* Bits in the measure data register */ #define YAS5XX_MEASURE_DATA_BUSY BIT(7) @@ -91,6 +113,22 @@ #define YAS532_DATA_CENTER BIT(YAS532_DATA_BITS - 1) #define YAS532_DATA_OVERFLOW (BIT(YAS532_DATA_BITS) - 1) +#define YAS537_DEVICE_ID 0x07 /* YAS537 (MS-3T) */ +#define YAS537_VERSION_0 0 /* Version naming unknown */ +#define YAS537_VERSION_1 1 /* Version naming unknown */ +#define YAS537_MAG_AVERAGE_32_MASK GENMASK(6, 4) +#define YAS537_MEASURE_TIME_WORST_US 1500 +#define YAS537_DEFAULT_SENSOR_DELAY_MS 50 +#define YAS537_MAG_RCOIL_TIME_US 65 +#define YAS537_MTC3_MASK_PREP GENMASK(7, 0) +#define YAS537_MTC3_MASK_GET GENMASK(7, 5) +#define YAS537_MTC3_ADD_BIT BIT(4) +#define YAS537_HCK_MASK_PREP GENMASK(4, 0) +#define YAS537_HCK_MASK_GET GENMASK(7, 4) +#define YAS537_LCK_MASK_PREP GENMASK(4, 0) +#define YAS537_LCK_MASK_GET GENMASK(3, 0) +#define YAS537_OC_MASK_GET GENMASK(5, 0) + /* Turn off device regulators etc after 5 seconds of inactivity */ #define YAS5XX_AUTOSUSPEND_DELAY_MS 5000 @@ -98,6 +136,7 @@ enum chip_ids { yas530, yas532, yas533, + yas537, }; static const int yas530_volatile_reg[] = { @@ -105,6 +144,10 @@ static const int yas530_volatile_reg[] = { YAS530_MEASURE, }; +static const int yas537_volatile_reg[] = { + YAS537_MEASURE, +}; + struct yas5xx_calibration { /* Linearization calibration x, y1, y2 */ s32 r[3]; @@ -138,7 +181,7 @@ struct yas5xx; * @power_on: function pointer to power-on procedure * * The "t_ref" value for YAS532/533 is known from the Android driver. - * For YAS530 it was approximately measured. + * For YAS530 and YAS537 it was approximately measured. * * The temperatures "min_temp_x10" are derived from the temperature resolutions * given in the data sheets. @@ -312,6 +355,77 @@ out_unlock: return ret; } +/** + * yas537_measure() - Make a measure from the hardware + * @yas5xx: The device state + * @t: the raw temperature measurement + * @x: the raw x axis measurement + * @y1: the y1 axis measurement + * @y2: the y2 axis measurement + * @return: 0 on success or error code + */ +static int yas537_measure(struct yas5xx *yas5xx, u16 *t, u16 *x, u16 *y1, u16 *y2) +{ + struct yas5xx_calibration *c = &yas5xx->calibration; + unsigned int busy; + u8 data[8]; + u16 xy1y2[3]; + s32 h[3], s[3]; + int i, ret; + + mutex_lock(&yas5xx->lock); + + /* Contrary to YAS530/532, also a "cont" bit is set, meaning unknown */ + ret = regmap_write(yas5xx->map, YAS537_MEASURE, YAS5XX_MEASURE_START | + YAS5XX_MEASURE_CONT); + if (ret < 0) + goto out_unlock; + + /* Use same timeout like YAS530/532 but the bit is in data row 2 */ + ret = regmap_read_poll_timeout(yas5xx->map, YAS5XX_MEASURE_DATA + 2, busy, + !(busy & YAS5XX_MEASURE_DATA_BUSY), + 500, 20000); + if (ret) { + dev_err(yas5xx->dev, "timeout waiting for measurement\n"); + goto out_unlock; + } + + ret = regmap_bulk_read(yas5xx->map, YAS5XX_MEASURE_DATA, + data, sizeof(data)); + if (ret) + goto out_unlock; + + mutex_unlock(&yas5xx->lock); + + *t = get_unaligned_be16(&data[0]); + xy1y2[0] = FIELD_GET(GENMASK(13, 0), get_unaligned_be16(&data[2])); + xy1y2[1] = get_unaligned_be16(&data[4]); + xy1y2[2] = get_unaligned_be16(&data[6]); + + /* The second version of YAS537 needs to include calibration coefficients */ + if (yas5xx->version == YAS537_VERSION_1) { + for (i = 0; i < 3; i++) + s[i] = xy1y2[i] - BIT(13); + h[0] = (c->k * (128 * s[0] + c->a2 * s[1] + c->a3 * s[2])) / BIT(13); + h[1] = (c->k * (c->a4 * s[0] + c->a5 * s[1] + c->a6 * s[2])) / BIT(13); + h[2] = (c->k * (c->a7 * s[0] + c->a8 * s[1] + c->a9 * s[2])) / BIT(13); + for (i = 0; i < 3; i++) { + clamp_val(h[i], -BIT(13), BIT(13) - 1); + xy1y2[i] = h[i] + BIT(13); + } + } + + *x = xy1y2[0]; + *y1 = xy1y2[1]; + *y2 = xy1y2[2]; + + return 0; + +out_unlock: + mutex_unlock(&yas5xx->lock); + return ret; +} + /* Used by YAS530, YAS532 and YAS533 */ static s32 yas530_linearize(struct yas5xx *yas5xx, u16 val, int axis) { @@ -457,6 +571,41 @@ static int yas530_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, return 0; } +/** + * yas537_get_measure() - Measure a sample of all axis and process + * @yas5xx: The device state + * @to: Temperature out + * @xo: X axis out + * @yo: Y axis out + * @zo: Z axis out + * @return: 0 on success or error code + */ +static int yas537_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, s32 *zo) +{ + u16 t, x, y1, y2; + int ret; + + /* We first get raw data that needs to be translated to [x,y,z] */ + ret = yas537_measure(yas5xx, &t, &x, &y1, &y2); + if (ret) + return ret; + + /* Calculate temperature readout */ + *to = yas5xx_calc_temperature(yas5xx, t); + + /* + * Unfortunately, no linearization or temperature compensation formulas + * are known for YAS537. + */ + + /* Calculate x, y, z from x, y1, y2 */ + *xo = (x - BIT(13)) * 300; + *yo = (y1 - y2) * 1732 / 10; + *zo = (-y1 - y2 + BIT(14)) * 300; + + return 0; +} + static int yas5xx_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, @@ -772,6 +921,202 @@ static int yas532_get_calibration_data(struct yas5xx *yas5xx) return 0; } +static int yas537_get_calibration_data(struct yas5xx *yas5xx) +{ + struct yas5xx_calibration *c = &yas5xx->calibration; + u8 data[17]; + u32 val1, val2, val3, val4; + int i, ret; + + /* Writing SRST register */ + ret = regmap_write(yas5xx->map, YAS537_SRST, BIT(1)); + if (ret) + return ret; + + /* Calibration readout, YAS537 needs one readout only */ + ret = regmap_bulk_read(yas5xx->map, YAS537_CAL, data, sizeof(data)); + if (ret) + return ret; + dev_dbg(yas5xx->dev, "calibration data: %17ph\n", data); + + /* Sanity check, is this all zeroes? */ + if (!memchr_inv(data, 0x00, 16) && !FIELD_GET(GENMASK(5, 0), data[16])) + dev_warn(yas5xx->dev, "calibration is blank!\n"); + + /* Contribute calibration data to the input pool for kernel entropy */ + add_device_randomness(data, sizeof(data)); + + /* Extract version information */ + yas5xx->version = FIELD_GET(GENMASK(7, 6), data[16]); + + /* There are two versions of YAS537 behaving differently */ + switch (yas5xx->version) { + case YAS537_VERSION_0: + /* + * The first version simply writes data back into registers: + * + * data[0] YAS537_MTC 0x93 + * data[1] 0x94 + * data[2] 0x95 + * data[3] 0x96 + * data[4] 0x97 + * data[5] 0x98 + * data[6] 0x99 + * data[7] 0x9a + * data[8] 0x9b + * data[9] 0x9c + * data[10] 0x9d + * data[11] YAS537_OC 0x9e + * + * data[12] YAS537_OFFSET_X 0x84 + * data[13] YAS537_OFFSET_Y1 0x85 + * data[14] YAS537_OFFSET_Y2 0x86 + * + * data[15] YAS537_HCK 0x88 + * data[16] YAS537_LCK 0x89 + */ + for (i = 0; i < 12; i++) { + ret = regmap_write(yas5xx->map, YAS537_MTC + i, + data[i]); + if (ret) + return ret; + } + for (i = 0; i < 3; i++) { + ret = regmap_write(yas5xx->map, YAS537_OFFSET_X + i, + data[i + 12]); + if (ret) + return ret; + yas5xx->hard_offsets[i] = data[i + 12]; + } + for (i = 0; i < 2; i++) { + ret = regmap_write(yas5xx->map, YAS537_HCK + i, + data[i + 15]); + if (ret) + return ret; + } + break; + case YAS537_VERSION_1: + /* + * The second version writes some data into registers but also + * extracts calibration coefficients. + * + * Registers being written: + * + * data[0] YAS537_MTC 0x93 + * data[1] YAS537_MTC+1 0x94 + * data[2] YAS537_MTC+2 0x95 + * data[3] YAS537_MTC+3 (partially) 0x96 + * + * data[12] YAS537_OFFSET_X 0x84 + * data[13] YAS537_OFFSET_Y1 0x85 + * data[14] YAS537_OFFSET_Y2 0x86 + * + * data[15] YAS537_HCK (partially) 0x88 + * YAS537_LCK (partially) 0x89 + * data[16] YAS537_OC (partially) 0x9e + */ + for (i = 0; i < 3; i++) { + ret = regmap_write(yas5xx->map, YAS537_MTC + i, + data[i]); + if (ret) + return ret; + } + for (i = 0; i < 3; i++) { + ret = regmap_write(yas5xx->map, YAS537_OFFSET_X + i, + data[i + 12]); + if (ret) + return ret; + yas5xx->hard_offsets[i] = data[i + 12]; + } + /* + * Visualization of partially taken data: + * + * data[3] n 7 6 5 4 3 2 1 0 + * YAS537_MTC+3 x x x 1 0 0 0 0 + * + * data[15] n 7 6 5 4 3 2 1 0 + * YAS537_HCK x x x x 0 + * + * data[15] n 7 6 5 4 3 2 1 0 + * YAS537_LCK x x x x 0 + * + * data[16] n 7 6 5 4 3 2 1 0 + * YAS537_OC x x x x x x + */ + ret = regmap_write(yas5xx->map, YAS537_MTC + 3, + FIELD_PREP(YAS537_MTC3_MASK_PREP, + FIELD_GET(YAS537_MTC3_MASK_GET, data[3])) | + YAS537_MTC3_ADD_BIT); + if (ret) + return ret; + ret = regmap_write(yas5xx->map, YAS537_HCK, + FIELD_PREP(YAS537_HCK_MASK_PREP, + FIELD_GET(YAS537_HCK_MASK_GET, data[15]))); + if (ret) + return ret; + ret = regmap_write(yas5xx->map, YAS537_LCK, + FIELD_PREP(YAS537_LCK_MASK_PREP, + FIELD_GET(YAS537_LCK_MASK_GET, data[15]))); + if (ret) + return ret; + ret = regmap_write(yas5xx->map, YAS537_OC, + FIELD_GET(YAS537_OC_MASK_GET, data[16])); + if (ret) + return ret; + /* + * For data extraction, build some blocks. Four 32-bit blocks + * look appropriate. + * + * n 7 6 5 4 3 2 1 0 + * data[0] 0 [ Cx Cx Cx Cx Cx Cx Cx Cx ] bits 31 .. 24 + * data[1] 1 [ Cx C1 C1 C1 C1 C1 C1 C1 ] bits 23 .. 16 + * data[2] 2 [ C1 C1 C2 C2 C2 C2 C2 C2 ] bits 15 .. 8 + * data[3] 3 [ C2 C2 C2 ] bits 7 .. 0 + * + * n 7 6 5 4 3 2 1 0 + * data[3] 0 [ a2 a2 a2 a2 a2 ] bits 31 .. 24 + * data[4] 1 [ a2 a2 a3 a3 a3 a3 a3 a3 ] bits 23 .. 16 + * data[5] 2 [ a3 a4 a4 a4 a4 a4 a4 a4 ] bits 15 .. 8 + * data[6] 3 [ a4 ] bits 7 .. 0 + * + * n 7 6 5 4 3 2 1 0 + * data[6] 0 [ a5 a5 a5 a5 a5 a5 a5 ] bits 31 .. 24 + * data[7] 1 [ a5 a5 a6 a6 a6 a6 a6 a6 ] bits 23 .. 16 + * data[8] 2 [ a6 a7 a7 a7 a7 a7 a7 a7 ] bits 15 .. 8 + * data[9] 3 [ a7 ] bits 7 .. 0 + * + * n 7 6 5 4 3 2 1 0 + * data[9] 0 [ a8 a8 a8 a8 a8 a8 a8 ] bits 31 .. 24 + * data[10] 1 [ a9 a9 a9 a9 a9 a9 a9 a9 ] bits 23 .. 16 + * data[11] 2 [ a9 k k k k k k k ] bits 15 .. 8 + * data[12] 3 [ ] bits 7 .. 0 + */ + val1 = get_unaligned_be32(&data[0]); + val2 = get_unaligned_be32(&data[3]); + val3 = get_unaligned_be32(&data[6]); + val4 = get_unaligned_be32(&data[9]); + /* Extract calibration coefficients and modify */ + c->Cx = FIELD_GET(GENMASK(31, 23), val1) - 256; + c->Cy1 = FIELD_GET(GENMASK(22, 14), val1) - 256; + c->Cy2 = FIELD_GET(GENMASK(13, 5), val1) - 256; + c->a2 = FIELD_GET(GENMASK(28, 22), val2) - 64; + c->a3 = FIELD_GET(GENMASK(21, 15), val2) - 64; + c->a4 = FIELD_GET(GENMASK(14, 7), val2) - 128; + c->a5 = FIELD_GET(GENMASK(30, 22), val3) - 112; + c->a6 = FIELD_GET(GENMASK(21, 15), val3) - 64; + c->a7 = FIELD_GET(GENMASK(14, 7), val3) - 128; + c->a8 = FIELD_GET(GENMASK(30, 24), val4) - 64; + c->a9 = FIELD_GET(GENMASK(23, 15), val4) - 112; + c->k = FIELD_GET(GENMASK(14, 8), val4); + break; + default: + dev_err(yas5xx->dev, "unknown version of YAS537\n"); + return -EINVAL; + } + + return 0; +} + /* Used by YAS530, YAS532 and YAS533 */ static void yas530_dump_calibration(struct yas5xx *yas5xx) { @@ -796,6 +1141,26 @@ static void yas530_dump_calibration(struct yas5xx *yas5xx) dev_dbg(yas5xx->dev, "dck = %d\n", c->dck); } +static void yas537_dump_calibration(struct yas5xx *yas5xx) +{ + struct yas5xx_calibration *c = &yas5xx->calibration; + + if (yas5xx->version == YAS537_VERSION_1) { + dev_dbg(yas5xx->dev, "Cx = %d\n", c->Cx); + dev_dbg(yas5xx->dev, "Cy1 = %d\n", c->Cy1); + dev_dbg(yas5xx->dev, "Cy2 = %d\n", c->Cy2); + dev_dbg(yas5xx->dev, "a2 = %d\n", c->a2); + dev_dbg(yas5xx->dev, "a3 = %d\n", c->a3); + dev_dbg(yas5xx->dev, "a4 = %d\n", c->a4); + dev_dbg(yas5xx->dev, "a5 = %d\n", c->a5); + dev_dbg(yas5xx->dev, "a6 = %d\n", c->a6); + dev_dbg(yas5xx->dev, "a7 = %d\n", c->a7); + dev_dbg(yas5xx->dev, "a8 = %d\n", c->a8); + dev_dbg(yas5xx->dev, "a9 = %d\n", c->a9); + dev_dbg(yas5xx->dev, "k = %d\n", c->k); + } +} + /* Used by YAS530, YAS532 and YAS533 */ static int yas530_set_offsets(struct yas5xx *yas5xx, s8 ox, s8 oy1, s8 oy2) { @@ -917,6 +1282,44 @@ static int yas530_power_on(struct yas5xx *yas5xx) return regmap_write(yas5xx->map, YAS530_MEASURE_INTERVAL, 0); } +static int yas537_power_on(struct yas5xx *yas5xx) +{ + __be16 buf; + int ret; + u8 intrvl; + + /* Writing ADCCAL and TRM registers */ + buf = cpu_to_be16(GENMASK(9, 3)); + ret = regmap_bulk_write(yas5xx->map, YAS537_ADCCAL, &buf, sizeof(buf)); + if (ret) + return ret; + ret = regmap_write(yas5xx->map, YAS537_TRM, GENMASK(7, 0)); + if (ret) + return ret; + + /* The interval value is static in regular operation */ + intrvl = (YAS537_DEFAULT_SENSOR_DELAY_MS * MILLI + - YAS537_MEASURE_TIME_WORST_US) / 4100; + ret = regmap_write(yas5xx->map, YAS537_MEASURE_INTERVAL, intrvl); + if (ret) + return ret; + + /* The average value is also static in regular operation */ + ret = regmap_write(yas5xx->map, YAS537_AVR, YAS537_MAG_AVERAGE_32_MASK); + if (ret) + return ret; + + /* Perform the "rcoil" part but skip the "last_after_rcoil" read */ + ret = regmap_write(yas5xx->map, YAS537_CONFIG, BIT(3)); + if (ret) + return ret; + + /* Wait until the coil has ramped up */ + usleep_range(YAS537_MAG_RCOIL_TIME_US, YAS537_MAG_RCOIL_TIME_US + 100); + + return 0; +} + static const struct yas5xx_chip_info yas5xx_chip_info_tbl[] = { [yas530] = { .devid = YAS530_DEVICE_ID, @@ -963,6 +1366,21 @@ static const struct yas5xx_chip_info yas5xx_chip_info_tbl[] = { .measure_offsets = yas530_measure_offsets, .power_on = yas530_power_on, }, + [yas537] = { + .devid = YAS537_DEVICE_ID, + .product_name = "YAS537 MS-3T", + .version_names = { "v0", "v1" }, /* version naming unknown */ + .volatile_reg = yas537_volatile_reg, + .volatile_reg_qty = ARRAY_SIZE(yas537_volatile_reg), + .scaling_val2 = 100000, /* nanotesla to Gauss */ + .t_ref = 8120, /* counts */ + .min_temp_x10 = -3860, /* 1/10:s degrees Celsius */ + .get_measure = yas537_get_measure, + .get_calibration_data = yas537_get_calibration_data, + .dump_calibration = yas537_dump_calibration, + /* .measure_offets is not needed for yas537 */ + .power_on = yas537_power_on, + }, }; static int yas5xx_probe(struct i2c_client *i2c, @@ -1170,6 +1588,7 @@ static const struct i2c_device_id yas5xx_id[] = { {"yas530", yas530 }, {"yas532", yas532 }, {"yas533", yas533 }, + {"yas537", yas537 }, {} }; MODULE_DEVICE_TABLE(i2c, yas5xx_id); @@ -1178,6 +1597,7 @@ static const struct of_device_id yas5xx_of_match[] = { { .compatible = "yamaha,yas530", }, { .compatible = "yamaha,yas532", }, { .compatible = "yamaha,yas533", }, + { .compatible = "yamaha,yas537", }, {} }; MODULE_DEVICE_TABLE(of, yas5xx_of_match); -- cgit v1.2.3 From b82217e73b5aa6db8453ad91b929ca2366e47184 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 26 Jul 2022 17:20:48 +0300 Subject: iio: pressure: dlhl60d: Don't take garbage into consideration when reading data Both pressure and temperature are 24-bit long. Use proper accessors instead of overlapping readings. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220726142048.4494-1-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/dlhl60d.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/iio/pressure/dlhl60d.c b/drivers/iio/pressure/dlhl60d.c index 5f6bb3603a8b..f0b0d198c6d4 100644 --- a/drivers/iio/pressure/dlhl60d.c +++ b/drivers/iio/pressure/dlhl60d.c @@ -129,9 +129,8 @@ static int dlh_read_direct(struct dlh_state *st, if (ret) return ret; - *pressure = get_unaligned_be32(&st->rx_buf[1]) >> 8; - *temperature = get_unaligned_be32(&st->rx_buf[3]) & - GENMASK(DLH_NUM_TEMP_BITS - 1, 0); + *pressure = get_unaligned_be24(&st->rx_buf[1]); + *temperature = get_unaligned_be24(&st->rx_buf[4]); return 0; } -- cgit v1.2.3 From 7ec26b8dcc5c770a06a7e7ca2e1c888e2115bf0b Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 18 Jul 2022 14:14:02 +0200 Subject: interconnect: imx: Ignore return value of icc_provider_del() in .remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit icc_provider_del() already emits an error message on failure. In this case letting .remove() return the corresponding error code results in another error message and the device is removed anyhow. (See platform_remove().) So ignore the return value of icc_provider_del() and return 0 unconditionally. This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220718121409.171773-2-u.kleine-koenig@pengutronix.de Signed-off-by: Georgi Djakov --- drivers/interconnect/imx/imx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/interconnect/imx/imx.c b/drivers/interconnect/imx/imx.c index 48ffd59953bf..6d9f107ff227 100644 --- a/drivers/interconnect/imx/imx.c +++ b/drivers/interconnect/imx/imx.c @@ -330,7 +330,9 @@ int imx_icc_unregister(struct platform_device *pdev) imx_icc_unregister_nodes(&imx_provider->provider); - return icc_provider_del(&imx_provider->provider); + icc_provider_del(&imx_provider->provider); + + return 0; } EXPORT_SYMBOL_GPL(imx_icc_unregister); -- cgit v1.2.3 From 8ef2ca20754d84369971aac261ad7f99801adf87 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 18 Jul 2022 14:14:03 +0200 Subject: interconnect: icc-rpm: Ignore return value of icc_provider_del() in .remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit icc_provider_del() already emits an error message on failure. In this case letting .remove() return the corresponding error code results in another error message and the device is removed anyhow. (See platform_remove().) So ignore the return value of icc_provider_del() and return 0 unconditionally. This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220718121409.171773-3-u.kleine-koenig@pengutronix.de Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index 7f6a70e0256a..39e43b957599 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -563,6 +563,8 @@ int qnoc_remove(struct platform_device *pdev) icc_nodes_remove(&qp->provider); clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); - return icc_provider_del(&qp->provider); + icc_provider_del(&qp->provider); + + return 0; } EXPORT_SYMBOL(qnoc_remove); -- cgit v1.2.3 From 4681086c9becc283ba4f8ed6be315918e1e0b917 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 18 Jul 2022 14:14:04 +0200 Subject: interconnect: icc-rpmh: Ignore return value of icc_provider_del() in .remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit icc_provider_del() already emits an error message on failure. In this case letting .remove() return the corresponding error code results in another error message and the device is removed anyhow. (See platform_remove().) So ignore the return value of icc_provider_del() and return 0 unconditionally. This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220718121409.171773-4-u.kleine-koenig@pengutronix.de Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpmh.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/interconnect/qcom/icc-rpmh.c b/drivers/interconnect/qcom/icc-rpmh.c index 114bb8f64573..fd17291c61eb 100644 --- a/drivers/interconnect/qcom/icc-rpmh.c +++ b/drivers/interconnect/qcom/icc-rpmh.c @@ -251,7 +251,9 @@ int qcom_icc_rpmh_remove(struct platform_device *pdev) struct qcom_icc_provider *qp = platform_get_drvdata(pdev); icc_nodes_remove(&qp->provider); - return icc_provider_del(&qp->provider); + icc_provider_del(&qp->provider); + + return 0; } EXPORT_SYMBOL_GPL(qcom_icc_rpmh_remove); -- cgit v1.2.3 From 919d4e1a207e9e837404c49e1386f210ac305f67 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 18 Jul 2022 14:14:05 +0200 Subject: interconnect: msm8974: Ignore return value of icc_provider_del() in .remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit icc_provider_del() already emits an error message on failure. In this case letting .remove() return the corresponding error code results in another error message and the device is removed anyhow. (See platform_remove().) So ignore the return value of icc_provider_del() and return 0 unconditionally. This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220718121409.171773-5-u.kleine-koenig@pengutronix.de Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/msm8974.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/interconnect/qcom/msm8974.c b/drivers/interconnect/qcom/msm8974.c index 6fa0ad90fc3d..5ea192f1141d 100644 --- a/drivers/interconnect/qcom/msm8974.c +++ b/drivers/interconnect/qcom/msm8974.c @@ -749,7 +749,9 @@ static int msm8974_icc_remove(struct platform_device *pdev) icc_nodes_remove(&qp->provider); clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); - return icc_provider_del(&qp->provider); + icc_provider_del(&qp->provider); + + return 0; } static const struct of_device_id msm8974_noc_of_match[] = { -- cgit v1.2.3 From f221bd781f25fa0ebb4c7e9553a9154a9722fd39 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 18 Jul 2022 14:14:06 +0200 Subject: interconnect: osm-l3: Ignore return value of icc_provider_del() in .remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit icc_provider_del() already emits an error message on failure. In this case letting .remove() return the corresponding error code results in another error message and the device is removed anyhow. (See platform_remove().) So ignore the return value of icc_provider_del() and return 0 unconditionally. This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220718121409.171773-6-u.kleine-koenig@pengutronix.de Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/osm-l3.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/interconnect/qcom/osm-l3.c b/drivers/interconnect/qcom/osm-l3.c index 4198656f4e59..ddbdf0943f94 100644 --- a/drivers/interconnect/qcom/osm-l3.c +++ b/drivers/interconnect/qcom/osm-l3.c @@ -217,7 +217,9 @@ static int qcom_osm_l3_remove(struct platform_device *pdev) struct qcom_osm_l3_icc_provider *qp = platform_get_drvdata(pdev); icc_nodes_remove(&qp->provider); - return icc_provider_del(&qp->provider); + icc_provider_del(&qp->provider); + + return 0; } static int qcom_osm_l3_probe(struct platform_device *pdev) -- cgit v1.2.3 From fa80a2994d35af064b194fe9bb587ae8ea05d379 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 18 Jul 2022 14:14:07 +0200 Subject: interconnect: sm8450: Ignore return value of icc_provider_del() in .remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit icc_provider_del() already emits an error message on failure. In this case letting .remove() return the corresponding error code results in another error message and the device is removed anyhow. (See platform_remove().) So ignore the return value of icc_provider_del() and return 0 unconditionally. This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220718121409.171773-7-u.kleine-koenig@pengutronix.de Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/sm8450.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/interconnect/qcom/sm8450.c b/drivers/interconnect/qcom/sm8450.c index e821fd0b2f66..e3a12e3d6e06 100644 --- a/drivers/interconnect/qcom/sm8450.c +++ b/drivers/interconnect/qcom/sm8450.c @@ -1933,7 +1933,9 @@ static int qnoc_remove(struct platform_device *pdev) struct qcom_icc_provider *qp = platform_get_drvdata(pdev); icc_nodes_remove(&qp->provider); - return icc_provider_del(&qp->provider); + icc_provider_del(&qp->provider); + + return 0; } static const struct of_device_id qnoc_of_match[] = { -- cgit v1.2.3 From 680f8666baf6b4a6cc368dfff6614010ec23c51d Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 18 Jul 2022 14:14:08 +0200 Subject: interconnect: Make icc_provider_del() return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All users ignore the return value of icc_provider_del(). Consequently make it not return an error code. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220718121409.171773-8-u.kleine-koenig@pengutronix.de Signed-off-by: Georgi Djakov --- drivers/interconnect/core.c | 10 +++------- include/linux/interconnect-provider.h | 5 ++--- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index 808f6e7a8048..25debded65a8 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -1057,29 +1057,25 @@ EXPORT_SYMBOL_GPL(icc_provider_add); /** * icc_provider_del() - delete previously added interconnect provider * @provider: the interconnect provider that will be removed from topology - * - * Return: 0 on success, or an error code otherwise */ -int icc_provider_del(struct icc_provider *provider) +void icc_provider_del(struct icc_provider *provider) { mutex_lock(&icc_lock); if (provider->users) { pr_warn("interconnect provider still has %d users\n", provider->users); mutex_unlock(&icc_lock); - return -EBUSY; + return; } if (!list_empty(&provider->nodes)) { pr_warn("interconnect provider still has nodes\n"); mutex_unlock(&icc_lock); - return -EBUSY; + return; } list_del(&provider->provider_list); mutex_unlock(&icc_lock); - - return 0; } EXPORT_SYMBOL_GPL(icc_provider_del); diff --git a/include/linux/interconnect-provider.h b/include/linux/interconnect-provider.h index 6bd01f7159c6..cd5c5a27557f 100644 --- a/include/linux/interconnect-provider.h +++ b/include/linux/interconnect-provider.h @@ -123,7 +123,7 @@ void icc_node_add(struct icc_node *node, struct icc_provider *provider); void icc_node_del(struct icc_node *node); int icc_nodes_remove(struct icc_provider *provider); int icc_provider_add(struct icc_provider *provider); -int icc_provider_del(struct icc_provider *provider); +void icc_provider_del(struct icc_provider *provider); struct icc_node_data *of_icc_get_from_provider(struct of_phandle_args *spec); void icc_sync_state(struct device *dev); @@ -172,9 +172,8 @@ static inline int icc_provider_add(struct icc_provider *provider) return -ENOTSUPP; } -static inline int icc_provider_del(struct icc_provider *provider) +static inline void icc_provider_del(struct icc_provider *provider) { - return -ENOTSUPP; } static inline struct icc_node_data *of_icc_get_from_provider(struct of_phandle_args *spec) -- cgit v1.2.3 From f62e3f595c5f08e6066d88bc96d50247424291f8 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 18 Jul 2022 14:14:09 +0200 Subject: interconnect: imx: Make imx_icc_unregister() return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function imx_icc_unregister() returns zero unconditionally. Make it return void. This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220718121409.171773-9-u.kleine-koenig@pengutronix.de Signed-off-by: Georgi Djakov --- drivers/interconnect/imx/imx.c | 4 +--- drivers/interconnect/imx/imx.h | 2 +- drivers/interconnect/imx/imx8mm.c | 4 +++- drivers/interconnect/imx/imx8mn.c | 4 +++- drivers/interconnect/imx/imx8mp.c | 4 +++- drivers/interconnect/imx/imx8mq.c | 4 +++- 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/interconnect/imx/imx.c b/drivers/interconnect/imx/imx.c index 6d9f107ff227..823d9be9771a 100644 --- a/drivers/interconnect/imx/imx.c +++ b/drivers/interconnect/imx/imx.c @@ -324,15 +324,13 @@ provider_del: } EXPORT_SYMBOL_GPL(imx_icc_register); -int imx_icc_unregister(struct platform_device *pdev) +void imx_icc_unregister(struct platform_device *pdev) { struct imx_icc_provider *imx_provider = platform_get_drvdata(pdev); imx_icc_unregister_nodes(&imx_provider->provider); icc_provider_del(&imx_provider->provider); - - return 0; } EXPORT_SYMBOL_GPL(imx_icc_unregister); diff --git a/drivers/interconnect/imx/imx.h b/drivers/interconnect/imx/imx.h index e0a2ee173ecd..895907cdcb3b 100644 --- a/drivers/interconnect/imx/imx.h +++ b/drivers/interconnect/imx/imx.h @@ -103,6 +103,6 @@ int imx_icc_register(struct platform_device *pdev, struct imx_icc_node_desc *nodes, int nodes_count, struct imx_icc_noc_setting *noc_settings); -int imx_icc_unregister(struct platform_device *pdev); +void imx_icc_unregister(struct platform_device *pdev); #endif /* __DRIVERS_INTERCONNECT_IMX_H */ diff --git a/drivers/interconnect/imx/imx8mm.c b/drivers/interconnect/imx/imx8mm.c index ae797412db96..b43325364aa3 100644 --- a/drivers/interconnect/imx/imx8mm.c +++ b/drivers/interconnect/imx/imx8mm.c @@ -88,7 +88,9 @@ static int imx8mm_icc_probe(struct platform_device *pdev) static int imx8mm_icc_remove(struct platform_device *pdev) { - return imx_icc_unregister(pdev); + imx_icc_unregister(pdev); + + return 0; } static struct platform_driver imx8mm_icc_driver = { diff --git a/drivers/interconnect/imx/imx8mn.c b/drivers/interconnect/imx/imx8mn.c index 1ce94c5bdd8c..8ce6d8e4bf5e 100644 --- a/drivers/interconnect/imx/imx8mn.c +++ b/drivers/interconnect/imx/imx8mn.c @@ -77,7 +77,9 @@ static int imx8mn_icc_probe(struct platform_device *pdev) static int imx8mn_icc_remove(struct platform_device *pdev) { - return imx_icc_unregister(pdev); + imx_icc_unregister(pdev); + + return 0; } static struct platform_driver imx8mn_icc_driver = { diff --git a/drivers/interconnect/imx/imx8mp.c b/drivers/interconnect/imx/imx8mp.c index 5f1c83ed157b..8bfaf173f1da 100644 --- a/drivers/interconnect/imx/imx8mp.c +++ b/drivers/interconnect/imx/imx8mp.c @@ -242,7 +242,9 @@ static int imx8mp_icc_probe(struct platform_device *pdev) static int imx8mp_icc_remove(struct platform_device *pdev) { - return imx_icc_unregister(pdev); + imx_icc_unregister(pdev); + + return 0; } static struct platform_driver imx8mp_icc_driver = { diff --git a/drivers/interconnect/imx/imx8mq.c b/drivers/interconnect/imx/imx8mq.c index 7f00a0511c6e..b6fb71305c99 100644 --- a/drivers/interconnect/imx/imx8mq.c +++ b/drivers/interconnect/imx/imx8mq.c @@ -87,7 +87,9 @@ static int imx8mq_icc_probe(struct platform_device *pdev) static int imx8mq_icc_remove(struct platform_device *pdev) { - return imx_icc_unregister(pdev); + imx_icc_unregister(pdev); + + return 0; } static struct platform_driver imx8mq_icc_driver = { -- cgit v1.2.3 From 30475ef2836ee47965e424b79e54e35c18ff0aeb Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 19 Aug 2022 13:41:17 +0300 Subject: iio: frequency: admv1014: return -EINVAL directly Remove extra step where the error code is assigned to the `ret` variable. Return instead error code directly. Signed-off-by: Antoniu Miclaus Link: https://lore.kernel.org/r/20220819104117.4600-1-antoniu.miclaus@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/admv1014.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/frequency/admv1014.c b/drivers/iio/frequency/admv1014.c index 865addd10db4..bb5e1feef42b 100644 --- a/drivers/iio/frequency/admv1014.c +++ b/drivers/iio/frequency/admv1014.c @@ -669,8 +669,7 @@ static int admv1014_init(struct admv1014_state *st) chip_id = FIELD_GET(ADMV1014_CHIP_ID_MSK, chip_id); if (chip_id != ADMV1014_CHIP_ID) { dev_err(&spi->dev, "Invalid Chip ID.\n"); - ret = -EINVAL; - return ret; + return -EINVAL; } ret = __admv1014_spi_update_bits(st, ADMV1014_REG_QUAD, -- cgit v1.2.3 From ae2c9cf14c1ee451cd2215584b21619f1568d9e1 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 18 Aug 2022 23:00:16 +0200 Subject: iio: st_sensors: move from strlcpy with unused retval to strscpy Follow the advice of the below link and prefer 'strscpy' in this subsystem. Conversion is 1:1 because the return value is not used. Generated by a coccinelle script. Link: https://lore.kernel.org/r/CAHk-=wgfRnXz0W3D37d01q3JFkr_i_uTL=V6A6G1oUZcprmknw@mail.gmail.com/ Signed-off-by: Wolfram Sang Link: https://lore.kernel.org/r/20220818210017.6817-1-wsa+renesas@sang-engineering.com Signed-off-by: Jonathan Cameron --- drivers/iio/common/st_sensors/st_sensors_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index 9910ba1da085..35720c64fea8 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -354,7 +354,7 @@ void st_sensors_dev_name_probe(struct device *dev, char *name, int len) return; /* The name from the match takes precedence if present */ - strlcpy(name, match, len); + strscpy(name, match, len); } EXPORT_SYMBOL_NS(st_sensors_dev_name_probe, IIO_ST_SENSORS); -- cgit v1.2.3 From a723df3d430933a326257b049a993f377df66e1c Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 19 Aug 2022 18:23:48 +0100 Subject: iio: imu: inv_mpu6050: move from strlcpy with unused retval to strscpy Follow the advice of the below link and prefer 'strscpy' in this subsystem. Conversion is 1:1 because the return value is not used. Generated by a coccinelle script. Link: https://lore.kernel.org/r/CAHk-=wgfRnXz0W3D37d01q3JFkr_i_uTL=V6A6G1oUZcprmknw@mail.gmail.com/ Signed-off-by: Wolfram Sang Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c index 9b4298095d3f..f7bce428d9eb 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c @@ -65,7 +65,7 @@ static int asus_acpi_get_sensor_info(struct acpi_device *adev, sub_elem = &elem->package.elements[j]; if (sub_elem->type == ACPI_TYPE_STRING) - strlcpy(info->type, sub_elem->string.pointer, + strscpy(info->type, sub_elem->string.pointer, sizeof(info->type)); else if (sub_elem->type == ACPI_TYPE_INTEGER) { if (sub_elem->integer.value != client->addr) { @@ -158,7 +158,7 @@ int inv_mpu_acpi_create_mux_client(struct i2c_client *client) char *name; info.addr = secondary; - strlcpy(info.type, dev_name(&adev->dev), + strscpy(info.type, dev_name(&adev->dev), sizeof(info.type)); name = strchr(info.type, ':'); if (name) -- cgit v1.2.3 From 83de806074980ea94930ca4ff6754fecf9ad8290 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Fri, 19 Aug 2022 00:18:13 +0200 Subject: iio: adc: qcom-spmi-adc5: add ADC5_VREF_VADC to rev2 ADC5 Add support for ADC5_VREF_VADC channel to rev2 ADC5 channel list. This channel measures the VADC reference LDO output. Signed-off-by: Robert Marko Link: https://lore.kernel.org/r/20220818221815.346233-3-robimarko@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/qcom-spmi-adc5.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c index e96da2ef1964..821fee60a765 100644 --- a/drivers/iio/adc/qcom-spmi-adc5.c +++ b/drivers/iio/adc/qcom-spmi-adc5.c @@ -597,6 +597,8 @@ static const struct adc5_channels adc5_chans_rev2[ADC5_MAX_CHANNEL] = { SCALE_HW_CALIB_DEFAULT) [ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 0, SCALE_HW_CALIB_DEFAULT) + [ADC5_VREF_VADC] = ADC5_CHAN_VOLT("vref_vadc", 0, + SCALE_HW_CALIB_DEFAULT) [ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 1, SCALE_HW_CALIB_DEFAULT) [ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 1, -- cgit v1.2.3 From f33abd2d57f9df81de8d4c1fee5c68849a0f8690 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 16 Aug 2022 15:53:59 +0300 Subject: dt-bindings: iio: Drop Tomislav Denis Emails to Tomislav Denis bounce ("550 5.1.1 User Unknown"). Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring Link: https://lore.kernel.org/r/20220816125401.70317-1-krzysztof.kozlowski@linaro.org Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/ti,ads131e08.yaml | 2 +- Documentation/devicetree/bindings/iio/pressure/asc,dlhl60d.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads131e08.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads131e08.yaml index e0670e3fbb72..2876397fc668 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,ads131e08.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,ads131e08.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Texas Instruments ADS131E0x 4-, 6- and 8-Channel ADCs maintainers: - - Tomislav Denis + - Jonathan Cameron description: | The ADS131E0x are a family of multichannel, simultaneous sampling, diff --git a/Documentation/devicetree/bindings/iio/pressure/asc,dlhl60d.yaml b/Documentation/devicetree/bindings/iio/pressure/asc,dlhl60d.yaml index be2be4b556db..1f9fe15b4b3c 100644 --- a/Documentation/devicetree/bindings/iio/pressure/asc,dlhl60d.yaml +++ b/Documentation/devicetree/bindings/iio/pressure/asc,dlhl60d.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: All Sensors DLH series low voltage digital pressure sensors maintainers: - - Tomislav Denis + - Jonathan Cameron description: | Bindings for the All Sensors DLH series pressure sensors. -- cgit v1.2.3 From 6683fdf4202c5d7cb2fa47a13adaf275ca3ac1a2 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 16 Aug 2022 15:54:00 +0300 Subject: iio: MAINTAINERS: Drop Tomislav Denis Emails to Tomislav Denis bounce ("550 5.1.1 User Unknown"). Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220816125401.70317-2-krzysztof.kozlowski@linaro.org Signed-off-by: Jonathan Cameron --- MAINTAINERS | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index b8b6544ba27c..a88edf508003 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -754,14 +754,6 @@ L: Dell.Client.Kernel@dell.com S: Maintained F: drivers/platform/x86/dell/alienware-wmi.c -ALL SENSORS DLH SERIES PRESSURE SENSORS DRIVER -M: Tomislav Denis -L: linux-iio@vger.kernel.org -S: Maintained -W: http://www.allsensors.com/ -F: Documentation/devicetree/bindings/iio/pressure/asc,dlhl60d.yaml -F: drivers/iio/pressure/dlhl60d.c - ALLEGRO DVT VIDEO IP CORE DRIVER M: Michael Tretter R: Pengutronix Kernel Team @@ -20246,13 +20238,6 @@ M: Robert Richter S: Odd Fixes F: drivers/gpio/gpio-thunderx.c -TI ADS131E0X ADC SERIES DRIVER -M: Tomislav Denis -L: linux-iio@vger.kernel.org -S: Maintained -F: Documentation/devicetree/bindings/iio/adc/ti,ads131e08.yaml -F: drivers/iio/adc/ti-ads131e08.c - TI AM437X VPFE DRIVER M: "Lad, Prabhakar" L: linux-media@vger.kernel.org -- cgit v1.2.3 From 59d1c811c1dd9ab3bd2d216d1453eb0e4cacd52c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 16 Aug 2022 15:54:01 +0300 Subject: dt-bindings: iio: adc: Drop Patrick Vasseur Emails to Patrick Vasseur bounce ("Unknown To address"). Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring Link: https://lore.kernel.org/r/20220816125401.70317-3-krzysztof.kozlowski@linaro.org Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml index e82194974eea..82168b1495eb 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml @@ -8,7 +8,6 @@ title: Analog Devices AD7923 and similars with 4 and 8 Channel ADCs. maintainers: - Michael Hennerich - - Patrick Vasseur description: | Analog Devices AD7904, AD7914, AD7923, AD7924 4 Channel ADCs, and AD7908, -- cgit v1.2.3 From 801373884560e707883adb2aa7ef9bf2293bf88a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 16 Aug 2022 15:43:12 +0300 Subject: dt-bindings: iio: adc: use spi-peripheral-props.yaml Instead of listing directly properties typical for SPI peripherals, reference the spi-peripheral-props.yaml schema. This allows using all properties typical for SPI-connected devices, even these which device bindings author did not tried yet. Remove the spi-* properties which now come via spi-peripheral-props.yaml schema, except for the cases when device schema adds some constraints like maximum frequency. While changing additionalProperties->unevaluatedProperties, put it in typical place, just before example DTS. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Marcus Folkesson Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220816124321.67817-2-krzysztof.kozlowski@linaro.org Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/adi,ad7124.yaml | 7 +++--- .../devicetree/bindings/iio/adc/adi,ad7192.yaml | 7 +++--- .../devicetree/bindings/iio/adc/adi,ad7280a.yaml | 7 +++--- .../devicetree/bindings/iio/adc/adi,ad7292.yaml | 7 +++--- .../devicetree/bindings/iio/adc/adi,ad7298.yaml | 6 +++-- .../devicetree/bindings/iio/adc/adi,ad7476.yaml | 8 +++---- .../devicetree/bindings/iio/adc/adi,ad7606.yaml | 7 +++--- .../devicetree/bindings/iio/adc/adi,ad7768-1.yaml | 7 +++--- .../devicetree/bindings/iio/adc/adi,ad7923.yaml | 7 +++--- .../devicetree/bindings/iio/adc/adi,ad7949.yaml | 7 +++--- .../devicetree/bindings/iio/adc/holt,hi8435.yaml | 7 +++--- .../devicetree/bindings/iio/adc/lltc,ltc2496.yaml | 8 +++---- .../devicetree/bindings/iio/adc/maxim,max1027.yaml | 5 ++++- .../bindings/iio/adc/maxim,max11100.yaml | 7 ++++-- .../devicetree/bindings/iio/adc/maxim,max1118.yaml | 26 ++++++++++++---------- .../devicetree/bindings/iio/adc/maxim,max1241.yaml | 7 +++--- .../bindings/iio/adc/microchip,mcp3201.yaml | 6 +++-- .../bindings/iio/adc/microchip,mcp3911.yaml | 5 ++++- .../devicetree/bindings/iio/adc/ti,adc0832.yaml | 7 +++--- .../devicetree/bindings/iio/adc/ti,adc084s021.yaml | 7 +++--- .../devicetree/bindings/iio/adc/ti,adc108s102.yaml | 6 +++-- .../devicetree/bindings/iio/adc/ti,adc12138.yaml | 7 +++--- .../devicetree/bindings/iio/adc/ti,adc128s052.yaml | 7 +++--- .../devicetree/bindings/iio/adc/ti,adc161s626.yaml | 7 +++--- .../devicetree/bindings/iio/adc/ti,ads124s08.yaml | 7 +++--- .../devicetree/bindings/iio/adc/ti,ads131e08.yaml | 7 +++--- .../devicetree/bindings/iio/adc/ti,ads8344.yaml | 7 +++--- .../devicetree/bindings/iio/adc/ti,ads8688.yaml | 7 +++--- .../devicetree/bindings/iio/adc/ti,tlc4541.yaml | 7 +++--- .../devicetree/bindings/iio/adc/ti,tsc2046.yaml | 7 +++--- 30 files changed, 131 insertions(+), 93 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7124.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7124.yaml index fb3d0dae9bae..75a7184a4735 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7124.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7124.yaml @@ -52,8 +52,6 @@ properties: avdd-supply: description: avdd supply can be used as reference for conversion. - spi-max-frequency: true - required: - compatible - reg @@ -106,7 +104,10 @@ patternProperties: additionalProperties: false -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7192.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7192.yaml index 22b7ed3723f6..cc347dade4ef 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7192.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7192.yaml @@ -30,8 +30,6 @@ properties: spi-cpha: true - spi-max-frequency: true - clocks: maxItems: 1 description: phandle to the master clock (mclk) @@ -94,7 +92,10 @@ required: - spi-cpol - spi-cpha -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7280a.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7280a.yaml index a694d5794d4a..dfb8f305e2f0 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7280a.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7280a.yaml @@ -28,8 +28,6 @@ properties: description: IRQ line for the ADC maxItems: 1 - spi-max-frequency: true - adi,voltage-alert-last-chan: $ref: /schemas/types.yaml#/definitions/uint32 description: @@ -55,7 +53,10 @@ required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7292.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7292.yaml index a3e39a40c9b3..1bfbeed6f299 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7292.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7292.yaml @@ -30,8 +30,6 @@ properties: spi-cpha: true - spi-max-frequency: true - '#address-cells': const: 1 @@ -65,7 +63,10 @@ patternProperties: additionalProperties: true -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7298.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7298.yaml index ca414bb396c5..cd8ac5162d27 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7298.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7298.yaml @@ -24,13 +24,15 @@ properties: vref-supply: true vdd-supply: true - spi-max-frequency: true required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7476.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7476.yaml index 666414a9c0de..44c671eeda73 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7476.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7476.yaml @@ -66,8 +66,6 @@ properties: to the other supplies. Needed to be able to establish channel scaling unless there is also an internal reference available (e.g. ad7091r) - spi-max-frequency: true - adi,conversion-start-gpios: description: A GPIO used to trigger the start of a conversion maxItems: 1 @@ -76,9 +74,9 @@ required: - compatible - reg -additionalProperties: false - allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + # Devices where reference is vcc - if: properties: @@ -158,6 +156,8 @@ allOf: properties: adi,conversion-start-gpios: false +unevaluatedProperties: false + examples: - | spi { diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml index 516fc24d3346..ac5a47c8f070 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml @@ -32,8 +32,6 @@ properties: spi-cpol: true - spi-max-frequency: true - avcc-supply: true interrupts: @@ -105,7 +103,10 @@ required: - interrupts - adi,conversion-start-gpios -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml index a85a28145ef6..3ce59d4d065f 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml @@ -50,8 +50,6 @@ properties: reset-gpios: maxItems: 1 - spi-max-frequency: true - spi-cpol: true spi-cpha: true @@ -88,7 +86,10 @@ patternProperties: - reg additionalProperties: false -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml index 82168b1495eb..40b0a887db57 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml @@ -42,13 +42,14 @@ properties: '#size-cells': const: 0 - spi-max-frequency: true - required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7949.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7949.yaml index 0b10ed5f74ae..9ee4d977c5ed 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7949.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7949.yaml @@ -49,8 +49,6 @@ properties: default: 4096000 - spi-max-frequency: true - '#io-channel-cells': const: 1 @@ -64,7 +62,10 @@ required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/holt,hi8435.yaml b/Documentation/devicetree/bindings/iio/adc/holt,hi8435.yaml index 52490cbb0af0..56bcbe5dcd79 100644 --- a/Documentation/devicetree/bindings/iio/adc/holt,hi8435.yaml +++ b/Documentation/devicetree/bindings/iio/adc/holt,hi8435.yaml @@ -24,8 +24,6 @@ properties: GPIO used for controlling the reset pin maxItems: 1 - spi-max-frequency: true - "#io-channel-cells": const: 1 @@ -33,7 +31,10 @@ required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/lltc,ltc2496.yaml b/Documentation/devicetree/bindings/iio/adc/lltc,ltc2496.yaml index 0bd2fc0356c8..5207c919abe0 100644 --- a/Documentation/devicetree/bindings/iio/adc/lltc,ltc2496.yaml +++ b/Documentation/devicetree/bindings/iio/adc/lltc,ltc2496.yaml @@ -22,15 +22,15 @@ properties: reg: maxItems: 1 - spi-max-frequency: - description: maximal spi bus frequency supported - required: - compatible - vref-supply - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/maxim,max1027.yaml b/Documentation/devicetree/bindings/iio/adc/maxim,max1027.yaml index 46b7747076b9..d0a7ed26d9ea 100644 --- a/Documentation/devicetree/bindings/iio/adc/maxim,max1027.yaml +++ b/Documentation/devicetree/bindings/iio/adc/maxim,max1027.yaml @@ -45,7 +45,10 @@ required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/maxim,max11100.yaml b/Documentation/devicetree/bindings/iio/adc/maxim,max11100.yaml index 0cf87556ef82..4f74cb33383a 100644 --- a/Documentation/devicetree/bindings/iio/adc/maxim,max11100.yaml +++ b/Documentation/devicetree/bindings/iio/adc/maxim,max11100.yaml @@ -26,13 +26,16 @@ properties: minimum: 100000 maximum: 4800000 -additionalProperties: false - required: - compatible - reg - vref-supply +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + examples: - | spi { diff --git a/Documentation/devicetree/bindings/iio/adc/maxim,max1118.yaml b/Documentation/devicetree/bindings/iio/adc/maxim,max1118.yaml index e948b3e37b0c..bb336e33ebe2 100644 --- a/Documentation/devicetree/bindings/iio/adc/maxim,max1118.yaml +++ b/Documentation/devicetree/bindings/iio/adc/maxim,max1118.yaml @@ -28,23 +28,25 @@ properties: vref-supply: description: External reference, needed to establish input scaling -if: - properties: - compatible: - contains: - const: maxim,max1118 -then: - required: - - vref-supply -else: - properties: - vref-supply: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + - if: + properties: + compatible: + contains: + const: maxim,max1118 + then: + required: + - vref-supply + else: + properties: + vref-supply: false required: - compatible - reg -additionalProperties: false +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/maxim,max1241.yaml b/Documentation/devicetree/bindings/iio/adc/maxim,max1241.yaml index 4c7e0d94bff1..58b12fe8070c 100644 --- a/Documentation/devicetree/bindings/iio/adc/maxim,max1241.yaml +++ b/Documentation/devicetree/bindings/iio/adc/maxim,max1241.yaml @@ -39,15 +39,16 @@ properties: thus enabling power-down mode. maxItems: 1 - spi-max-frequency: true - required: - compatible - reg - vdd-supply - vref-supply -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/microchip,mcp3201.yaml b/Documentation/devicetree/bindings/iio/adc/microchip,mcp3201.yaml index fcc1ba53b20d..18108f0f3731 100644 --- a/Documentation/devicetree/bindings/iio/adc/microchip,mcp3201.yaml +++ b/Documentation/devicetree/bindings/iio/adc/microchip,mcp3201.yaml @@ -32,7 +32,6 @@ properties: reg: maxItems: 1 - spi-max-frequency: true spi-cpha: true spi-cpol: true @@ -51,7 +50,10 @@ required: - reg - vref-supply -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml b/Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml index 95ab285f4eba..067a7bbadab8 100644 --- a/Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml +++ b/Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml @@ -51,7 +51,10 @@ required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/ti,adc0832.yaml b/Documentation/devicetree/bindings/iio/adc/ti,adc0832.yaml index f5a923cc847f..686721176a58 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,adc0832.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,adc0832.yaml @@ -24,8 +24,6 @@ properties: reg: maxItems: 1 - spi-max-frequency: true - vref-supply: description: External reference, needed to establish input scaling @@ -37,7 +35,10 @@ required: - reg - vref-supply -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/ti,adc084s021.yaml b/Documentation/devicetree/bindings/iio/adc/ti,adc084s021.yaml index 1a113b30a414..726d2cbfa368 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,adc084s021.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,adc084s021.yaml @@ -19,8 +19,6 @@ properties: reg: maxItems: 1 - spi-max-frequency: true - vref-supply: description: External reference, needed to establish input scaling @@ -37,7 +35,10 @@ required: - spi-cpol - spi-cpha -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/ti,adc108s102.yaml b/Documentation/devicetree/bindings/iio/adc/ti,adc108s102.yaml index ae5ce60987fe..9b072b057f16 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,adc108s102.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,adc108s102.yaml @@ -19,7 +19,6 @@ properties: reg: true vref-supply: true - spi-max-frequency: true "#io-channel-cells": const: 1 @@ -28,7 +27,10 @@ required: - reg - vref-supply -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/ti,adc12138.yaml b/Documentation/devicetree/bindings/iio/adc/ti,adc12138.yaml index ec3b2edf1fb7..076088a328c3 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,adc12138.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,adc12138.yaml @@ -31,8 +31,6 @@ properties: maxItems: 1 description: Conversion clock input. - spi-max-frequency: true - vref-p-supply: description: The regulator supply for positive analog voltage reference @@ -62,7 +60,10 @@ required: - clocks - vref-p-supply -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/ti,adc128s052.yaml b/Documentation/devicetree/bindings/iio/adc/ti,adc128s052.yaml index d54a0183f024..775eee972b12 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,adc128s052.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,adc128s052.yaml @@ -27,8 +27,6 @@ properties: reg: maxItems: 1 - spi-max-frequency: true - vref-supply: true "#io-channel-cells": @@ -39,7 +37,10 @@ required: - reg - vref-supply -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/ti,adc161s626.yaml b/Documentation/devicetree/bindings/iio/adc/ti,adc161s626.yaml index 3f4f334d6f73..afe782522904 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,adc161s626.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,adc161s626.yaml @@ -21,8 +21,6 @@ properties: reg: maxItems: 1 - spi-max-frequency: true - vdda-supply: true "#io-channel-cells": @@ -32,7 +30,10 @@ required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads124s08.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads124s08.yaml index 2e6abc9d746a..56a3f1766aab 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,ads124s08.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,ads124s08.yaml @@ -18,8 +18,6 @@ properties: reg: maxItems: 1 - spi-max-frequency: true - spi-cpha: true reset-gpios: @@ -32,7 +30,10 @@ required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads131e08.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads131e08.yaml index 2876397fc668..55c2c73626f4 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,ads131e08.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,ads131e08.yaml @@ -28,8 +28,6 @@ properties: reg: maxItems: 1 - spi-max-frequency: true - spi-cpha: true clocks: @@ -120,7 +118,10 @@ patternProperties: additionalProperties: false -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads8344.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads8344.yaml index b8c398187d5c..f75b2c702986 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,ads8344.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,ads8344.yaml @@ -19,8 +19,6 @@ properties: reg: maxItems: 1 - spi-max-frequency: true - vref-supply: description: Supply the 2.5V or 5V reference voltage @@ -32,7 +30,10 @@ required: - reg - vref-supply -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads8688.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads8688.yaml index a0af4b24877f..f26fdbc15f84 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,ads8688.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,ads8688.yaml @@ -25,13 +25,14 @@ properties: description: Optional external reference. If not supplied, assume REFSEL input tied low to enable the internal reference. - spi-max-frequency: true - required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/ti,tlc4541.yaml b/Documentation/devicetree/bindings/iio/adc/ti,tlc4541.yaml index 6c2539b3d707..314d1d99bf73 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,tlc4541.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,tlc4541.yaml @@ -21,8 +21,6 @@ properties: reg: maxItems: 1 - spi-max-frequency: true - vref-supply: true "#io-channel-cells": @@ -33,7 +31,10 @@ required: - reg - vref-supply -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/adc/ti,tsc2046.yaml b/Documentation/devicetree/bindings/iio/adc/ti,tsc2046.yaml index 601d69971d84..0b48814c0dc2 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,tsc2046.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,tsc2046.yaml @@ -23,8 +23,6 @@ properties: interrupts: maxItems: 1 - spi-max-frequency: true - "#io-channel-cells": const: 1 @@ -59,7 +57,10 @@ patternProperties: additionalProperties: false -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | -- cgit v1.2.3 From 5f72930016202ca44ef0f4502d14e9054fbb3644 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 16 Aug 2022 15:43:13 +0300 Subject: dt-bindings: iio: accel: use spi-peripheral-props.yaml Instead of listing directly properties typical for SPI peripherals, reference the spi-peripheral-props.yaml schema. This allows using all properties typical for SPI-connected devices, even these which device bindings author did not tried yet. Remove the spi-* properties which now come via spi-peripheral-props.yaml schema, except for the cases when device schema adds some constraints like maximum frequency. While changing additionalProperties->unevaluatedProperties, put it in typical place, just before example DTS. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220816124321.67817-3-krzysztof.kozlowski@linaro.org Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/accel/adi,adis16201.yaml | 7 ++++--- Documentation/devicetree/bindings/iio/accel/adi,adis16240.yaml | 7 ++++--- Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml | 7 ++++--- Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml | 7 ++++--- Documentation/devicetree/bindings/iio/accel/adi,adxl355.yaml | 7 ++++--- Documentation/devicetree/bindings/iio/accel/adi,adxl367.yaml | 7 ++++--- Documentation/devicetree/bindings/iio/accel/adi,adxl372.yaml | 7 ++++--- Documentation/devicetree/bindings/iio/accel/bosch,bma220.yaml | 7 ++++--- Documentation/devicetree/bindings/iio/accel/bosch,bma255.yaml | 5 ++++- Documentation/devicetree/bindings/iio/accel/bosch,bmi088.yaml | 7 ++++--- Documentation/devicetree/bindings/iio/accel/fsl,mma7455.yaml | 7 ++++--- Documentation/devicetree/bindings/iio/accel/kionix,kxsd9.yaml | 7 ++++--- Documentation/devicetree/bindings/iio/accel/murata,sca3300.yaml | 5 ++++- Documentation/devicetree/bindings/iio/accel/nxp,fxls8962af.yaml | 7 ++++--- 14 files changed, 56 insertions(+), 38 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adis16201.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adis16201.yaml index 6f8f8a6258fe..7332442e5661 100644 --- a/Documentation/devicetree/bindings/iio/accel/adi,adis16201.yaml +++ b/Documentation/devicetree/bindings/iio/accel/adi,adis16201.yaml @@ -27,15 +27,16 @@ properties: interrupts: maxItems: 1 - spi-max-frequency: true - vdd-supply: true required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adis16240.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adis16240.yaml index 8d829ef878bc..f6f97164c2ca 100644 --- a/Documentation/devicetree/bindings/iio/accel/adi,adis16240.yaml +++ b/Documentation/devicetree/bindings/iio/accel/adi,adis16240.yaml @@ -25,14 +25,15 @@ properties: interrupts: maxItems: 1 - spi-max-frequency: true - required: - compatible - reg - interrupts -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml index d6afc1b8c272..05fa7af409cc 100644 --- a/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml +++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml @@ -24,8 +24,6 @@ properties: spi-3wire: true - spi-max-frequency: true - vs-supply: description: Regulator that supplies power to the accelerometer @@ -48,7 +46,10 @@ required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml index 9bb039e2f533..346abfb13a3a 100644 --- a/Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml +++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml @@ -32,8 +32,6 @@ properties: spi-cpol: true - spi-max-frequency: true - interrupts: maxItems: 1 @@ -42,7 +40,10 @@ required: - reg - interrupts -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl355.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl355.yaml index ba54d6998f2e..14b487088ab4 100644 --- a/Documentation/devicetree/bindings/iio/accel/adi,adxl355.yaml +++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl355.yaml @@ -45,13 +45,14 @@ properties: vddio-supply: description: Regulator that provides power to the bus - spi-max-frequency: true - required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl367.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl367.yaml index d259e796c1d6..f10d98d34cb8 100644 --- a/Documentation/devicetree/bindings/iio/accel/adi,adxl367.yaml +++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl367.yaml @@ -35,8 +35,6 @@ properties: interrupts: maxItems: 1 - spi-max-frequency: true - vdd-supply: true vddio-supply: true @@ -45,7 +43,10 @@ required: - reg - interrupts -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl372.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl372.yaml index 38b59b6454ce..73a5c8f814cc 100644 --- a/Documentation/devicetree/bindings/iio/accel/adi,adxl372.yaml +++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl372.yaml @@ -25,14 +25,15 @@ properties: interrupts: maxItems: 1 - spi-max-frequency: true - required: - compatible - reg - interrupts -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/accel/bosch,bma220.yaml b/Documentation/devicetree/bindings/iio/accel/bosch,bma220.yaml index 942b23ad0712..5dd06f5905b4 100644 --- a/Documentation/devicetree/bindings/iio/accel/bosch,bma220.yaml +++ b/Documentation/devicetree/bindings/iio/accel/bosch,bma220.yaml @@ -20,8 +20,6 @@ properties: interrupts: maxItems: 1 - spi-max-frequency: true - vdda-supply: true vddd-supply: true vddio-supply: true @@ -30,7 +28,10 @@ required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/accel/bosch,bma255.yaml b/Documentation/devicetree/bindings/iio/accel/bosch,bma255.yaml index 478e75ae0885..457a709b583c 100644 --- a/Documentation/devicetree/bindings/iio/accel/bosch,bma255.yaml +++ b/Documentation/devicetree/bindings/iio/accel/bosch,bma255.yaml @@ -72,7 +72,10 @@ required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/accel/bosch,bmi088.yaml b/Documentation/devicetree/bindings/iio/accel/bosch,bmi088.yaml index 272eb48eef5a..3cb82576d758 100644 --- a/Documentation/devicetree/bindings/iio/accel/bosch,bmi088.yaml +++ b/Documentation/devicetree/bindings/iio/accel/bosch,bmi088.yaml @@ -24,8 +24,6 @@ properties: reg: maxItems: 1 - spi-max-frequency: true - vdd-supply: true vddio-supply: true @@ -50,7 +48,10 @@ required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/accel/fsl,mma7455.yaml b/Documentation/devicetree/bindings/iio/accel/fsl,mma7455.yaml index 9c7c66feeffc..c8659c5eba2a 100644 --- a/Documentation/devicetree/bindings/iio/accel/fsl,mma7455.yaml +++ b/Documentation/devicetree/bindings/iio/accel/fsl,mma7455.yaml @@ -39,13 +39,14 @@ properties: - "INT1" - "INT2" - spi-max-frequency: true - required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/accel/kionix,kxsd9.yaml b/Documentation/devicetree/bindings/iio/accel/kionix,kxsd9.yaml index 390b87242fcb..f64d99b35492 100644 --- a/Documentation/devicetree/bindings/iio/accel/kionix,kxsd9.yaml +++ b/Documentation/devicetree/bindings/iio/accel/kionix,kxsd9.yaml @@ -29,13 +29,14 @@ properties: mount-matrix: description: an optional 3x3 mounting rotation matrix. - spi-max-frequency: true - required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/accel/murata,sca3300.yaml b/Documentation/devicetree/bindings/iio/accel/murata,sca3300.yaml index f6e2a16a710b..00c990caa1e4 100644 --- a/Documentation/devicetree/bindings/iio/accel/murata,sca3300.yaml +++ b/Documentation/devicetree/bindings/iio/accel/murata,sca3300.yaml @@ -29,7 +29,10 @@ required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/accel/nxp,fxls8962af.yaml b/Documentation/devicetree/bindings/iio/accel/nxp,fxls8962af.yaml index ad529ab2c6e2..65ce8ea14b52 100644 --- a/Documentation/devicetree/bindings/iio/accel/nxp,fxls8962af.yaml +++ b/Documentation/devicetree/bindings/iio/accel/nxp,fxls8962af.yaml @@ -27,8 +27,6 @@ properties: vdd-supply: description: phandle to the regulator that provides power to the accelerometer - spi-max-frequency: true - interrupts: maxItems: 1 @@ -44,7 +42,10 @@ required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | -- cgit v1.2.3 From 1deca207e1544a1683e300cd6c3a71e46c7b58ef Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 16 Aug 2022 15:43:14 +0300 Subject: dt-bindings: iio: amplifiers: adi,ada4250: use spi-peripheral-props.yaml Instead of listing directly properties typical for SPI peripherals, reference the spi-peripheral-props.yaml schema. This allows using all properties typical for SPI-connected devices, even these which device bindings author did not tried yet. Remove the spi-* properties which now come via spi-peripheral-props.yaml schema, except for the cases when device schema adds some constraints like maximum frequency. While changing additionalProperties->unevaluatedProperties, put it in typical place, just before example DTS. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220816124321.67817-4-krzysztof.kozlowski@linaro.org Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/amplifiers/adi,ada4250.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/amplifiers/adi,ada4250.yaml b/Documentation/devicetree/bindings/iio/amplifiers/adi,ada4250.yaml index 5277479be382..c15da155d300 100644 --- a/Documentation/devicetree/bindings/iio/amplifiers/adi,ada4250.yaml +++ b/Documentation/devicetree/bindings/iio/amplifiers/adi,ada4250.yaml @@ -27,14 +27,15 @@ properties: Enable internal buffer to drive the reference pin. type: boolean - spi-max-frequency: true - required: - compatible - reg - avdd-supply -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | -- cgit v1.2.3 From 25d0469946c3b528c34ef517dd57f6d4b5595e99 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 16 Aug 2022 15:43:15 +0300 Subject: dt-bindings: iio: dac: use spi-peripheral-props.yaml Instead of listing directly properties typical for SPI peripherals, reference the spi-peripheral-props.yaml schema. This allows using all properties typical for SPI-connected devices, even these which device bindings author did not tried yet. Remove the spi-* properties which now come via spi-peripheral-props.yaml schema, except for the cases when device schema adds some constraints like maximum frequency. While changing additionalProperties->unevaluatedProperties, put it in typical place, just before example DTS. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220816124321.67817-5-krzysztof.kozlowski@linaro.org Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/dac/adi,ad5064.yaml | 7 +++---- Documentation/devicetree/bindings/iio/dac/adi,ad5360.yaml | 7 +++---- Documentation/devicetree/bindings/iio/dac/adi,ad5380.yaml | 9 +++++---- Documentation/devicetree/bindings/iio/dac/adi,ad5421.yaml | 7 ++++--- Documentation/devicetree/bindings/iio/dac/adi,ad5449.yaml | 7 +++---- Documentation/devicetree/bindings/iio/dac/adi,ad5624r.yaml | 9 +++++---- Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml | 9 +++++---- Documentation/devicetree/bindings/iio/dac/adi,ad5755.yaml | 9 +++++---- Documentation/devicetree/bindings/iio/dac/adi,ad5758.yaml | 4 ++-- Documentation/devicetree/bindings/iio/dac/adi,ad5761.yaml | 7 +++---- Documentation/devicetree/bindings/iio/dac/adi,ad5764.yaml | 7 +++---- Documentation/devicetree/bindings/iio/dac/adi,ad5770r.yaml | 7 ++++--- Documentation/devicetree/bindings/iio/dac/adi,ad5791.yaml | 9 +++++---- Documentation/devicetree/bindings/iio/dac/adi,ad8801.yaml | 7 +++---- Documentation/devicetree/bindings/iio/dac/microchip,mcp4922.yaml | 9 +++++---- Documentation/devicetree/bindings/iio/dac/ti,dac082s085.yaml | 9 +++++---- Documentation/devicetree/bindings/iio/dac/ti,dac7311.yaml | 7 ++++--- Documentation/devicetree/bindings/iio/dac/ti,dac7612.yaml | 7 ++++--- 18 files changed, 71 insertions(+), 66 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5064.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5064.yaml index 05ed4e0ec364..c04165fa9259 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5064.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5064.yaml @@ -95,15 +95,12 @@ properties: vrefD-supply: true vref-supply: true - spi-max-frequency: true - -additionalProperties: false - required: - compatible - reg allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# - # Shared external vref, no internal reference if: properties: @@ -232,6 +229,8 @@ allOf: - vrefA-supply - vrefB-supply +unevaluatedProperties: false + examples: - | spi { diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5360.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5360.yaml index 65f86f26947c..86e2884cdfb1 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5360.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5360.yaml @@ -28,10 +28,6 @@ properties: vref1-supply: true vref2-supply: true - spi-max-frequency: true - -additionalProperties: false - required: - compatible - reg @@ -39,6 +35,7 @@ required: - vref1-supply allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# - if: properties: compatible: @@ -63,6 +60,8 @@ allOf: required: - vref2-supply +unevaluatedProperties: false + examples: - | spi { diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5380.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5380.yaml index d599b418a020..ff50c72c62b5 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5380.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5380.yaml @@ -39,14 +39,15 @@ properties: description: If not supplied devices will use internal regulators. - spi-max-frequency: true - -additionalProperties: false - required: - compatible - reg +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + examples: - | spi { diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5421.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5421.yaml index 188f656617e3..52d089ebde95 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5421.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5421.yaml @@ -26,13 +26,14 @@ properties: maxItems: 1 description: Fault signal. - spi-max-frequency: true - required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5449.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5449.yaml index 044332c97743..d2af2d491986 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5449.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5449.yaml @@ -27,19 +27,16 @@ properties: reg: maxItems: 1 - spi-max-frequency: true - VREF-supply: true VREFA-supply: true VREFB-supply: true -additionalProperties: false - required: - compatible - reg allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# - if: properties: compatible: @@ -72,6 +69,8 @@ allOf: - VREFA-supply - VREFB-supply +unevaluatedProperties: false + examples: - | spi { diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5624r.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5624r.yaml index 330383b85eeb..4d5111a5f9bd 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5624r.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5624r.yaml @@ -22,17 +22,18 @@ properties: reg: maxItems: 1 - spi-max-frequency: true - vref-supply: description: If not present, internal reference will be used. -additionalProperties: false - required: - compatible - reg +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + examples: - | spi { diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml index 5c26441eae9f..13f214234b8e 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml @@ -53,14 +53,15 @@ properties: vcc-supply: description: If not supplied the internal reference is used. - spi-max-frequency: true - -additionalProperties: false - required: - compatible - reg +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + examples: - | spi { diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5755.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5755.yaml index f866b88e1440..9a3c2926bf85 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5755.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5755.yaml @@ -25,8 +25,6 @@ properties: description: Either this or spi-cpol but not both. spi-cpol: true - spi-max-frequency: true - adi,ext-dc-dc-compenstation-resistor: $ref: /schemas/types.yaml#/definitions/flag description: @@ -67,8 +65,6 @@ required: - compatible - reg -additionalProperties: false - patternProperties: "^channel@[0-7]$": type: object @@ -123,6 +119,11 @@ oneOf: - required: - spi-cpol +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + examples: - | spi { diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5758.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5758.yaml index fd4edca34a28..e49e7556175d 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5758.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5758.yaml @@ -16,7 +16,6 @@ properties: reg: maxItems: 1 - spi-max-frequency: true spi-cpha: true adi,dc-dc-mode: @@ -99,6 +98,7 @@ required: - adi,dc-dc-mode allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# - if: properties: adi,dc-dc-mode: @@ -115,7 +115,7 @@ allOf: required: - adi,range-microvolt -additionalProperties: false +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5761.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5761.yaml index 7f95a9ed55fe..df550b5af2f7 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5761.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5761.yaml @@ -22,18 +22,15 @@ properties: reg: maxItems: 1 - spi-max-frequency: true - vref-supply: description: If not supplied, internal reference will be used. -additionalProperties: false - required: - compatible - reg allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# - if: properties: compatible: @@ -45,6 +42,8 @@ allOf: required: - vref-supply +unevaluatedProperties: false + examples: - | spi { diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5764.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5764.yaml index 8e893d52bfb1..0b409a727a43 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5764.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5764.yaml @@ -22,18 +22,15 @@ properties: reg: maxItems: 1 - spi-max-frequency: true - vrefAB-supply: true vrefCD-supply: true -additionalProperties: false - required: - compatible - reg allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# - if: properties: compatible: @@ -46,6 +43,8 @@ allOf: - vrefAB-supply - vrefCD-supply +unevaluatedProperties: false + examples: - | spi { diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5770r.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5770r.yaml index 24ac40180ac1..ca5432ffdedb 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5770r.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5770r.yaml @@ -49,8 +49,6 @@ properties: asserted during driver probe. maxItems: 1 - spi-max-frequency: true - '#address-cells': const: 1 @@ -138,7 +136,10 @@ required: - channel@4 - channel@5 -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5791.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5791.yaml index 650d1ebdcec3..3a84739736f6 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5791.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5791.yaml @@ -23,19 +23,20 @@ properties: reg: maxItems: 1 - spi-max-frequency: true - vdd-supply: true vss-supply: true -additionalProperties: false - required: - compatible - reg - vdd-supply - vss-supply +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + examples: - | spi { diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad8801.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad8801.yaml index 6a3990a8d0ad..1849a2ff05c7 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad8801.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad8801.yaml @@ -19,19 +19,16 @@ properties: reg: maxItems: 1 - spi-max-frequency: true - vrefh-supply: true vrefl-supply: true -additionalProperties: false - required: - compatible - reg - vrefh-supply allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# - if: properties: compatible: @@ -44,6 +41,8 @@ allOf: properties: vrefl-supply: false +unevaluatedProperties: false + examples: - | spi { diff --git a/Documentation/devicetree/bindings/iio/dac/microchip,mcp4922.yaml b/Documentation/devicetree/bindings/iio/dac/microchip,mcp4922.yaml index 4c430abcdbf9..19374401e509 100644 --- a/Documentation/devicetree/bindings/iio/dac/microchip,mcp4922.yaml +++ b/Documentation/devicetree/bindings/iio/dac/microchip,mcp4922.yaml @@ -21,17 +21,18 @@ properties: reg: maxItems: 1 - spi-max-frequency: true - vref-supply: true -additionalProperties: false - required: - compatible - reg - vref-supply +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + examples: - | spi { diff --git a/Documentation/devicetree/bindings/iio/dac/ti,dac082s085.yaml b/Documentation/devicetree/bindings/iio/dac/ti,dac082s085.yaml index b0157050f1ee..201b04af2b22 100644 --- a/Documentation/devicetree/bindings/iio/dac/ti,dac082s085.yaml +++ b/Documentation/devicetree/bindings/iio/dac/ti,dac082s085.yaml @@ -33,21 +33,22 @@ properties: vref-supply: description: Needed to provide output scaling. - spi-max-frequency: true - required: - compatible - reg - vref-supply -additionalProperties: false - oneOf: - required: - spi-cpha - required: - spi-cpol +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + examples: - | vref_2v5_reg: regulator-vref { diff --git a/Documentation/devicetree/bindings/iio/dac/ti,dac7311.yaml b/Documentation/devicetree/bindings/iio/dac/ti,dac7311.yaml index 10be98d1f19c..a6814587dbc4 100644 --- a/Documentation/devicetree/bindings/iio/dac/ti,dac7311.yaml +++ b/Documentation/devicetree/bindings/iio/dac/ti,dac7311.yaml @@ -24,14 +24,15 @@ properties: Reference voltage must be supplied to establish the scaling of the output voltage. - spi-max-frequency: true - required: - compatible - reg - vref-supply -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/dac/ti,dac7612.yaml b/Documentation/devicetree/bindings/iio/dac/ti,dac7612.yaml index d172b142f6ed..20dd1370660d 100644 --- a/Documentation/devicetree/bindings/iio/dac/ti,dac7612.yaml +++ b/Documentation/devicetree/bindings/iio/dac/ti,dac7612.yaml @@ -29,13 +29,14 @@ properties: DACs are loaded when the pin connected to this GPIO is pulled low. maxItems: 1 - spi-max-frequency: true - required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | -- cgit v1.2.3 From 3e03f90fe0b6d08142e0934eb7db6c26a509b505 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 16 Aug 2022 15:43:16 +0300 Subject: dt-bindings: iio: frequency: adf4371: use spi-peripheral-props.yaml Instead of listing directly properties typical for SPI peripherals, reference the spi-peripheral-props.yaml schema. This allows using all properties typical for SPI-connected devices, even these which device bindings author did not tried yet. Remove the spi-* properties which now come via spi-peripheral-props.yaml schema, except for the cases when device schema adds some constraints like maximum frequency. While changing additionalProperties->unevaluatedProperties, put it in typical place, just before example DTS. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220816124321.67817-6-krzysztof.kozlowski@linaro.org Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/frequency/adf4371.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/frequency/adf4371.yaml b/Documentation/devicetree/bindings/iio/frequency/adf4371.yaml index 6b3a611e1cf1..0144f74a4768 100644 --- a/Documentation/devicetree/bindings/iio/frequency/adf4371.yaml +++ b/Documentation/devicetree/bindings/iio/frequency/adf4371.yaml @@ -40,15 +40,16 @@ properties: output stage will shut down until the ADF4371/ADF4372 achieves lock as measured by the digital lock detect circuitry. - spi-max-frequency: true - required: - compatible - reg - clocks - clock-names -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | -- cgit v1.2.3 From d9ca9d28068a151471141dd1b0ddbf272c2364ac Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 16 Aug 2022 15:43:17 +0300 Subject: dt-bindings: iio: health: ti,afe4403: use spi-peripheral-props.yaml Instead of listing directly properties typical for SPI peripherals, reference the spi-peripheral-props.yaml schema. This allows using all properties typical for SPI-connected devices, even these which device bindings author did not tried yet. Remove the spi-* properties which now come via spi-peripheral-props.yaml schema, except for the cases when device schema adds some constraints like maximum frequency. While changing additionalProperties->unevaluatedProperties, put it in typical place, just before example DTS. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220816124321.67817-7-krzysztof.kozlowski@linaro.org Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/health/ti,afe4403.yaml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/health/ti,afe4403.yaml b/Documentation/devicetree/bindings/iio/health/ti,afe4403.yaml index d861526c5c42..6c5ad426a016 100644 --- a/Documentation/devicetree/bindings/iio/health/ti,afe4403.yaml +++ b/Documentation/devicetree/bindings/iio/health/ti,afe4403.yaml @@ -25,14 +25,15 @@ properties: reset-gpios: true - spi-max-frequency: true - -additionalProperties: false - required: - compatible - reg +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + examples: - | #include -- cgit v1.2.3 From 8d98a8c6b0520aeaef1f0be6cbcadece65f855e9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 16 Aug 2022 15:43:18 +0300 Subject: dt-bindings: iio: imu: use spi-peripheral-props.yaml Instead of listing directly properties typical for SPI peripherals, reference the spi-peripheral-props.yaml schema. This allows using all properties typical for SPI-connected devices, even these which device bindings author did not tried yet. Remove the spi-* properties which now come via spi-peripheral-props.yaml schema, except for the cases when device schema adds some constraints like maximum frequency. While changing additionalProperties->unevaluatedProperties, put it in typical place, just before example DTS. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220816124321.67817-8-krzysztof.kozlowski@linaro.org Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/imu/adi,adis16460.yaml | 7 ++++--- Documentation/devicetree/bindings/iio/imu/adi,adis16480.yaml | 9 +++++---- Documentation/devicetree/bindings/iio/imu/bosch,bmi160.yaml | 7 ++++--- .../devicetree/bindings/iio/imu/invensense,icm42600.yaml | 6 ++++-- .../devicetree/bindings/iio/imu/invensense,mpu6050.yaml | 5 ++--- Documentation/devicetree/bindings/iio/imu/nxp,fxos8700.yaml | 7 ++++--- Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml | 9 +++++---- 7 files changed, 28 insertions(+), 22 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/imu/adi,adis16460.yaml b/Documentation/devicetree/bindings/iio/imu/adi,adis16460.yaml index 340be256f283..d166dbca18c3 100644 --- a/Documentation/devicetree/bindings/iio/imu/adi,adis16460.yaml +++ b/Documentation/devicetree/bindings/iio/imu/adi,adis16460.yaml @@ -25,8 +25,6 @@ properties: spi-cpol: true - spi-max-frequency: true - interrupts: maxItems: 1 @@ -35,7 +33,10 @@ required: - reg - interrupts -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/imu/adi,adis16480.yaml b/Documentation/devicetree/bindings/iio/imu/adi,adis16480.yaml index dd29dc6c4c19..56e0dc20f5e4 100644 --- a/Documentation/devicetree/bindings/iio/imu/adi,adis16480.yaml +++ b/Documentation/devicetree/bindings/iio/imu/adi,adis16480.yaml @@ -47,8 +47,6 @@ properties: - DIO3 - DIO4 - spi-max-frequency: true - spi-cpha: true spi-cpol: true @@ -96,8 +94,6 @@ properties: - DIO3 - DIO4 -additionalProperties: false - required: - compatible - reg @@ -106,6 +102,11 @@ required: - spi-cpol - spi-max-frequency +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + examples: - | #include diff --git a/Documentation/devicetree/bindings/iio/imu/bosch,bmi160.yaml b/Documentation/devicetree/bindings/iio/imu/bosch,bmi160.yaml index 6e73cd889b5c..a0760382548d 100644 --- a/Documentation/devicetree/bindings/iio/imu/bosch,bmi160.yaml +++ b/Documentation/devicetree/bindings/iio/imu/bosch,bmi160.yaml @@ -46,13 +46,14 @@ properties: mount-matrix: description: an optional 3x3 mounting rotation matrix - spi-max-frequency: true - required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml index 4c1c083d0e92..488349755c99 100644 --- a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml +++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml @@ -47,7 +47,6 @@ properties: vddio-supply: description: Regulator that provides power to the bus - spi-max-frequency: true spi-cpha: true spi-cpol: true @@ -56,7 +55,10 @@ required: - reg - interrupts -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml index 3ebc6526d82d..ec64d7877fe5 100644 --- a/Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml +++ b/Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml @@ -40,8 +40,6 @@ properties: interrupts: maxItems: 1 - spi-max-frequency: true - vdd-supply: true vddio-supply: true @@ -54,6 +52,7 @@ properties: These devices also support an auxiliary i2c bus via an i2c-gate. allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# - if: not: properties: @@ -67,7 +66,7 @@ allOf: properties: i2c-gate: false -additionalProperties: false +unevaluatedProperties: false required: - compatible diff --git a/Documentation/devicetree/bindings/iio/imu/nxp,fxos8700.yaml b/Documentation/devicetree/bindings/iio/imu/nxp,fxos8700.yaml index 0203b83b8587..24416b59b782 100644 --- a/Documentation/devicetree/bindings/iio/imu/nxp,fxos8700.yaml +++ b/Documentation/devicetree/bindings/iio/imu/nxp,fxos8700.yaml @@ -36,13 +36,14 @@ properties: drive-open-drain: type: boolean - spi-max-frequency: true - required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml b/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml index 5d4839f00898..0ceb29fb01b7 100644 --- a/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml +++ b/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml @@ -45,8 +45,6 @@ properties: description: Supports up to 2 interrupt lines via the INT1 and INT2 pins. - spi-max-frequency: true - vdd-supply: description: if defined provides VDD power to the sensor. @@ -81,12 +79,15 @@ properties: wakeup-source: $ref: /schemas/types.yaml#/definitions/flag -additionalProperties: false - required: - compatible - reg +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + examples: - | #include -- cgit v1.2.3 From efcdb1ab5030f231e377ac241fda83e1d3a126ed Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 16 Aug 2022 15:43:19 +0300 Subject: dt-bindings: iio: potentiometer: use spi-peripheral-props.yaml Instead of listing directly properties typical for SPI peripherals, reference the spi-peripheral-props.yaml schema. This allows using all properties typical for SPI-connected devices, even these which device bindings author did not tried yet. Remove the spi-* properties which now come via spi-peripheral-props.yaml schema, except for the cases when device schema adds some constraints like maximum frequency. While changing additionalProperties->unevaluatedProperties, put it in typical place, just before example DTS. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220816124321.67817-9-krzysztof.kozlowski@linaro.org Signed-off-by: Jonathan Cameron --- .../bindings/iio/potentiometer/microchip,mcp41010.yaml | 9 +++++---- .../devicetree/bindings/iio/potentiometer/microchip,mcp4131.yaml | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/potentiometer/microchip,mcp41010.yaml b/Documentation/devicetree/bindings/iio/potentiometer/microchip,mcp41010.yaml index 567697d996ec..87e88f2a9908 100644 --- a/Documentation/devicetree/bindings/iio/potentiometer/microchip,mcp41010.yaml +++ b/Documentation/devicetree/bindings/iio/potentiometer/microchip,mcp41010.yaml @@ -25,14 +25,15 @@ properties: reg: maxItems: 1 - spi-max-frequency: true - -additionalProperties: false - required: - compatible - reg +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + examples: - | spi { diff --git a/Documentation/devicetree/bindings/iio/potentiometer/microchip,mcp4131.yaml b/Documentation/devicetree/bindings/iio/potentiometer/microchip,mcp4131.yaml index 32e92bced81f..896fe0b5edcc 100644 --- a/Documentation/devicetree/bindings/iio/potentiometer/microchip,mcp4131.yaml +++ b/Documentation/devicetree/bindings/iio/potentiometer/microchip,mcp4131.yaml @@ -80,14 +80,15 @@ properties: reg: maxItems: 1 - spi-max-frequency: true - -additionalProperties: false - required: - compatible - reg +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + examples: - | spi { -- cgit v1.2.3 From 6920f48efd58596af8e58ad5c861bd15726165e7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 16 Aug 2022 15:43:20 +0300 Subject: dt-bindings: iio: samsung,sensorhub-rinato: use spi-peripheral-props.yaml Instead of listing directly properties typical for SPI peripherals, reference the spi-peripheral-props.yaml schema. This allows using all properties typical for SPI-connected devices, even these which device bindings author did not tried yet. Remove the spi-* properties which now come via spi-peripheral-props.yaml schema, except for the cases when device schema adds some constraints like maximum frequency. While changing additionalProperties->unevaluatedProperties, put it in typical place, just before example DTS. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220816124321.67817-10-krzysztof.kozlowski@linaro.org Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/samsung,sensorhub-rinato.yaml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/samsung,sensorhub-rinato.yaml b/Documentation/devicetree/bindings/iio/samsung,sensorhub-rinato.yaml index a88b3b14d6bd..dd2ae2bd1ad7 100644 --- a/Documentation/devicetree/bindings/iio/samsung,sensorhub-rinato.yaml +++ b/Documentation/devicetree/bindings/iio/samsung,sensorhub-rinato.yaml @@ -40,10 +40,6 @@ properties: description: Reset the sensorhub. - spi-max-frequency: true - -additionalProperties: false - required: - compatible - reg @@ -52,6 +48,11 @@ required: - mcu-ap-gpios - mcu-reset-gpios +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + examples: - | spi { -- cgit v1.2.3 From 14a4d22ead0d9c01a6d7e9cb7f1d321dd29d354b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 16 Aug 2022 15:43:21 +0300 Subject: dt-bindings: iio: temperature: use spi-peripheral-props.yaml Instead of listing directly properties typical for SPI peripherals, reference the spi-peripheral-props.yaml schema. This allows using all properties typical for SPI-connected devices, even these which device bindings author did not tried yet. Remove the spi-* properties which now come via spi-peripheral-props.yaml schema, except for the cases when device schema adds some constraints like maximum frequency. While changing additionalProperties->unevaluatedProperties, put it in typical place, just before example DTS. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220816124321.67817-11-krzysztof.kozlowski@linaro.org Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/temperature/maxim,max31855k.yaml | 4 ++-- .../devicetree/bindings/iio/temperature/maxim,max31856.yaml | 6 ++++-- .../devicetree/bindings/iio/temperature/maxim,max31865.yaml | 6 ++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/temperature/maxim,max31855k.yaml b/Documentation/devicetree/bindings/iio/temperature/maxim,max31855k.yaml index 9969bac66aa1..0805ed7e2113 100644 --- a/Documentation/devicetree/bindings/iio/temperature/maxim,max31855k.yaml +++ b/Documentation/devicetree/bindings/iio/temperature/maxim,max31855k.yaml @@ -32,7 +32,6 @@ properties: reg: maxItems: 1 - spi-max-frequency: true spi-cpha: true required: @@ -40,6 +39,7 @@ required: - reg allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# - if: properties: compatible: @@ -53,7 +53,7 @@ allOf: properties: spi-cpha: false -additionalProperties: false +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/temperature/maxim,max31856.yaml b/Documentation/devicetree/bindings/iio/temperature/maxim,max31856.yaml index 873b34766676..228a94165487 100644 --- a/Documentation/devicetree/bindings/iio/temperature/maxim,max31856.yaml +++ b/Documentation/devicetree/bindings/iio/temperature/maxim,max31856.yaml @@ -19,7 +19,6 @@ properties: reg: maxItems: 1 - spi-max-frequency: true spi-cpha: true thermocouple-type: @@ -34,7 +33,10 @@ required: - reg - spi-cpha -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/iio/temperature/maxim,max31865.yaml b/Documentation/devicetree/bindings/iio/temperature/maxim,max31865.yaml index aafb33b16549..a2823ed6867b 100644 --- a/Documentation/devicetree/bindings/iio/temperature/maxim,max31865.yaml +++ b/Documentation/devicetree/bindings/iio/temperature/maxim,max31865.yaml @@ -25,7 +25,6 @@ properties: enables 3-wire RTD connection. Else 2-wire or 4-wire RTD connection. type: boolean - spi-max-frequency: true spi-cpha: true required: @@ -33,7 +32,10 @@ required: - reg - spi-cpha -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | -- cgit v1.2.3 From 83856aaab45da0fd34f94aac0371ba80668c1dbc Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 13 Aug 2022 17:06:00 +0100 Subject: staging: iio: frequency: ad9832: Fix alignment for DMA safety ____cacheline_aligned is an insufficient guarantee for non-coherent DMA on platforms with 128 byte cachelines above L1. Switch to the updated IIO_DMA_MINALIGN definition. Whilst here, move the marking to cover the whole union. That has no functional affect, but makes it slightly easier to see what is going on. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220813160600.1157169-1-jic23@kernel.org --- drivers/staging/iio/frequency/ad9832.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c index f43464db618a..6f9eebd6c7ee 100644 --- a/drivers/staging/iio/frequency/ad9832.c +++ b/drivers/staging/iio/frequency/ad9832.c @@ -112,10 +112,10 @@ struct ad9832_state { * transfer buffers to live in their own cache lines. */ union { - __be16 freq_data[4]____cacheline_aligned; + __be16 freq_data[4]; __be16 phase_data[2]; __be16 data; - }; + } __aligned(IIO_DMA_MINALIGN); }; static unsigned long ad9832_calc_freqreg(unsigned long mclk, unsigned long fout) -- cgit v1.2.3 From 955c2aa9cff2dd07ff798ca8c883398731687972 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Mon, 15 Aug 2022 18:29:21 -0400 Subject: iio: stx104: Move to addac subdirectory The stx104 driver supports both ADC and DAC functionality. Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/20220815222921.138945-1-william.gray@linaro.org Signed-off-by: Jonathan Cameron --- MAINTAINERS | 2 +- drivers/iio/adc/Kconfig | 16 -- drivers/iio/adc/Makefile | 1 - drivers/iio/adc/stx104.c | 402 --------------------------------------------- drivers/iio/addac/Kconfig | 16 ++ drivers/iio/addac/Makefile | 1 + drivers/iio/addac/stx104.c | 402 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 420 insertions(+), 420 deletions(-) delete mode 100644 drivers/iio/adc/stx104.c create mode 100644 drivers/iio/addac/stx104.c diff --git a/MAINTAINERS b/MAINTAINERS index a88edf508003..7dcae9243256 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1374,7 +1374,7 @@ APEX EMBEDDED SYSTEMS STX104 IIO DRIVER M: William Breathitt Gray L: linux-iio@vger.kernel.org S: Maintained -F: drivers/iio/adc/stx104.c +F: drivers/iio/addac/stx104.c APM DRIVER M: Jiri Kosina diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 5a3e8d9ae26c..e3c2881ed23a 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1037,22 +1037,6 @@ config STMPE_ADC Say yes here to build support for ST Microelectronics STMPE built-in ADC block (stmpe811). -config STX104 - tristate "Apex Embedded Systems STX104 driver" - depends on PC104 && X86 - select ISA_BUS_API - select GPIOLIB - help - Say yes here to build support for the Apex Embedded Systems STX104 - integrated analog PC/104 card. - - This driver supports the 16 channels of single-ended (8 channels of - differential) analog inputs, 2 channels of analog output, 4 digital - inputs, and 4 digital outputs provided by the STX104. - - The base port addresses for the devices may be configured via the base - array module parameter. - config SUN4I_GPADC tristate "Support for the Allwinner SoCs GPADC" depends on IIO diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index c1a861a978ad..ab084094263b 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -89,7 +89,6 @@ obj-$(CONFIG_RICHTEK_RTQ6056) += rtq6056.o obj-$(CONFIG_RZG2L_ADC) += rzg2l_adc.o obj-$(CONFIG_SC27XX_ADC) += sc27xx_adc.o obj-$(CONFIG_SPEAR_ADC) += spear_adc.o -obj-$(CONFIG_STX104) += stx104.o obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o obj-$(CONFIG_STM32_ADC) += stm32-adc.o diff --git a/drivers/iio/adc/stx104.c b/drivers/iio/adc/stx104.c deleted file mode 100644 index 48a91a95e597..000000000000 --- a/drivers/iio/adc/stx104.c +++ /dev/null @@ -1,402 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * IIO driver for the Apex Embedded Systems STX104 - * Copyright (C) 2016 William Breathitt Gray - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define STX104_OUT_CHAN(chan) { \ - .type = IIO_VOLTAGE, \ - .channel = chan, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .indexed = 1, \ - .output = 1 \ -} -#define STX104_IN_CHAN(chan, diff) { \ - .type = IIO_VOLTAGE, \ - .channel = chan, \ - .channel2 = chan, \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \ - BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .indexed = 1, \ - .differential = diff \ -} - -#define STX104_NUM_OUT_CHAN 2 - -#define STX104_EXTENT 16 - -static unsigned int base[max_num_isa_dev(STX104_EXTENT)]; -static unsigned int num_stx104; -module_param_hw_array(base, uint, ioport, &num_stx104, 0); -MODULE_PARM_DESC(base, "Apex Embedded Systems STX104 base addresses"); - -/** - * struct stx104_reg - device register structure - * @ssr_ad: Software Strobe Register and ADC Data - * @achan: ADC Channel - * @dio: Digital I/O - * @dac: DAC Channels - * @cir_asr: Clear Interrupts and ADC Status - * @acr: ADC Control - * @pccr_fsh: Pacer Clock Control and FIFO Status MSB - * @acfg: ADC Configuration - */ -struct stx104_reg { - u16 ssr_ad; - u8 achan; - u8 dio; - u16 dac[2]; - u8 cir_asr; - u8 acr; - u8 pccr_fsh; - u8 acfg; -}; - -/** - * struct stx104_iio - IIO device private data structure - * @chan_out_states: channels' output states - * @reg: I/O address offset for the device registers - */ -struct stx104_iio { - unsigned int chan_out_states[STX104_NUM_OUT_CHAN]; - struct stx104_reg __iomem *reg; -}; - -/** - * struct stx104_gpio - GPIO device private data structure - * @chip: instance of the gpio_chip - * @lock: synchronization lock to prevent I/O race conditions - * @base: base port address of the GPIO device - * @out_state: output bits state - */ -struct stx104_gpio { - struct gpio_chip chip; - spinlock_t lock; - u8 __iomem *base; - unsigned int out_state; -}; - -static int stx104_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int *val, int *val2, long mask) -{ - struct stx104_iio *const priv = iio_priv(indio_dev); - struct stx104_reg __iomem *const reg = priv->reg; - unsigned int adc_config; - int adbu; - int gain; - - switch (mask) { - case IIO_CHAN_INFO_HARDWAREGAIN: - /* get gain configuration */ - adc_config = ioread8(®->acfg); - gain = adc_config & 0x3; - - *val = 1 << gain; - return IIO_VAL_INT; - case IIO_CHAN_INFO_RAW: - if (chan->output) { - *val = priv->chan_out_states[chan->channel]; - return IIO_VAL_INT; - } - - /* select ADC channel */ - iowrite8(chan->channel | (chan->channel << 4), ®->achan); - - /* trigger ADC sample capture by writing to the 8-bit - * Software Strobe Register and wait for completion - */ - iowrite8(0, ®->ssr_ad); - while (ioread8(®->cir_asr) & BIT(7)); - - *val = ioread16(®->ssr_ad); - return IIO_VAL_INT; - case IIO_CHAN_INFO_OFFSET: - /* get ADC bipolar/unipolar configuration */ - adc_config = ioread8(®->acfg); - adbu = !(adc_config & BIT(2)); - - *val = -32768 * adbu; - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - /* get ADC bipolar/unipolar and gain configuration */ - adc_config = ioread8(®->acfg); - adbu = !(adc_config & BIT(2)); - gain = adc_config & 0x3; - - *val = 5; - *val2 = 15 - adbu + gain; - return IIO_VAL_FRACTIONAL_LOG2; - } - - return -EINVAL; -} - -static int stx104_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int val, int val2, long mask) -{ - struct stx104_iio *const priv = iio_priv(indio_dev); - - switch (mask) { - case IIO_CHAN_INFO_HARDWAREGAIN: - /* Only four gain states (x1, x2, x4, x8) */ - switch (val) { - case 1: - iowrite8(0, &priv->reg->acfg); - break; - case 2: - iowrite8(1, &priv->reg->acfg); - break; - case 4: - iowrite8(2, &priv->reg->acfg); - break; - case 8: - iowrite8(3, &priv->reg->acfg); - break; - default: - return -EINVAL; - } - - return 0; - case IIO_CHAN_INFO_RAW: - if (chan->output) { - /* DAC can only accept up to a 16-bit value */ - if ((unsigned int)val > 65535) - return -EINVAL; - - priv->chan_out_states[chan->channel] = val; - iowrite16(val, &priv->reg->dac[chan->channel]); - - return 0; - } - return -EINVAL; - } - - return -EINVAL; -} - -static const struct iio_info stx104_info = { - .read_raw = stx104_read_raw, - .write_raw = stx104_write_raw -}; - -/* single-ended input channels configuration */ -static const struct iio_chan_spec stx104_channels_sing[] = { - STX104_OUT_CHAN(0), STX104_OUT_CHAN(1), - STX104_IN_CHAN(0, 0), STX104_IN_CHAN(1, 0), STX104_IN_CHAN(2, 0), - STX104_IN_CHAN(3, 0), STX104_IN_CHAN(4, 0), STX104_IN_CHAN(5, 0), - STX104_IN_CHAN(6, 0), STX104_IN_CHAN(7, 0), STX104_IN_CHAN(8, 0), - STX104_IN_CHAN(9, 0), STX104_IN_CHAN(10, 0), STX104_IN_CHAN(11, 0), - STX104_IN_CHAN(12, 0), STX104_IN_CHAN(13, 0), STX104_IN_CHAN(14, 0), - STX104_IN_CHAN(15, 0) -}; -/* differential input channels configuration */ -static const struct iio_chan_spec stx104_channels_diff[] = { - STX104_OUT_CHAN(0), STX104_OUT_CHAN(1), - STX104_IN_CHAN(0, 1), STX104_IN_CHAN(1, 1), STX104_IN_CHAN(2, 1), - STX104_IN_CHAN(3, 1), STX104_IN_CHAN(4, 1), STX104_IN_CHAN(5, 1), - STX104_IN_CHAN(6, 1), STX104_IN_CHAN(7, 1) -}; - -static int stx104_gpio_get_direction(struct gpio_chip *chip, - unsigned int offset) -{ - /* GPIO 0-3 are input only, while the rest are output only */ - if (offset < 4) - return 1; - - return 0; -} - -static int stx104_gpio_direction_input(struct gpio_chip *chip, - unsigned int offset) -{ - if (offset >= 4) - return -EINVAL; - - return 0; -} - -static int stx104_gpio_direction_output(struct gpio_chip *chip, - unsigned int offset, int value) -{ - if (offset < 4) - return -EINVAL; - - chip->set(chip, offset, value); - return 0; -} - -static int stx104_gpio_get(struct gpio_chip *chip, unsigned int offset) -{ - struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); - - if (offset >= 4) - return -EINVAL; - - return !!(ioread8(stx104gpio->base) & BIT(offset)); -} - -static int stx104_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, - unsigned long *bits) -{ - struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); - - *bits = ioread8(stx104gpio->base); - - return 0; -} - -static void stx104_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) -{ - struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); - const unsigned int mask = BIT(offset) >> 4; - unsigned long flags; - - if (offset < 4) - return; - - spin_lock_irqsave(&stx104gpio->lock, flags); - - if (value) - stx104gpio->out_state |= mask; - else - stx104gpio->out_state &= ~mask; - - iowrite8(stx104gpio->out_state, stx104gpio->base); - - spin_unlock_irqrestore(&stx104gpio->lock, flags); -} - -#define STX104_NGPIO 8 -static const char *stx104_names[STX104_NGPIO] = { - "DIN0", "DIN1", "DIN2", "DIN3", "DOUT0", "DOUT1", "DOUT2", "DOUT3" -}; - -static void stx104_gpio_set_multiple(struct gpio_chip *chip, - unsigned long *mask, unsigned long *bits) -{ - struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); - unsigned long flags; - - /* verify masked GPIO are output */ - if (!(*mask & 0xF0)) - return; - - *mask >>= 4; - *bits >>= 4; - - spin_lock_irqsave(&stx104gpio->lock, flags); - - stx104gpio->out_state &= ~*mask; - stx104gpio->out_state |= *mask & *bits; - iowrite8(stx104gpio->out_state, stx104gpio->base); - - spin_unlock_irqrestore(&stx104gpio->lock, flags); -} - -static int stx104_probe(struct device *dev, unsigned int id) -{ - struct iio_dev *indio_dev; - struct stx104_iio *priv; - struct stx104_gpio *stx104gpio; - int err; - - indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); - if (!indio_dev) - return -ENOMEM; - - stx104gpio = devm_kzalloc(dev, sizeof(*stx104gpio), GFP_KERNEL); - if (!stx104gpio) - return -ENOMEM; - - if (!devm_request_region(dev, base[id], STX104_EXTENT, - dev_name(dev))) { - dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", - base[id], base[id] + STX104_EXTENT); - return -EBUSY; - } - - priv = iio_priv(indio_dev); - priv->reg = devm_ioport_map(dev, base[id], STX104_EXTENT); - if (!priv->reg) - return -ENOMEM; - - indio_dev->info = &stx104_info; - indio_dev->modes = INDIO_DIRECT_MODE; - - /* determine if differential inputs */ - if (ioread8(&priv->reg->cir_asr) & BIT(5)) { - indio_dev->num_channels = ARRAY_SIZE(stx104_channels_diff); - indio_dev->channels = stx104_channels_diff; - } else { - indio_dev->num_channels = ARRAY_SIZE(stx104_channels_sing); - indio_dev->channels = stx104_channels_sing; - } - - indio_dev->name = dev_name(dev); - - /* configure device for software trigger operation */ - iowrite8(0, &priv->reg->acr); - - /* initialize gain setting to x1 */ - iowrite8(0, &priv->reg->acfg); - - /* initialize DAC output to 0V */ - iowrite16(0, &priv->reg->dac[0]); - iowrite16(0, &priv->reg->dac[1]); - - stx104gpio->chip.label = dev_name(dev); - stx104gpio->chip.parent = dev; - stx104gpio->chip.owner = THIS_MODULE; - stx104gpio->chip.base = -1; - stx104gpio->chip.ngpio = STX104_NGPIO; - stx104gpio->chip.names = stx104_names; - stx104gpio->chip.get_direction = stx104_gpio_get_direction; - stx104gpio->chip.direction_input = stx104_gpio_direction_input; - stx104gpio->chip.direction_output = stx104_gpio_direction_output; - stx104gpio->chip.get = stx104_gpio_get; - stx104gpio->chip.get_multiple = stx104_gpio_get_multiple; - stx104gpio->chip.set = stx104_gpio_set; - stx104gpio->chip.set_multiple = stx104_gpio_set_multiple; - stx104gpio->base = &priv->reg->dio; - stx104gpio->out_state = 0x0; - - spin_lock_init(&stx104gpio->lock); - - err = devm_gpiochip_add_data(dev, &stx104gpio->chip, stx104gpio); - if (err) { - dev_err(dev, "GPIO registering failed (%d)\n", err); - return err; - } - - return devm_iio_device_register(dev, indio_dev); -} - -static struct isa_driver stx104_driver = { - .probe = stx104_probe, - .driver = { - .name = "stx104" - }, -}; - -module_isa_driver(stx104_driver, num_stx104); - -MODULE_AUTHOR("William Breathitt Gray "); -MODULE_DESCRIPTION("Apex Embedded Systems STX104 IIO driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/addac/Kconfig b/drivers/iio/addac/Kconfig index 138492362f20..fcf6d2269bfc 100644 --- a/drivers/iio/addac/Kconfig +++ b/drivers/iio/addac/Kconfig @@ -17,4 +17,20 @@ config AD74413R To compile this driver as a module, choose M here: the module will be called ad74413r. +config STX104 + tristate "Apex Embedded Systems STX104 driver" + depends on PC104 && X86 + select ISA_BUS_API + select GPIOLIB + help + Say yes here to build support for the Apex Embedded Systems STX104 + integrated analog PC/104 card. + + This driver supports the 16 channels of single-ended (8 channels of + differential) analog inputs, 2 channels of analog output, 4 digital + inputs, and 4 digital outputs provided by the STX104. + + The base port addresses for the devices may be configured via the base + array module parameter. + endmenu diff --git a/drivers/iio/addac/Makefile b/drivers/iio/addac/Makefile index cfd4bbe64ad3..17de20ef0d8e 100644 --- a/drivers/iio/addac/Makefile +++ b/drivers/iio/addac/Makefile @@ -5,3 +5,4 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_AD74413R) += ad74413r.o +obj-$(CONFIG_STX104) += stx104.o diff --git a/drivers/iio/addac/stx104.c b/drivers/iio/addac/stx104.c new file mode 100644 index 000000000000..48a91a95e597 --- /dev/null +++ b/drivers/iio/addac/stx104.c @@ -0,0 +1,402 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * IIO driver for the Apex Embedded Systems STX104 + * Copyright (C) 2016 William Breathitt Gray + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STX104_OUT_CHAN(chan) { \ + .type = IIO_VOLTAGE, \ + .channel = chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .indexed = 1, \ + .output = 1 \ +} +#define STX104_IN_CHAN(chan, diff) { \ + .type = IIO_VOLTAGE, \ + .channel = chan, \ + .channel2 = chan, \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \ + BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .indexed = 1, \ + .differential = diff \ +} + +#define STX104_NUM_OUT_CHAN 2 + +#define STX104_EXTENT 16 + +static unsigned int base[max_num_isa_dev(STX104_EXTENT)]; +static unsigned int num_stx104; +module_param_hw_array(base, uint, ioport, &num_stx104, 0); +MODULE_PARM_DESC(base, "Apex Embedded Systems STX104 base addresses"); + +/** + * struct stx104_reg - device register structure + * @ssr_ad: Software Strobe Register and ADC Data + * @achan: ADC Channel + * @dio: Digital I/O + * @dac: DAC Channels + * @cir_asr: Clear Interrupts and ADC Status + * @acr: ADC Control + * @pccr_fsh: Pacer Clock Control and FIFO Status MSB + * @acfg: ADC Configuration + */ +struct stx104_reg { + u16 ssr_ad; + u8 achan; + u8 dio; + u16 dac[2]; + u8 cir_asr; + u8 acr; + u8 pccr_fsh; + u8 acfg; +}; + +/** + * struct stx104_iio - IIO device private data structure + * @chan_out_states: channels' output states + * @reg: I/O address offset for the device registers + */ +struct stx104_iio { + unsigned int chan_out_states[STX104_NUM_OUT_CHAN]; + struct stx104_reg __iomem *reg; +}; + +/** + * struct stx104_gpio - GPIO device private data structure + * @chip: instance of the gpio_chip + * @lock: synchronization lock to prevent I/O race conditions + * @base: base port address of the GPIO device + * @out_state: output bits state + */ +struct stx104_gpio { + struct gpio_chip chip; + spinlock_t lock; + u8 __iomem *base; + unsigned int out_state; +}; + +static int stx104_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long mask) +{ + struct stx104_iio *const priv = iio_priv(indio_dev); + struct stx104_reg __iomem *const reg = priv->reg; + unsigned int adc_config; + int adbu; + int gain; + + switch (mask) { + case IIO_CHAN_INFO_HARDWAREGAIN: + /* get gain configuration */ + adc_config = ioread8(®->acfg); + gain = adc_config & 0x3; + + *val = 1 << gain; + return IIO_VAL_INT; + case IIO_CHAN_INFO_RAW: + if (chan->output) { + *val = priv->chan_out_states[chan->channel]; + return IIO_VAL_INT; + } + + /* select ADC channel */ + iowrite8(chan->channel | (chan->channel << 4), ®->achan); + + /* trigger ADC sample capture by writing to the 8-bit + * Software Strobe Register and wait for completion + */ + iowrite8(0, ®->ssr_ad); + while (ioread8(®->cir_asr) & BIT(7)); + + *val = ioread16(®->ssr_ad); + return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + /* get ADC bipolar/unipolar configuration */ + adc_config = ioread8(®->acfg); + adbu = !(adc_config & BIT(2)); + + *val = -32768 * adbu; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* get ADC bipolar/unipolar and gain configuration */ + adc_config = ioread8(®->acfg); + adbu = !(adc_config & BIT(2)); + gain = adc_config & 0x3; + + *val = 5; + *val2 = 15 - adbu + gain; + return IIO_VAL_FRACTIONAL_LOG2; + } + + return -EINVAL; +} + +static int stx104_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long mask) +{ + struct stx104_iio *const priv = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_HARDWAREGAIN: + /* Only four gain states (x1, x2, x4, x8) */ + switch (val) { + case 1: + iowrite8(0, &priv->reg->acfg); + break; + case 2: + iowrite8(1, &priv->reg->acfg); + break; + case 4: + iowrite8(2, &priv->reg->acfg); + break; + case 8: + iowrite8(3, &priv->reg->acfg); + break; + default: + return -EINVAL; + } + + return 0; + case IIO_CHAN_INFO_RAW: + if (chan->output) { + /* DAC can only accept up to a 16-bit value */ + if ((unsigned int)val > 65535) + return -EINVAL; + + priv->chan_out_states[chan->channel] = val; + iowrite16(val, &priv->reg->dac[chan->channel]); + + return 0; + } + return -EINVAL; + } + + return -EINVAL; +} + +static const struct iio_info stx104_info = { + .read_raw = stx104_read_raw, + .write_raw = stx104_write_raw +}; + +/* single-ended input channels configuration */ +static const struct iio_chan_spec stx104_channels_sing[] = { + STX104_OUT_CHAN(0), STX104_OUT_CHAN(1), + STX104_IN_CHAN(0, 0), STX104_IN_CHAN(1, 0), STX104_IN_CHAN(2, 0), + STX104_IN_CHAN(3, 0), STX104_IN_CHAN(4, 0), STX104_IN_CHAN(5, 0), + STX104_IN_CHAN(6, 0), STX104_IN_CHAN(7, 0), STX104_IN_CHAN(8, 0), + STX104_IN_CHAN(9, 0), STX104_IN_CHAN(10, 0), STX104_IN_CHAN(11, 0), + STX104_IN_CHAN(12, 0), STX104_IN_CHAN(13, 0), STX104_IN_CHAN(14, 0), + STX104_IN_CHAN(15, 0) +}; +/* differential input channels configuration */ +static const struct iio_chan_spec stx104_channels_diff[] = { + STX104_OUT_CHAN(0), STX104_OUT_CHAN(1), + STX104_IN_CHAN(0, 1), STX104_IN_CHAN(1, 1), STX104_IN_CHAN(2, 1), + STX104_IN_CHAN(3, 1), STX104_IN_CHAN(4, 1), STX104_IN_CHAN(5, 1), + STX104_IN_CHAN(6, 1), STX104_IN_CHAN(7, 1) +}; + +static int stx104_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + /* GPIO 0-3 are input only, while the rest are output only */ + if (offset < 4) + return 1; + + return 0; +} + +static int stx104_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + if (offset >= 4) + return -EINVAL; + + return 0; +} + +static int stx104_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + if (offset < 4) + return -EINVAL; + + chip->set(chip, offset, value); + return 0; +} + +static int stx104_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); + + if (offset >= 4) + return -EINVAL; + + return !!(ioread8(stx104gpio->base) & BIT(offset)); +} + +static int stx104_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) +{ + struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); + + *bits = ioread8(stx104gpio->base); + + return 0; +} + +static void stx104_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); + const unsigned int mask = BIT(offset) >> 4; + unsigned long flags; + + if (offset < 4) + return; + + spin_lock_irqsave(&stx104gpio->lock, flags); + + if (value) + stx104gpio->out_state |= mask; + else + stx104gpio->out_state &= ~mask; + + iowrite8(stx104gpio->out_state, stx104gpio->base); + + spin_unlock_irqrestore(&stx104gpio->lock, flags); +} + +#define STX104_NGPIO 8 +static const char *stx104_names[STX104_NGPIO] = { + "DIN0", "DIN1", "DIN2", "DIN3", "DOUT0", "DOUT1", "DOUT2", "DOUT3" +}; + +static void stx104_gpio_set_multiple(struct gpio_chip *chip, + unsigned long *mask, unsigned long *bits) +{ + struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); + unsigned long flags; + + /* verify masked GPIO are output */ + if (!(*mask & 0xF0)) + return; + + *mask >>= 4; + *bits >>= 4; + + spin_lock_irqsave(&stx104gpio->lock, flags); + + stx104gpio->out_state &= ~*mask; + stx104gpio->out_state |= *mask & *bits; + iowrite8(stx104gpio->out_state, stx104gpio->base); + + spin_unlock_irqrestore(&stx104gpio->lock, flags); +} + +static int stx104_probe(struct device *dev, unsigned int id) +{ + struct iio_dev *indio_dev; + struct stx104_iio *priv; + struct stx104_gpio *stx104gpio; + int err; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + stx104gpio = devm_kzalloc(dev, sizeof(*stx104gpio), GFP_KERNEL); + if (!stx104gpio) + return -ENOMEM; + + if (!devm_request_region(dev, base[id], STX104_EXTENT, + dev_name(dev))) { + dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", + base[id], base[id] + STX104_EXTENT); + return -EBUSY; + } + + priv = iio_priv(indio_dev); + priv->reg = devm_ioport_map(dev, base[id], STX104_EXTENT); + if (!priv->reg) + return -ENOMEM; + + indio_dev->info = &stx104_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + /* determine if differential inputs */ + if (ioread8(&priv->reg->cir_asr) & BIT(5)) { + indio_dev->num_channels = ARRAY_SIZE(stx104_channels_diff); + indio_dev->channels = stx104_channels_diff; + } else { + indio_dev->num_channels = ARRAY_SIZE(stx104_channels_sing); + indio_dev->channels = stx104_channels_sing; + } + + indio_dev->name = dev_name(dev); + + /* configure device for software trigger operation */ + iowrite8(0, &priv->reg->acr); + + /* initialize gain setting to x1 */ + iowrite8(0, &priv->reg->acfg); + + /* initialize DAC output to 0V */ + iowrite16(0, &priv->reg->dac[0]); + iowrite16(0, &priv->reg->dac[1]); + + stx104gpio->chip.label = dev_name(dev); + stx104gpio->chip.parent = dev; + stx104gpio->chip.owner = THIS_MODULE; + stx104gpio->chip.base = -1; + stx104gpio->chip.ngpio = STX104_NGPIO; + stx104gpio->chip.names = stx104_names; + stx104gpio->chip.get_direction = stx104_gpio_get_direction; + stx104gpio->chip.direction_input = stx104_gpio_direction_input; + stx104gpio->chip.direction_output = stx104_gpio_direction_output; + stx104gpio->chip.get = stx104_gpio_get; + stx104gpio->chip.get_multiple = stx104_gpio_get_multiple; + stx104gpio->chip.set = stx104_gpio_set; + stx104gpio->chip.set_multiple = stx104_gpio_set_multiple; + stx104gpio->base = &priv->reg->dio; + stx104gpio->out_state = 0x0; + + spin_lock_init(&stx104gpio->lock); + + err = devm_gpiochip_add_data(dev, &stx104gpio->chip, stx104gpio); + if (err) { + dev_err(dev, "GPIO registering failed (%d)\n", err); + return err; + } + + return devm_iio_device_register(dev, indio_dev); +} + +static struct isa_driver stx104_driver = { + .probe = stx104_probe, + .driver = { + .name = "stx104" + }, +}; + +module_isa_driver(stx104_driver, num_stx104); + +MODULE_AUTHOR("William Breathitt Gray "); +MODULE_DESCRIPTION("Apex Embedded Systems STX104 IIO driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 682ca76bc60ab86824cca1b34bc865bd0094ac7e Mon Sep 17 00:00:00 2001 From: Joe Simmons-Talbott Date: Fri, 19 Aug 2022 14:20:12 -0400 Subject: iio: Avoid multiple line dereference for mask Prefer lines > 80 characters over splitting dereferences across multiple lines. Reported by checkpatch.pl. Signed-off-by: Joe Simmons-Talbott Link: https://lore.kernel.org/r/20220819182012.219523-1-joetalbott@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 67d3d01d2dac..d38623c046cc 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1303,8 +1303,7 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, ret = iio_device_add_info_mask_type_avail(indio_dev, chan, IIO_SEPARATE, - &chan-> - info_mask_separate_available); + &chan->info_mask_separate_available); if (ret < 0) return ret; attrcount += ret; @@ -1318,8 +1317,7 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, ret = iio_device_add_info_mask_type_avail(indio_dev, chan, IIO_SHARED_BY_TYPE, - &chan-> - info_mask_shared_by_type_available); + &chan->info_mask_shared_by_type_available); if (ret < 0) return ret; attrcount += ret; -- cgit v1.2.3 From 8c6989e5463a2d9415b743a20e3b843a2354beec Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 14 Jul 2022 18:59:25 -0700 Subject: coresight: trbe: fix Kconfig "its" grammar Use the possessive "its" instead of the contraction "it's" where appropriate. Signed-off-by: Randy Dunlap Cc: Anshuman Khandual Cc: Mathieu Poirier Cc: Suzuki K Poulose Cc: Alexander Shishkin Cc: coresight@lists.linaro.org Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20220715015925.12569-1-rdunlap@infradead.org Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index 514a9b8086e3..45c1eb5dfcb7 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -193,10 +193,10 @@ config CORESIGHT_TRBE depends on ARM64 && CORESIGHT_SOURCE_ETM4X help This driver provides support for percpu Trace Buffer Extension (TRBE). - TRBE always needs to be used along with it's corresponding percpu ETE + TRBE always needs to be used along with its corresponding percpu ETE component. ETE generates trace data which is then captured with TRBE. Unlike traditional sink devices, TRBE is a CPU feature accessible via - system registers. But it's explicit dependency with trace unit (ETE) + system registers. But its explicit dependency with trace unit (ETE) requires it to be plugged in as a coresight sink device. To compile this driver as a module, choose M here: the module will be -- cgit v1.2.3 From b99ee26a1a98a8ac0d8241224c40e6c047091d4d Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 31 Jul 2022 09:06:48 +0200 Subject: coresight: docs: Fix a broken reference Since the commit in Fixes: tag, "coresight-cpu-debug.txt" has been turned into "arm,coresight-cpu-debug.yaml". Update the doc accordingly to avoid a 'make htmldocs' warning Fixes: 66d052047ca8 ("dt-bindings: arm: Convert CoreSight CPU debug to DT schema") Signed-off-by: Christophe JAILLET Reviewed-by: James Clark Link: https://lore.kernel.org/r/c7f864854e9e03916017712017ff59132c51c338.1659251193.git.christophe.jaillet@wanadoo.fr Signed-off-by: Mathieu Poirier --- Documentation/trace/coresight/coresight-cpu-debug.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/trace/coresight/coresight-cpu-debug.rst b/Documentation/trace/coresight/coresight-cpu-debug.rst index 993dd294b81b..836b35532667 100644 --- a/Documentation/trace/coresight/coresight-cpu-debug.rst +++ b/Documentation/trace/coresight/coresight-cpu-debug.rst @@ -117,7 +117,8 @@ divide into below cases: Device Tree Bindings -------------------- -See Documentation/devicetree/bindings/arm/coresight-cpu-debug.txt for details. +See Documentation/devicetree/bindings/arm/arm,coresight-cpu-debug.yaml for +details. How to use the module -- cgit v1.2.3 From 8559e62ccb4b0b77eb33496d9984ce05f0b756bb Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 21 Jul 2022 15:27:14 -0600 Subject: dt-bindings: arm: coresight: Add 'power-domains' property Coresight components may be in a power domain which is the case for the Arm Juno board. Allow a single 'power-domains' entry for Coresight components. Signed-off-by: Rob Herring Reviewed-by: Sudeep Holla Link: https://lore.kernel.org/r/20220721212718.1980905-1-robh@kernel.org Signed-off-by: Mathieu Poirier --- Documentation/devicetree/bindings/arm/arm,coresight-catu.yaml | 3 +++ Documentation/devicetree/bindings/arm/arm,coresight-cti.yaml | 3 +++ .../devicetree/bindings/arm/arm,coresight-dynamic-funnel.yaml | 3 +++ .../devicetree/bindings/arm/arm,coresight-dynamic-replicator.yaml | 3 +++ Documentation/devicetree/bindings/arm/arm,coresight-etb10.yaml | 3 +++ Documentation/devicetree/bindings/arm/arm,coresight-etm.yaml | 3 +++ Documentation/devicetree/bindings/arm/arm,coresight-static-funnel.yaml | 3 +++ .../devicetree/bindings/arm/arm,coresight-static-replicator.yaml | 3 +++ Documentation/devicetree/bindings/arm/arm,coresight-stm.yaml | 3 +++ Documentation/devicetree/bindings/arm/arm,coresight-tmc.yaml | 3 +++ Documentation/devicetree/bindings/arm/arm,coresight-tpiu.yaml | 3 +++ .../devicetree/bindings/arm/arm,embedded-trace-extension.yaml | 3 +++ 12 files changed, 36 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-catu.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-catu.yaml index d783d9276124..2bae06eed693 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-catu.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-catu.yaml @@ -61,6 +61,9 @@ properties: maxItems: 1 description: Address translation error interrupt + power-domains: + maxItems: 1 + in-ports: $ref: /schemas/graph.yaml#/properties/ports additionalProperties: false diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-cti.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-cti.yaml index 72ffe4d1e948..0c5b875cb654 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-cti.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-cti.yaml @@ -98,6 +98,9 @@ properties: base cti node if compatible string arm,coresight-cti-v8-arch is used, or may appear in a trig-conns child node when appropriate. + power-domains: + maxItems: 1 + arm,cti-ctm-id: $ref: /schemas/types.yaml#/definitions/uint32 description: diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-dynamic-funnel.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-dynamic-funnel.yaml index 1eeedc22857c..44a1041cb0fc 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-dynamic-funnel.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-dynamic-funnel.yaml @@ -54,6 +54,9 @@ properties: - const: apb_pclk - const: atclk + power-domains: + maxItems: 1 + in-ports: $ref: /schemas/graph.yaml#/properties/ports diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-dynamic-replicator.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-dynamic-replicator.yaml index a26ed9214e00..03792e9bd97a 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-dynamic-replicator.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-dynamic-replicator.yaml @@ -54,6 +54,9 @@ properties: - const: apb_pclk - const: atclk + power-domains: + maxItems: 1 + qcom,replicator-loses-context: type: boolean description: diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-etb10.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-etb10.yaml index fd06ede26ceb..90679788e0bf 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-etb10.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-etb10.yaml @@ -54,6 +54,9 @@ properties: - const: apb_pclk - const: atclk + power-domains: + maxItems: 1 + in-ports: $ref: /schemas/graph.yaml#/properties/ports additionalProperties: false diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-etm.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-etm.yaml index e0377ce48537..01200f67504a 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-etm.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-etm.yaml @@ -73,6 +73,9 @@ properties: - const: apb_pclk - const: atclk + power-domains: + maxItems: 1 + arm,coresight-loses-context-with-cpu: type: boolean description: diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-static-funnel.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-static-funnel.yaml index 374083956b20..cc8c3baa79b4 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-static-funnel.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-static-funnel.yaml @@ -27,6 +27,9 @@ properties: compatible: const: arm,coresight-static-funnel + power-domains: + maxItems: 1 + in-ports: $ref: /schemas/graph.yaml#/properties/ports diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-static-replicator.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-static-replicator.yaml index a34d8583830c..1892a091ac35 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-static-replicator.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-static-replicator.yaml @@ -27,6 +27,9 @@ properties: compatible: const: arm,coresight-static-replicator + power-domains: + maxItems: 1 + in-ports: $ref: /schemas/graph.yaml#/properties/ports additionalProperties: false diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-stm.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-stm.yaml index 905008faa012..378380c3f5aa 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-stm.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-stm.yaml @@ -61,6 +61,9 @@ properties: - const: apb_pclk - const: atclk + power-domains: + maxItems: 1 + out-ports: $ref: /schemas/graph.yaml#/properties/ports additionalProperties: false diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-tmc.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-tmc.yaml index 3463b6e53aef..e0b88a71356a 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-tmc.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-tmc.yaml @@ -55,6 +55,9 @@ properties: - const: apb_pclk - const: atclk + power-domains: + maxItems: 1 + arm,buffer-size: $ref: /schemas/types.yaml#/definitions/uint32 deprecated: true diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-tpiu.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-tpiu.yaml index e80d48200c37..61a0cdc27745 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-tpiu.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-tpiu.yaml @@ -54,6 +54,9 @@ properties: - const: apb_pclk - const: atclk + power-domains: + maxItems: 1 + in-ports: $ref: /schemas/graph.yaml#/properties/ports additionalProperties: false diff --git a/Documentation/devicetree/bindings/arm/arm,embedded-trace-extension.yaml b/Documentation/devicetree/bindings/arm/arm,embedded-trace-extension.yaml index 5f07fb166c56..108460627d9a 100644 --- a/Documentation/devicetree/bindings/arm/arm,embedded-trace-extension.yaml +++ b/Documentation/devicetree/bindings/arm/arm,embedded-trace-extension.yaml @@ -33,6 +33,9 @@ properties: Handle to the cpu this ETE is bound to. $ref: /schemas/types.yaml#/definitions/phandle + power-domains: + maxItems: 1 + out-ports: description: | Output connections from the ETE to legacy CoreSight trace bus. -- cgit v1.2.3 From 84fa8f159022aab8e93667b56672238b10cdbb9b Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 21 Jul 2022 15:27:15 -0600 Subject: dt-bindings: arm: coresight-tmc: Add 'iommu' property The Coresight TMC component may be behind an IOMMU which is the case for the Arm Juno SoC and some Qualcomm SoCs. Add 'iommus' property to the binding. Signed-off-by: Rob Herring Link: https://lore.kernel.org/r/20220721212718.1980905-2-robh@kernel.org Signed-off-by: Mathieu Poirier --- Documentation/devicetree/bindings/arm/arm,coresight-tmc.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-tmc.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-tmc.yaml index e0b88a71356a..cb8dceaca70e 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-tmc.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-tmc.yaml @@ -55,6 +55,9 @@ properties: - const: apb_pclk - const: atclk + iommus: + maxItems: 1 + power-domains: maxItems: 1 -- cgit v1.2.3 From 0f00b223ea22f9e32a82a6ffb5a4de0753c99b55 Mon Sep 17 00:00:00 2001 From: German Gomez Date: Tue, 23 Aug 2022 17:06:49 +0100 Subject: coresight: etm4x: Expose default timestamp source in sysfs Add a new sysfs interface in /sys/bus/coresight/devices/etm/ts_source indicating the configured timestamp source when the ETM device driver was probed. The perf tool will use this information to detect if the trace data timestamp matches the kernel time, enabling correlation of CoreSight trace with perf events. Suggested-by: Suzuki K Poulose Signed-off-by: German Gomez Reviewed-by: Leo Yan Signed-off-by: James Clark Link: https://lore.kernel.org/r/20220823160650.455823-2-james.clark@arm.com Signed-off-by: Mathieu Poirier --- arch/arm64/include/asm/sysreg.h | 1 + .../hwtracing/coresight/coresight-etm4x-sysfs.c | 29 ++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index 7c71358d44c4..7a518a011669 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h @@ -1021,6 +1021,7 @@ #define SYS_MPIDR_SAFE_VAL (BIT(31)) #define TRFCR_ELx_TS_SHIFT 5 +#define TRFCR_ELx_TS_MASK ((0x3UL) << TRFCR_ELx_TS_SHIFT) #define TRFCR_ELx_TS_VIRTUAL ((0x1UL) << TRFCR_ELx_TS_SHIFT) #define TRFCR_ELx_TS_GUEST_PHYSICAL ((0x2UL) << TRFCR_ELx_TS_SHIFT) #define TRFCR_ELx_TS_PHYSICAL ((0x3UL) << TRFCR_ELx_TS_SHIFT) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index 6ea8181816fc..9cac848cffaf 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -2306,6 +2306,34 @@ static ssize_t cpu_show(struct device *dev, } static DEVICE_ATTR_RO(cpu); +static ssize_t ts_source_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (!drvdata->trfcr) { + val = -1; + goto out; + } + + switch (drvdata->trfcr & TRFCR_ELx_TS_MASK) { + case TRFCR_ELx_TS_VIRTUAL: + case TRFCR_ELx_TS_GUEST_PHYSICAL: + case TRFCR_ELx_TS_PHYSICAL: + val = FIELD_GET(TRFCR_ELx_TS_MASK, drvdata->trfcr); + break; + default: + val = -1; + break; + } + +out: + return sysfs_emit(buf, "%d\n", val); +} +static DEVICE_ATTR_RO(ts_source); + static struct attribute *coresight_etmv4_attrs[] = { &dev_attr_nr_pe_cmp.attr, &dev_attr_nr_addr_cmp.attr, @@ -2360,6 +2388,7 @@ static struct attribute *coresight_etmv4_attrs[] = { &dev_attr_vmid_val.attr, &dev_attr_vmid_masks.attr, &dev_attr_cpu.attr, + &dev_attr_ts_source.attr, NULL, }; -- cgit v1.2.3 From 04d1edb0ecf297eba4fe946b3b715cff4f8a6656 Mon Sep 17 00:00:00 2001 From: German Gomez Date: Tue, 23 Aug 2022 17:06:50 +0100 Subject: coresight: etm4x: docs: Add documentation for 'ts_source' sysfs interface Sync sysfs documentation pages to include the new ts_source (timestamp source) interface. Signed-off-by: German Gomez Signed-off-by: James Clark Link: https://lore.kernel.org/r/20220823160650.455823-3-james.clark@arm.com Signed-off-by: Mathieu Poirier --- .../ABI/testing/sysfs-bus-coresight-devices-etm4x | 8 ++++++++ .../trace/coresight/coresight-etm4x-reference.rst | 14 ++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm4x b/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm4x index 8e53a32f8150..08b1964f27d3 100644 --- a/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm4x +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm4x @@ -516,3 +516,11 @@ Contact: Mathieu Poirier Description: (Read) Returns the number of special conditional P1 right-hand keys that the trace unit can use (0x194). The value is taken directly from the HW. + +What: /sys/bus/coresight/devices/etm/ts_source +Date: October 2022 +KernelVersion: 6.1 +Contact: Mathieu Poirier or Suzuki K Poulose +Description: (Read) When FEAT_TRF is implemented, value of TRFCR_ELx.TS used for + trace session. Otherwise -1 indicates an unknown time source. Check + trcidr0.tssize to see if a global timestamp is available. diff --git a/Documentation/trace/coresight/coresight-etm4x-reference.rst b/Documentation/trace/coresight/coresight-etm4x-reference.rst index fb7578fd9372..70e34b8c81c1 100644 --- a/Documentation/trace/coresight/coresight-etm4x-reference.rst +++ b/Documentation/trace/coresight/coresight-etm4x-reference.rst @@ -71,6 +71,20 @@ the ‘TRC’ prefix. ---- +:File: ``ts_source`` (ro) +:Trace Registers: None. +:Notes: + When FEAT_TRF is implemented, value of TRFCR_ELx.TS used for trace session. Otherwise -1 + indicates an unknown time source. Check trcidr0.tssize to see if a global timestamp is + available. + +:Example: + ``$> cat ts_source`` + + ``$> 1`` + +---- + :File: ``addr_idx`` (rw) :Trace Registers: None. :Notes: -- cgit v1.2.3 From d2a4cbcb8bdc0e3d1cf85bf47a670695da0bc27a Mon Sep 17 00:00:00 2001 From: Dmitry Rokosov Date: Fri, 12 Aug 2022 16:52:26 +0000 Subject: units: complement the set of Hz units Currently, Hz units do not have milli, micro and nano Hz coefficients. Some drivers (IIO especially) use their analogues to calculate appropriate Hz values. This patch includes them to units.h definitions, so they can be used from different kernel places. Signed-off-by: Dmitry Rokosov Link: https://lore.kernel.org/r/20220812165243.22177-3-ddrokosov@sberdevices.ru Signed-off-by: Jonathan Cameron --- include/linux/units.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/units.h b/include/linux/units.h index 681fc652e3d7..2793a41e73a2 100644 --- a/include/linux/units.h +++ b/include/linux/units.h @@ -20,6 +20,9 @@ #define PICO 1000000000000ULL #define FEMTO 1000000000000000ULL +#define NANOHZ_PER_HZ 1000000000UL +#define MICROHZ_PER_HZ 1000000UL +#define MILLIHZ_PER_HZ 1000UL #define HZ_PER_KHZ 1000UL #define KHZ_PER_MHZ 1000UL #define HZ_PER_MHZ 1000000UL -- cgit v1.2.3 From fe49ce7abd6af8c55e58a8b33a7978c9da32ffb2 Mon Sep 17 00:00:00 2001 From: Dmitry Rokosov Date: Fri, 12 Aug 2022 16:52:27 +0000 Subject: iio: accel: adxl345: use HZ macro from units.h Remove duplicated definition of NHZ_PER_HZ, because it's available in the units.h as NANOHZ_PER_HZ. Signed-off-by: Dmitry Rokosov Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220812165243.22177-4-ddrokosov@sberdevices.ru Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl345_core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c index 370bfec1275a..1919e0089c11 100644 --- a/drivers/iio/accel/adxl345_core.c +++ b/drivers/iio/accel/adxl345_core.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -32,7 +33,6 @@ #define ADXL345_BW_RATE GENMASK(3, 0) #define ADXL345_BASE_RATE_NANO_HZ 97656250LL -#define NHZ_PER_HZ 1000000000LL #define ADXL345_POWER_CTL_MEASURE BIT(3) #define ADXL345_POWER_CTL_STANDBY 0x00 @@ -139,7 +139,7 @@ static int adxl345_read_raw(struct iio_dev *indio_dev, samp_freq_nhz = ADXL345_BASE_RATE_NANO_HZ << (regval & ADXL345_BW_RATE); - *val = div_s64_rem(samp_freq_nhz, NHZ_PER_HZ, val2); + *val = div_s64_rem(samp_freq_nhz, NANOHZ_PER_HZ, val2); return IIO_VAL_INT_PLUS_NANO; } @@ -164,7 +164,8 @@ static int adxl345_write_raw(struct iio_dev *indio_dev, ADXL345_REG_OFS_AXIS(chan->address), val / 4); case IIO_CHAN_INFO_SAMP_FREQ: - n = div_s64(val * NHZ_PER_HZ + val2, ADXL345_BASE_RATE_NANO_HZ); + n = div_s64(val * NANOHZ_PER_HZ + val2, + ADXL345_BASE_RATE_NANO_HZ); return regmap_update_bits(data->regmap, ADXL345_REG_BW_RATE, ADXL345_BW_RATE, -- cgit v1.2.3 From c05c3e5d4e563ac84624c093addee644194984eb Mon Sep 17 00:00:00 2001 From: Dmitry Rokosov Date: Fri, 12 Aug 2022 16:52:27 +0000 Subject: iio: common: scmi_sensors: use HZ macro from units.h Remove duplicated definition of UHZ_PER_HZ, because it's available in the units.h as MICROHZ_PER_HZ. Signed-off-by: Dmitry Rokosov Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220812165243.22177-5-ddrokosov@sberdevices.ru Signed-off-by: Jonathan Cameron --- drivers/iio/common/scmi_sensors/scmi_iio.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/common/scmi_sensors/scmi_iio.c b/drivers/iio/common/scmi_sensors/scmi_iio.c index 793d628db55f..54ccf19ab2bb 100644 --- a/drivers/iio/common/scmi_sensors/scmi_iio.c +++ b/drivers/iio/common/scmi_sensors/scmi_iio.c @@ -18,6 +18,7 @@ #include #include #include +#include #define SCMI_IIO_NUM_OF_AXIS 3 @@ -130,7 +131,6 @@ static const struct iio_buffer_setup_ops scmi_iio_buffer_ops = { static int scmi_iio_set_odr_val(struct iio_dev *iio_dev, int val, int val2) { struct scmi_iio_priv *sensor = iio_priv(iio_dev); - const unsigned long UHZ_PER_HZ = 1000000UL; u64 sec, mult, uHz, sf; u32 sensor_config; char buf[32]; @@ -145,7 +145,7 @@ static int scmi_iio_set_odr_val(struct iio_dev *iio_dev, int val, int val2) return err; } - uHz = val * UHZ_PER_HZ + val2; + uHz = val * MICROHZ_PER_HZ + val2; /* * The seconds field in the sensor interval in SCMI is 16 bits long @@ -156,10 +156,10 @@ static int scmi_iio_set_odr_val(struct iio_dev *iio_dev, int val, int val2) * count the number of characters */ sf = (u64)uHz * 0xFFFF; - do_div(sf, UHZ_PER_HZ); + do_div(sf, MICROHZ_PER_HZ); mult = scnprintf(buf, sizeof(buf), "%llu", sf) - 1; - sec = int_pow(10, mult) * UHZ_PER_HZ; + sec = int_pow(10, mult) * MICROHZ_PER_HZ; do_div(sec, uHz); if (sec == 0) { dev_err(&iio_dev->dev, -- cgit v1.2.3 From 1f5d7ea73c4b630dbb2c90818cb9fc0be54d2fe3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 22 Aug 2022 17:49:23 +0000 Subject: lib/string_helpers: Add str_read_write() helper Add str_read_write() helper to return 'read' or 'write' string literal. Signed-off-by: Andy Shevchenko Signed-off-by: Dmitry Rokosov Link: https://lore.kernel.org/r/20220822175011.2886-2-ddrokosov@sberdevices.ru Signed-off-by: Jonathan Cameron --- include/linux/string_helpers.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/linux/string_helpers.h b/include/linux/string_helpers.h index 4d72258d42fd..9e22cd78f3b8 100644 --- a/include/linux/string_helpers.h +++ b/include/linux/string_helpers.h @@ -126,4 +126,9 @@ static inline const char *str_enabled_disabled(bool v) return v ? "enabled" : "disabled"; } +static inline const char *str_read_write(bool v) +{ + return v ? "read" : "write"; +} + #endif -- cgit v1.2.3 From 4905949395850e41912ae89e2d2fa88d2cd36319 Mon Sep 17 00:00:00 2001 From: Dmitry Rokosov Date: Mon, 22 Aug 2022 17:49:24 +0000 Subject: dt-bindings: vendor-prefixes: add MEMSensing Microsystems Co., Ltd. MEMSensing Microsystems (Suzhou, China) Co., Ltd. operates as a micro electromechanical system technology company which produces micro electromechanical system microphones and sensors. MEMSensing Microsystems (Suzhou, China) Co., Ltd. applies its products in consumer electronics, industrial control, medical electronics and automotive, and other fields. Signed-off-by: Dmitry Rokosov Acked-by: Rob Herring Link: https://lore.kernel.org/r/20220822175011.2886-3-ddrokosov@sberdevices.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 2f0151e9f6be..b1818b4eb972 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -777,6 +777,8 @@ patternProperties: description: MELFAS Inc. "^mellanox,.*": description: Mellanox Technologies + "^memsensing,.*": + description: MEMSensing Microsystems Co., Ltd. "^memsic,.*": description: MEMSIC Inc. "^menlo,.*": -- cgit v1.2.3 From 1ca2cfbc0c337ed3a3c817f84f9fcf0d32203f73 Mon Sep 17 00:00:00 2001 From: Dmitry Rokosov Date: Mon, 22 Aug 2022 17:49:25 +0000 Subject: iio: add MEMSensing MSA311 3-axis accelerometer driver MSA311 is a tri-axial, low-g accelerometer with I2C digital output for sensitivity consumer applications. It has dynamic user-selectable full scales range of +-2g/+-4g/+-8g/+-16g and allows acceleration measurements with output data rates from 1Hz to 1000Hz. This driver supports following MSA311 features: - IIO interface - Different power modes: NORMAL and SUSPEND (using pm_runtime) - ODR (Output Data Rate) selection - Scale and samp_freq selection - IIO triggered buffer, IIO reg access - NEW_DATA interrupt + trigger Below features to be done: - Motion Events: ACTIVE, TAP, ORIENT, FREEFALL - Low Power mode Datasheet: https://cdn-shop.adafruit.com/product-files/5309/MSA311-V1.1-ENG.pdf Signed-off-by: Dmitry Rokosov Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220822175011.2886-4-ddrokosov@sberdevices.ru Signed-off-by: Jonathan Cameron --- MAINTAINERS | 6 + drivers/iio/accel/Kconfig | 13 + drivers/iio/accel/Makefile | 2 + drivers/iio/accel/msa311.c | 1321 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1342 insertions(+) create mode 100644 drivers/iio/accel/msa311.c diff --git a/MAINTAINERS b/MAINTAINERS index 7dcae9243256..b8a621941db9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13176,6 +13176,12 @@ F: drivers/mtd/ F: include/linux/mtd/ F: include/uapi/mtd/ +MEMSENSING MICROSYSTEMS MSA311 DRIVER +M: Dmitry Rokosov +L: linux-iio@vger.kernel.org +S: Maintained +F: drivers/iio/accel/msa311.c + MEN A21 WATCHDOG DRIVER M: Johannes Thumshirn L: linux-watchdog@vger.kernel.org diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 35798712f811..ffac66db7ac9 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -541,6 +541,19 @@ config MMA9553 To compile this driver as a module, choose M here: the module will be called mma9553. +config MSA311 + tristate "MEMSensing Digital 3-Axis Accelerometer Driver" + depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select REGMAP_I2C + help + Say yes here to build support for the MEMSensing MSA311 + accelerometer driver. + + To compile this driver as a module, choose M here: the module will be + called msa311. + config MXC4005 tristate "Memsic MXC4005XC 3-Axis Accelerometer Driver" depends on I2C diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 4d8792668838..5e45b5fa5ab5 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -58,6 +58,8 @@ obj-$(CONFIG_MMA9551_CORE) += mma9551_core.o obj-$(CONFIG_MMA9551) += mma9551.o obj-$(CONFIG_MMA9553) += mma9553.o +obj-$(CONFIG_MSA311) += msa311.o + obj-$(CONFIG_MXC4005) += mxc4005.o obj-$(CONFIG_MXC6255) += mxc6255.o diff --git a/drivers/iio/accel/msa311.c b/drivers/iio/accel/msa311.c new file mode 100644 index 000000000000..2fded3759171 --- /dev/null +++ b/drivers/iio/accel/msa311.c @@ -0,0 +1,1321 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MEMSensing digital 3-Axis accelerometer + * + * MSA311 is a tri-axial, low-g accelerometer with I2C digital output for + * sensitivity consumer applications. It has dynamic user-selectable full + * scales range of +-2g/+-4g/+-8g/+-16g and allows acceleration measurements + * with output data rates from 1Hz to 1000Hz. + * + * MSA311 is available in an ultra small (2mm x 2mm, height 0.95mm) LGA package + * and is guaranteed to operate over -40C to +85C. + * + * This driver supports following MSA311 features: + * - IIO interface + * - Different power modes: NORMAL, SUSPEND + * - ODR (Output Data Rate) selection + * - Scale selection + * - IIO triggered buffer + * - NEW_DATA interrupt + trigger + * + * Below features to be done: + * - Motion Events: ACTIVE, TAP, ORIENT, FREEFALL + * - Low Power mode + * + * Copyright (c) 2022, SberDevices. All Rights Reserved. + * + * Author: Dmitry Rokosov + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define MSA311_SOFT_RESET_REG 0x00 +#define MSA311_PARTID_REG 0x01 +#define MSA311_ACC_X_REG 0x02 +#define MSA311_ACC_Y_REG 0x04 +#define MSA311_ACC_Z_REG 0x06 +#define MSA311_MOTION_INT_REG 0x09 +#define MSA311_DATA_INT_REG 0x0A +#define MSA311_TAP_ACTIVE_STS_REG 0x0B +#define MSA311_ORIENT_STS_REG 0x0C +#define MSA311_RANGE_REG 0x0F +#define MSA311_ODR_REG 0x10 +#define MSA311_PWR_MODE_REG 0x11 +#define MSA311_SWAP_POLARITY_REG 0x12 +#define MSA311_INT_SET_0_REG 0x16 +#define MSA311_INT_SET_1_REG 0x17 +#define MSA311_INT_MAP_0_REG 0x19 +#define MSA311_INT_MAP_1_REG 0x1A +#define MSA311_INT_CONFIG_REG 0x20 +#define MSA311_INT_LATCH_REG 0x21 +#define MSA311_FREEFALL_DUR_REG 0x22 +#define MSA311_FREEFALL_TH_REG 0x23 +#define MSA311_FREEFALL_HY_REG 0x24 +#define MSA311_ACTIVE_DUR_REG 0x27 +#define MSA311_ACTIVE_TH_REG 0x28 +#define MSA311_TAP_DUR_REG 0x2A +#define MSA311_TAP_TH_REG 0x2B +#define MSA311_ORIENT_HY_REG 0x2C +#define MSA311_Z_BLOCK_REG 0x2D +#define MSA311_OFFSET_X_REG 0x38 +#define MSA311_OFFSET_Y_REG 0x39 +#define MSA311_OFFSET_Z_REG 0x3A + +enum msa311_fields { + /* Soft_Reset */ + F_SOFT_RESET_I2C, F_SOFT_RESET_SPI, + /* Motion_Interrupt */ + F_ORIENT_INT, F_S_TAP_INT, F_D_TAP_INT, F_ACTIVE_INT, F_FREEFALL_INT, + /* Data_Interrupt */ + F_NEW_DATA_INT, + /* Tap_Active_Status */ + F_TAP_SIGN, F_TAP_FIRST_X, F_TAP_FIRST_Y, F_TAP_FIRST_Z, F_ACTV_SIGN, + F_ACTV_FIRST_X, F_ACTV_FIRST_Y, F_ACTV_FIRST_Z, + /* Orientation_Status */ + F_ORIENT_Z, F_ORIENT_X_Y, + /* Range */ + F_FS, + /* ODR */ + F_X_AXIS_DIS, F_Y_AXIS_DIS, F_Z_AXIS_DIS, F_ODR, + /* Power Mode/Bandwidth */ + F_PWR_MODE, F_LOW_POWER_BW, + /* Swap_Polarity */ + F_X_POLARITY, F_Y_POLARITY, F_Z_POLARITY, F_X_Y_SWAP, + /* Int_Set_0 */ + F_ORIENT_INT_EN, F_S_TAP_INT_EN, F_D_TAP_INT_EN, F_ACTIVE_INT_EN_Z, + F_ACTIVE_INT_EN_Y, F_ACTIVE_INT_EN_X, + /* Int_Set_1 */ + F_NEW_DATA_INT_EN, F_FREEFALL_INT_EN, + /* Int_Map_0 */ + F_INT1_ORIENT, F_INT1_S_TAP, F_INT1_D_TAP, F_INT1_ACTIVE, + F_INT1_FREEFALL, + /* Int_Map_1 */ + F_INT1_NEW_DATA, + /* Int_Config */ + F_INT1_OD, F_INT1_LVL, + /* Int_Latch */ + F_RESET_INT, F_LATCH_INT, + /* Freefall_Hy */ + F_FREEFALL_MODE, F_FREEFALL_HY, + /* Active_Dur */ + F_ACTIVE_DUR, + /* Tap_Dur */ + F_TAP_QUIET, F_TAP_SHOCK, F_TAP_DUR, + /* Tap_Th */ + F_TAP_TH, + /* Orient_Hy */ + F_ORIENT_HYST, F_ORIENT_BLOCKING, F_ORIENT_MODE, + /* Z_Block */ + F_Z_BLOCKING, + /* End of register map */ + F_MAX_FIELDS, +}; + +static const struct reg_field msa311_reg_fields[] = { + /* Soft_Reset */ + [F_SOFT_RESET_I2C] = REG_FIELD(MSA311_SOFT_RESET_REG, 2, 2), + [F_SOFT_RESET_SPI] = REG_FIELD(MSA311_SOFT_RESET_REG, 5, 5), + /* Motion_Interrupt */ + [F_ORIENT_INT] = REG_FIELD(MSA311_MOTION_INT_REG, 6, 6), + [F_S_TAP_INT] = REG_FIELD(MSA311_MOTION_INT_REG, 5, 5), + [F_D_TAP_INT] = REG_FIELD(MSA311_MOTION_INT_REG, 4, 4), + [F_ACTIVE_INT] = REG_FIELD(MSA311_MOTION_INT_REG, 2, 2), + [F_FREEFALL_INT] = REG_FIELD(MSA311_MOTION_INT_REG, 0, 0), + /* Data_Interrupt */ + [F_NEW_DATA_INT] = REG_FIELD(MSA311_DATA_INT_REG, 0, 0), + /* Tap_Active_Status */ + [F_TAP_SIGN] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 7, 7), + [F_TAP_FIRST_X] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 6, 6), + [F_TAP_FIRST_Y] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 5, 5), + [F_TAP_FIRST_Z] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 4, 4), + [F_ACTV_SIGN] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 3, 3), + [F_ACTV_FIRST_X] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 2, 2), + [F_ACTV_FIRST_Y] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 1, 1), + [F_ACTV_FIRST_Z] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 0, 0), + /* Orientation_Status */ + [F_ORIENT_Z] = REG_FIELD(MSA311_ORIENT_STS_REG, 6, 6), + [F_ORIENT_X_Y] = REG_FIELD(MSA311_ORIENT_STS_REG, 4, 5), + /* Range */ + [F_FS] = REG_FIELD(MSA311_RANGE_REG, 0, 1), + /* ODR */ + [F_X_AXIS_DIS] = REG_FIELD(MSA311_ODR_REG, 7, 7), + [F_Y_AXIS_DIS] = REG_FIELD(MSA311_ODR_REG, 6, 6), + [F_Z_AXIS_DIS] = REG_FIELD(MSA311_ODR_REG, 5, 5), + [F_ODR] = REG_FIELD(MSA311_ODR_REG, 0, 3), + /* Power Mode/Bandwidth */ + [F_PWR_MODE] = REG_FIELD(MSA311_PWR_MODE_REG, 6, 7), + [F_LOW_POWER_BW] = REG_FIELD(MSA311_PWR_MODE_REG, 1, 4), + /* Swap_Polarity */ + [F_X_POLARITY] = REG_FIELD(MSA311_SWAP_POLARITY_REG, 3, 3), + [F_Y_POLARITY] = REG_FIELD(MSA311_SWAP_POLARITY_REG, 2, 2), + [F_Z_POLARITY] = REG_FIELD(MSA311_SWAP_POLARITY_REG, 1, 1), + [F_X_Y_SWAP] = REG_FIELD(MSA311_SWAP_POLARITY_REG, 0, 0), + /* Int_Set_0 */ + [F_ORIENT_INT_EN] = REG_FIELD(MSA311_INT_SET_0_REG, 6, 6), + [F_S_TAP_INT_EN] = REG_FIELD(MSA311_INT_SET_0_REG, 5, 5), + [F_D_TAP_INT_EN] = REG_FIELD(MSA311_INT_SET_0_REG, 4, 4), + [F_ACTIVE_INT_EN_Z] = REG_FIELD(MSA311_INT_SET_0_REG, 2, 2), + [F_ACTIVE_INT_EN_Y] = REG_FIELD(MSA311_INT_SET_0_REG, 1, 1), + [F_ACTIVE_INT_EN_X] = REG_FIELD(MSA311_INT_SET_0_REG, 0, 0), + /* Int_Set_1 */ + [F_NEW_DATA_INT_EN] = REG_FIELD(MSA311_INT_SET_1_REG, 4, 4), + [F_FREEFALL_INT_EN] = REG_FIELD(MSA311_INT_SET_1_REG, 3, 3), + /* Int_Map_0 */ + [F_INT1_ORIENT] = REG_FIELD(MSA311_INT_MAP_0_REG, 6, 6), + [F_INT1_S_TAP] = REG_FIELD(MSA311_INT_MAP_0_REG, 5, 5), + [F_INT1_D_TAP] = REG_FIELD(MSA311_INT_MAP_0_REG, 4, 4), + [F_INT1_ACTIVE] = REG_FIELD(MSA311_INT_MAP_0_REG, 2, 2), + [F_INT1_FREEFALL] = REG_FIELD(MSA311_INT_MAP_0_REG, 0, 0), + /* Int_Map_1 */ + [F_INT1_NEW_DATA] = REG_FIELD(MSA311_INT_MAP_1_REG, 0, 0), + /* Int_Config */ + [F_INT1_OD] = REG_FIELD(MSA311_INT_CONFIG_REG, 1, 1), + [F_INT1_LVL] = REG_FIELD(MSA311_INT_CONFIG_REG, 0, 0), + /* Int_Latch */ + [F_RESET_INT] = REG_FIELD(MSA311_INT_LATCH_REG, 7, 7), + [F_LATCH_INT] = REG_FIELD(MSA311_INT_LATCH_REG, 0, 3), + /* Freefall_Hy */ + [F_FREEFALL_MODE] = REG_FIELD(MSA311_FREEFALL_HY_REG, 2, 2), + [F_FREEFALL_HY] = REG_FIELD(MSA311_FREEFALL_HY_REG, 0, 1), + /* Active_Dur */ + [F_ACTIVE_DUR] = REG_FIELD(MSA311_ACTIVE_DUR_REG, 0, 1), + /* Tap_Dur */ + [F_TAP_QUIET] = REG_FIELD(MSA311_TAP_DUR_REG, 7, 7), + [F_TAP_SHOCK] = REG_FIELD(MSA311_TAP_DUR_REG, 6, 6), + [F_TAP_DUR] = REG_FIELD(MSA311_TAP_DUR_REG, 0, 2), + /* Tap_Th */ + [F_TAP_TH] = REG_FIELD(MSA311_TAP_TH_REG, 0, 4), + /* Orient_Hy */ + [F_ORIENT_HYST] = REG_FIELD(MSA311_ORIENT_HY_REG, 4, 6), + [F_ORIENT_BLOCKING] = REG_FIELD(MSA311_ORIENT_HY_REG, 2, 3), + [F_ORIENT_MODE] = REG_FIELD(MSA311_ORIENT_HY_REG, 0, 1), + /* Z_Block */ + [F_Z_BLOCKING] = REG_FIELD(MSA311_Z_BLOCK_REG, 0, 3), +}; + +#define MSA311_WHO_AM_I 0x13 + +/* + * Possible Full Scale ranges + * + * Axis data is 12-bit signed value, so + * + * fs0 = (2 + 2) * 9.81 / (2^11) = 0.009580 + * fs1 = (4 + 4) * 9.81 / (2^11) = 0.019160 + * fs2 = (8 + 8) * 9.81 / (2^11) = 0.038320 + * fs3 = (16 + 16) * 9.81 / (2^11) = 0.076641 + */ +enum { + MSA311_FS_2G, + MSA311_FS_4G, + MSA311_FS_8G, + MSA311_FS_16G, +}; + +struct iio_decimal_fract { + int integral; + int microfract; +}; + +static const struct iio_decimal_fract msa311_fs_table[] = { + {0, 9580}, {0, 19160}, {0, 38320}, {0, 76641}, +}; + +/* Possible Output Data Rate values */ +enum { + MSA311_ODR_1_HZ, + MSA311_ODR_1_95_HZ, + MSA311_ODR_3_9_HZ, + MSA311_ODR_7_81_HZ, + MSA311_ODR_15_63_HZ, + MSA311_ODR_31_25_HZ, + MSA311_ODR_62_5_HZ, + MSA311_ODR_125_HZ, + MSA311_ODR_250_HZ, + MSA311_ODR_500_HZ, + MSA311_ODR_1000_HZ, +}; + +static const struct iio_decimal_fract msa311_odr_table[] = { + {1, 0}, {1, 950000}, {3, 900000}, {7, 810000}, {15, 630000}, + {31, 250000}, {62, 500000}, {125, 0}, {250, 0}, {500, 0}, {1000, 0}, +}; + +/* All supported power modes */ +#define MSA311_PWR_MODE_NORMAL 0b00 +#define MSA311_PWR_MODE_LOW 0b01 +#define MSA311_PWR_MODE_UNKNOWN 0b10 +#define MSA311_PWR_MODE_SUSPEND 0b11 +static const char * const msa311_pwr_modes[] = { + [MSA311_PWR_MODE_NORMAL] = "normal", + [MSA311_PWR_MODE_LOW] = "low", + [MSA311_PWR_MODE_UNKNOWN] = "unknown", + [MSA311_PWR_MODE_SUSPEND] = "suspend", +}; + +/* Autosuspend delay */ +#define MSA311_PWR_SLEEP_DELAY_MS 2000 + +/* Possible INT1 types and levels */ +enum { + MSA311_INT1_OD_PUSH_PULL, + MSA311_INT1_OD_OPEN_DRAIN, +}; + +enum { + MSA311_INT1_LVL_LOW, + MSA311_INT1_LVL_HIGH, +}; + +/* Latch INT modes */ +#define MSA311_LATCH_INT_NOT_LATCHED 0b0000 +#define MSA311_LATCH_INT_250MS 0b0001 +#define MSA311_LATCH_INT_500MS 0b0010 +#define MSA311_LATCH_INT_1S 0b0011 +#define MSA311_LATCH_INT_2S 0b0100 +#define MSA311_LATCH_INT_4S 0b0101 +#define MSA311_LATCH_INT_8S 0b0110 +#define MSA311_LATCH_INT_1MS 0b1010 +#define MSA311_LATCH_INT_2MS 0b1011 +#define MSA311_LATCH_INT_25MS 0b1100 +#define MSA311_LATCH_INT_50MS 0b1101 +#define MSA311_LATCH_INT_100MS 0b1110 +#define MSA311_LATCH_INT_LATCHED 0b0111 + +static const struct regmap_range msa311_readonly_registers[] = { + regmap_reg_range(MSA311_PARTID_REG, MSA311_ORIENT_STS_REG), +}; + +static const struct regmap_access_table msa311_writeable_table = { + .no_ranges = msa311_readonly_registers, + .n_no_ranges = ARRAY_SIZE(msa311_readonly_registers), +}; + +static const struct regmap_range msa311_writeonly_registers[] = { + regmap_reg_range(MSA311_SOFT_RESET_REG, MSA311_SOFT_RESET_REG), +}; + +static const struct regmap_access_table msa311_readable_table = { + .no_ranges = msa311_writeonly_registers, + .n_no_ranges = ARRAY_SIZE(msa311_writeonly_registers), +}; + +static const struct regmap_range msa311_volatile_registers[] = { + regmap_reg_range(MSA311_ACC_X_REG, MSA311_ORIENT_STS_REG), +}; + +static const struct regmap_access_table msa311_volatile_table = { + .yes_ranges = msa311_volatile_registers, + .n_yes_ranges = ARRAY_SIZE(msa311_volatile_registers), +}; + +static const struct regmap_config msa311_regmap_config = { + .name = "msa311", + .reg_bits = 8, + .val_bits = 8, + .max_register = MSA311_OFFSET_Z_REG, + .wr_table = &msa311_writeable_table, + .rd_table = &msa311_readable_table, + .volatile_table = &msa311_volatile_table, + .cache_type = REGCACHE_RBTREE, +}; + +#define MSA311_GENMASK(field) ({ \ + typeof(&(msa311_reg_fields)[0]) _field; \ + _field = &msa311_reg_fields[(field)]; \ + GENMASK(_field->msb, _field->lsb); \ +}) + +/** + * struct msa311_priv - MSA311 internal private state + * @regs: Underlying I2C bus adapter used to abstract slave + * register accesses + * @fields: Abstract objects for each registers fields access + * @dev: Device handler associated with appropriate bus client + * @lock: Protects msa311 device state between setup and data access routines + * (power transitions, samp_freq/scale tune, retrieving axes data, etc) + * @chip_name: Chip name in the format "msa311-%02x" % partid + * @new_data_trig: Optional NEW_DATA interrupt driven trigger used + * to notify external consumers a new sample is ready + * @vdd: Optional external voltage regulator for the device power supply + */ +struct msa311_priv { + struct regmap *regs; + struct regmap_field *fields[F_MAX_FIELDS]; + + struct device *dev; + struct mutex lock; + char *chip_name; + + struct iio_trigger *new_data_trig; + struct regulator *vdd; +}; + +enum msa311_si { + MSA311_SI_X, + MSA311_SI_Y, + MSA311_SI_Z, + MSA311_SI_TIMESTAMP, +}; + +#define MSA311_ACCEL_CHANNEL(axis) { \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .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_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = MSA311_SI_##axis, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 4, \ + .endianness = IIO_LE, \ + }, \ + .datasheet_name = "ACC_"#axis, \ +} + +static const struct iio_chan_spec msa311_channels[] = { + MSA311_ACCEL_CHANNEL(X), + MSA311_ACCEL_CHANNEL(Y), + MSA311_ACCEL_CHANNEL(Z), + IIO_CHAN_SOFT_TIMESTAMP(MSA311_SI_TIMESTAMP), +}; + +/** + * msa311_get_odr() - Read Output Data Rate (ODR) value from MSA311 accel + * @msa311: MSA311 internal private state + * @odr: output ODR value + * + * This function should be called under msa311->lock. + * + * Return: 0 on success, -ERRNO in other failures + */ +static int msa311_get_odr(struct msa311_priv *msa311, unsigned int *odr) +{ + int err; + + err = regmap_field_read(msa311->fields[F_ODR], odr); + if (err) + return err; + + /* + * Filter the same 1000Hz ODR register values based on datasheet info. + * ODR can be equal to 1010-1111 for 1000Hz, but function returns 1010 + * all the time. + */ + if (*odr > MSA311_ODR_1000_HZ) + *odr = MSA311_ODR_1000_HZ; + + return 0; +} + +/** + * msa311_set_odr() - Setup Output Data Rate (ODR) value for MSA311 accel + * @msa311: MSA311 internal private state + * @odr: requested ODR value + * + * This function should be called under msa311->lock. Possible ODR values: + * - 1Hz (not available in normal mode) + * - 1.95Hz (not available in normal mode) + * - 3.9Hz + * - 7.81Hz + * - 15.63Hz + * - 31.25Hz + * - 62.5Hz + * - 125Hz + * - 250Hz + * - 500Hz + * - 1000Hz + * + * Return: 0 on success, -EINVAL for bad ODR value in the certain power mode, + * -ERRNO in other failures + */ +static int msa311_set_odr(struct msa311_priv *msa311, unsigned int odr) +{ + struct device *dev = msa311->dev; + unsigned int pwr_mode; + bool good_odr; + int err; + + err = regmap_field_read(msa311->fields[F_PWR_MODE], &pwr_mode); + if (err) + return err; + + /* Filter bad ODR values */ + if (pwr_mode == MSA311_PWR_MODE_NORMAL) + good_odr = (odr > MSA311_ODR_1_95_HZ); + else + good_odr = false; + + if (!good_odr) { + dev_err(dev, + "can't set odr %u.%06uHz, not available in %s mode\n", + msa311_odr_table[odr].integral, + msa311_odr_table[odr].microfract, + msa311_pwr_modes[pwr_mode]); + return -EINVAL; + } + + return regmap_field_write(msa311->fields[F_ODR], odr); +} + +/** + * msa311_wait_for_next_data() - Wait next accel data available after resume + * @msa311: MSA311 internal private state + * + * Return: 0 on success, -EINTR if msleep() was interrupted, + * -ERRNO in other failures + */ +static int msa311_wait_for_next_data(struct msa311_priv *msa311) +{ + static const unsigned int unintr_thresh_ms = 20; + struct device *dev = msa311->dev; + unsigned long freq_uhz; + unsigned long wait_ms; + unsigned int odr; + int err; + + err = msa311_get_odr(msa311, &odr); + if (err) { + dev_err(dev, "can't get actual frequency (%pe)\n", + ERR_PTR(err)); + return err; + } + + /* + * After msa311 resuming is done, we need to wait for data + * to be refreshed by accel logic. + * A certain timeout is calculated based on the current ODR value. + * If requested timeout isn't so long (let's assume 20ms), + * we can wait for next data in uninterruptible sleep. + */ + freq_uhz = msa311_odr_table[odr].integral * MICROHZ_PER_HZ + + msa311_odr_table[odr].microfract; + wait_ms = (MICROHZ_PER_HZ / freq_uhz) * MSEC_PER_SEC; + + if (wait_ms < unintr_thresh_ms) + usleep_range(wait_ms * USEC_PER_MSEC, + unintr_thresh_ms * USEC_PER_MSEC); + else if (msleep_interruptible(wait_ms)) + return -EINTR; + + return 0; +} + +/** + * msa311_set_pwr_mode() - Install certain MSA311 power mode + * @msa311: MSA311 internal private state + * @mode: Power mode can be equal to NORMAL or SUSPEND + * + * This function should be called under msa311->lock. + * + * Return: 0 on success, -ERRNO on failure + */ +static int msa311_set_pwr_mode(struct msa311_priv *msa311, unsigned int mode) +{ + struct device *dev = msa311->dev; + unsigned int prev_mode; + int err; + + if (mode >= ARRAY_SIZE(msa311_pwr_modes)) + return -EINVAL; + + dev_dbg(dev, "transition to %s mode\n", msa311_pwr_modes[mode]); + + err = regmap_field_read(msa311->fields[F_PWR_MODE], &prev_mode); + if (err) + return err; + + err = regmap_field_write(msa311->fields[F_PWR_MODE], mode); + if (err) + return err; + + /* Wait actual data if we wake up */ + if (prev_mode == MSA311_PWR_MODE_SUSPEND && + mode == MSA311_PWR_MODE_NORMAL) + return msa311_wait_for_next_data(msa311); + + return 0; +} + +/** + * msa311_get_axis() - Read MSA311 accel data for certain IIO channel axis spec + * @msa311: MSA311 internal private state + * @chan: IIO channel specification + * @axis: Output accel axis data for requested IIO channel spec + * + * This function should be called under msa311->lock. + * + * Return: 0 on success, -EINVAL for unknown IIO channel specification, + * -ERRNO in other failures + */ +static int msa311_get_axis(struct msa311_priv *msa311, + const struct iio_chan_spec * const chan, + __le16 *axis) +{ + struct device *dev = msa311->dev; + unsigned int axis_reg; + + if (chan->scan_index < MSA311_SI_X || chan->scan_index > MSA311_SI_Z) { + dev_err(dev, "invalid scan_index value [%d]\n", + chan->scan_index); + return -EINVAL; + } + + /* Axes data layout has 2 byte gap for each axis starting from X axis */ + axis_reg = MSA311_ACC_X_REG + (chan->scan_index << 1); + + return regmap_bulk_read(msa311->regs, axis_reg, axis, sizeof(*axis)); +} + +static int msa311_read_raw_data(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2) +{ + struct msa311_priv *msa311 = iio_priv(indio_dev); + struct device *dev = msa311->dev; + __le16 axis; + int err; + + err = pm_runtime_resume_and_get(dev); + if (err) + return err; + + err = iio_device_claim_direct_mode(indio_dev); + if (err) + return err; + + mutex_lock(&msa311->lock); + err = msa311_get_axis(msa311, chan, &axis); + mutex_unlock(&msa311->lock); + + iio_device_release_direct_mode(indio_dev); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + if (err) { + dev_err(dev, "can't get axis %s (%pe)\n", + chan->datasheet_name, ERR_PTR(err)); + return err; + } + + /* + * Axis data format is: + * ACC_X = (ACC_X_MSB[7:0] << 4) | ACC_X_LSB[7:4] + */ + *val = sign_extend32(le16_to_cpu(axis) >> chan->scan_type.shift, + chan->scan_type.realbits - 1); + + return IIO_VAL_INT; +} + +static int msa311_read_scale(struct iio_dev *indio_dev, int *val, int *val2) +{ + struct msa311_priv *msa311 = iio_priv(indio_dev); + struct device *dev = msa311->dev; + unsigned int fs; + int err; + + mutex_lock(&msa311->lock); + err = regmap_field_read(msa311->fields[F_FS], &fs); + mutex_unlock(&msa311->lock); + if (err) { + dev_err(dev, "can't get actual scale (%pe)\n", ERR_PTR(err)); + return err; + } + + *val = msa311_fs_table[fs].integral; + *val2 = msa311_fs_table[fs].microfract; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int msa311_read_samp_freq(struct iio_dev *indio_dev, + int *val, int *val2) +{ + struct msa311_priv *msa311 = iio_priv(indio_dev); + struct device *dev = msa311->dev; + unsigned int odr; + int err; + + mutex_lock(&msa311->lock); + err = msa311_get_odr(msa311, &odr); + mutex_unlock(&msa311->lock); + if (err) { + dev_err(dev, "can't get actual frequency (%pe)\n", + ERR_PTR(err)); + return err; + } + + *val = msa311_odr_table[odr].integral; + *val2 = msa311_odr_table[odr].microfract; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int msa311_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_RAW: + return msa311_read_raw_data(indio_dev, chan, val, val2); + + case IIO_CHAN_INFO_SCALE: + return msa311_read_scale(indio_dev, val, val2); + + case IIO_CHAN_INFO_SAMP_FREQ: + return msa311_read_samp_freq(indio_dev, val, val2); + + default: + return -EINVAL; + } +} + +static int msa311_read_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 = (int *)msa311_odr_table; + *type = IIO_VAL_INT_PLUS_MICRO; + /* ODR value has 2 ints (integer and fractional parts) */ + *length = ARRAY_SIZE(msa311_odr_table) * 2; + return IIO_AVAIL_LIST; + + case IIO_CHAN_INFO_SCALE: + *vals = (int *)msa311_fs_table; + *type = IIO_VAL_INT_PLUS_MICRO; + /* FS value has 2 ints (integer and fractional parts) */ + *length = ARRAY_SIZE(msa311_fs_table) * 2; + return IIO_AVAIL_LIST; + + default: + return -EINVAL; + } +} + +static int msa311_write_scale(struct iio_dev *indio_dev, int val, int val2) +{ + struct msa311_priv *msa311 = iio_priv(indio_dev); + struct device *dev = msa311->dev; + unsigned int fs; + int err; + + /* We do not have fs >= 1, so skip such values */ + if (val) + return 0; + + err = pm_runtime_resume_and_get(dev); + if (err) + return err; + + err = -EINVAL; + for (fs = 0; fs < ARRAY_SIZE(msa311_fs_table); fs++) + /* Do not check msa311_fs_table[fs].integral, it's always 0 */ + if (val2 == msa311_fs_table[fs].microfract) { + mutex_lock(&msa311->lock); + err = regmap_field_write(msa311->fields[F_FS], fs); + mutex_unlock(&msa311->lock); + break; + } + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + if (err) + dev_err(dev, "can't update scale (%pe)\n", ERR_PTR(err)); + + return err; +} + +static int msa311_write_samp_freq(struct iio_dev *indio_dev, int val, int val2) +{ + struct msa311_priv *msa311 = iio_priv(indio_dev); + struct device *dev = msa311->dev; + unsigned int odr; + int err; + + err = pm_runtime_resume_and_get(dev); + if (err) + return err; + + /* + * Sampling frequency changing is prohibited when buffer mode is + * enabled, because sometimes MSA311 chip returns outliers during + * frequency values growing up in the read operation moment. + */ + err = iio_device_claim_direct_mode(indio_dev); + if (err) + return err; + + err = -EINVAL; + for (odr = 0; odr < ARRAY_SIZE(msa311_odr_table); odr++) + if (val == msa311_odr_table[odr].integral && + val2 == msa311_odr_table[odr].microfract) { + mutex_lock(&msa311->lock); + err = msa311_set_odr(msa311, odr); + mutex_unlock(&msa311->lock); + break; + } + + iio_device_release_direct_mode(indio_dev); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + if (err) + dev_err(dev, "can't update frequency (%pe)\n", ERR_PTR(err)); + + return err; +} + +static int msa311_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return msa311_write_scale(indio_dev, val, val2); + + case IIO_CHAN_INFO_SAMP_FREQ: + return msa311_write_samp_freq(indio_dev, val, val2); + + default: + return -EINVAL; + } +} + +static int msa311_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct msa311_priv *msa311 = iio_priv(indio_dev); + struct device *dev = msa311->dev; + int err; + + if (reg > regmap_get_max_register(msa311->regs)) + return -EINVAL; + + err = pm_runtime_resume_and_get(dev); + if (err) + return err; + + mutex_lock(&msa311->lock); + + if (readval) + err = regmap_read(msa311->regs, reg, readval); + else + err = regmap_write(msa311->regs, reg, writeval); + + mutex_unlock(&msa311->lock); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + if (err) + dev_err(dev, "can't %s register %u from debugfs (%pe)\n", + str_read_write(readval), reg, ERR_PTR(err)); + + return err; +} + +static int msa311_buffer_preenable(struct iio_dev *indio_dev) +{ + struct msa311_priv *msa311 = iio_priv(indio_dev); + struct device *dev = msa311->dev; + + return pm_runtime_resume_and_get(dev); +} + +static int msa311_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct msa311_priv *msa311 = iio_priv(indio_dev); + struct device *dev = msa311->dev; + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return 0; +} + +static int msa311_set_new_data_trig_state(struct iio_trigger *trig, bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct msa311_priv *msa311 = iio_priv(indio_dev); + struct device *dev = msa311->dev; + int err; + + mutex_lock(&msa311->lock); + err = regmap_field_write(msa311->fields[F_NEW_DATA_INT_EN], state); + mutex_unlock(&msa311->lock); + if (err) + dev_err(dev, + "can't %s buffer due to new_data_int failure (%pe)\n", + str_enable_disable(state), ERR_PTR(err)); + + return err; +} + +static int msa311_validate_device(struct iio_trigger *trig, + struct iio_dev *indio_dev) +{ + return iio_trigger_get_drvdata(trig) == indio_dev ? 0 : -EINVAL; +} + +static irqreturn_t msa311_buffer_thread(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct msa311_priv *msa311 = iio_priv(pf->indio_dev); + struct iio_dev *indio_dev = pf->indio_dev; + const struct iio_chan_spec *chan; + struct device *dev = msa311->dev; + int bit, err, i = 0; + __le16 axis; + struct { + __le16 channels[MSA311_SI_Z + 1]; + s64 ts __aligned(8); + } buf; + + memset(&buf, 0, sizeof(buf)); + + mutex_lock(&msa311->lock); + + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->masklength) { + chan = &msa311_channels[bit]; + + err = msa311_get_axis(msa311, chan, &axis); + if (err) { + mutex_unlock(&msa311->lock); + dev_err(dev, "can't get axis %s (%pe)\n", + chan->datasheet_name, ERR_PTR(err)); + goto notify_done; + } + + buf.channels[i++] = axis; + } + + mutex_unlock(&msa311->lock); + + iio_push_to_buffers_with_timestamp(indio_dev, &buf, + iio_get_time_ns(indio_dev)); + +notify_done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static irqreturn_t msa311_irq_thread(int irq, void *p) +{ + struct msa311_priv *msa311 = iio_priv(p); + unsigned int new_data_int_enabled; + struct device *dev = msa311->dev; + int err; + + mutex_lock(&msa311->lock); + + /* + * We do not check NEW_DATA int status, because based on the + * specification it's cleared automatically after a fixed time. + * So just check that is enabled by driver logic. + */ + err = regmap_field_read(msa311->fields[F_NEW_DATA_INT_EN], + &new_data_int_enabled); + + mutex_unlock(&msa311->lock); + if (err) { + dev_err(dev, "can't read new_data interrupt state (%pe)\n", + ERR_PTR(err)); + return IRQ_NONE; + } + + if (new_data_int_enabled) + iio_trigger_poll_chained(msa311->new_data_trig); + + return IRQ_HANDLED; +} + +static const struct iio_info msa311_info = { + .read_raw = msa311_read_raw, + .read_avail = msa311_read_avail, + .write_raw = msa311_write_raw, + .debugfs_reg_access = msa311_debugfs_reg_access, +}; + +static const struct iio_buffer_setup_ops msa311_buffer_setup_ops = { + .preenable = msa311_buffer_preenable, + .postdisable = msa311_buffer_postdisable, +}; + +static const struct iio_trigger_ops msa311_new_data_trig_ops = { + .set_trigger_state = msa311_set_new_data_trig_state, + .validate_device = msa311_validate_device, +}; + +static int msa311_check_partid(struct msa311_priv *msa311) +{ + struct device *dev = msa311->dev; + unsigned int partid; + int err; + + err = regmap_read(msa311->regs, MSA311_PARTID_REG, &partid); + if (err) + return dev_err_probe(dev, err, "failed to read partid\n"); + + if (partid != MSA311_WHO_AM_I) + dev_warn(dev, "invalid partid (%#x), expected (%#x)\n", + partid, MSA311_WHO_AM_I); + + msa311->chip_name = devm_kasprintf(dev, GFP_KERNEL, + "msa311-%02x", partid); + if (!msa311->chip_name) + return dev_err_probe(dev, -ENOMEM, "can't alloc chip name\n"); + + return 0; +} + +static int msa311_soft_reset(struct msa311_priv *msa311) +{ + struct device *dev = msa311->dev; + int err; + + err = regmap_write(msa311->regs, MSA311_SOFT_RESET_REG, + MSA311_GENMASK(F_SOFT_RESET_I2C) | + MSA311_GENMASK(F_SOFT_RESET_SPI)); + if (err) + return dev_err_probe(dev, err, "can't soft reset all logic\n"); + + return 0; +} + +static int msa311_chip_init(struct msa311_priv *msa311) +{ + struct device *dev = msa311->dev; + const char zero_bulk[2] = { }; + int err; + + err = regmap_write(msa311->regs, MSA311_RANGE_REG, MSA311_FS_16G); + if (err) + return dev_err_probe(dev, err, "failed to setup accel range\n"); + + /* Disable all interrupts by default */ + err = regmap_bulk_write(msa311->regs, MSA311_INT_SET_0_REG, + zero_bulk, sizeof(zero_bulk)); + if (err) + return dev_err_probe(dev, err, + "can't disable set0/set1 interrupts\n"); + + /* Unmap all INT1 interrupts by default */ + err = regmap_bulk_write(msa311->regs, MSA311_INT_MAP_0_REG, + zero_bulk, sizeof(zero_bulk)); + if (err) + return dev_err_probe(dev, err, + "failed to unmap map0/map1 interrupts\n"); + + /* Disable all axes by default */ + err = regmap_update_bits(msa311->regs, MSA311_ODR_REG, + MSA311_GENMASK(F_X_AXIS_DIS) | + MSA311_GENMASK(F_Y_AXIS_DIS) | + MSA311_GENMASK(F_Z_AXIS_DIS), 0); + if (err) + return dev_err_probe(dev, err, "can't enable all axes\n"); + + err = msa311_set_odr(msa311, MSA311_ODR_125_HZ); + if (err) + return dev_err_probe(dev, err, + "failed to set accel frequency\n"); + + return 0; +} + +static int msa311_setup_interrupts(struct msa311_priv *msa311) +{ + struct device *dev = msa311->dev; + struct i2c_client *i2c = to_i2c_client(dev); + struct iio_dev *indio_dev = i2c_get_clientdata(i2c); + struct iio_trigger *trig; + int err; + + /* Keep going without interrupts if no initialized I2C IRQ */ + if (i2c->irq <= 0) + return 0; + + err = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + msa311_irq_thread, IRQF_ONESHOT, + msa311->chip_name, indio_dev); + if (err) + return dev_err_probe(dev, err, "failed to request IRQ\n"); + + trig = devm_iio_trigger_alloc(dev, "%s-new-data", msa311->chip_name); + if (!trig) + return dev_err_probe(dev, -ENOMEM, + "can't allocate newdata trigger\n"); + + msa311->new_data_trig = trig; + msa311->new_data_trig->ops = &msa311_new_data_trig_ops; + iio_trigger_set_drvdata(msa311->new_data_trig, indio_dev); + + err = devm_iio_trigger_register(dev, msa311->new_data_trig); + if (err) + return dev_err_probe(dev, err, + "can't register newdata trigger\n"); + + err = regmap_field_write(msa311->fields[F_INT1_OD], + MSA311_INT1_OD_PUSH_PULL); + if (err) + return dev_err_probe(dev, err, + "can't enable push-pull interrupt\n"); + + err = regmap_field_write(msa311->fields[F_INT1_LVL], + MSA311_INT1_LVL_HIGH); + if (err) + return dev_err_probe(dev, err, + "can't set active interrupt level\n"); + + err = regmap_field_write(msa311->fields[F_LATCH_INT], + MSA311_LATCH_INT_LATCHED); + if (err) + return dev_err_probe(dev, err, + "can't latch interrupt\n"); + + err = regmap_field_write(msa311->fields[F_RESET_INT], 1); + if (err) + return dev_err_probe(dev, err, + "can't reset interrupt\n"); + + err = regmap_field_write(msa311->fields[F_INT1_NEW_DATA], 1); + if (err) + return dev_err_probe(dev, err, + "can't map new data interrupt\n"); + + return 0; +} + +static int msa311_regmap_init(struct msa311_priv *msa311) +{ + struct regmap_field **fields = msa311->fields; + struct device *dev = msa311->dev; + struct i2c_client *i2c = to_i2c_client(dev); + struct regmap *regmap; + int i; + + regmap = devm_regmap_init_i2c(i2c, &msa311_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "failed to register i2c regmap\n"); + + msa311->regs = regmap; + + for (i = 0; i < F_MAX_FIELDS; i++) { + fields[i] = devm_regmap_field_alloc(dev, + msa311->regs, + msa311_reg_fields[i]); + if (IS_ERR(msa311->fields[i])) + return dev_err_probe(dev, PTR_ERR(msa311->fields[i]), + "can't alloc field[%d]\n", i); + } + + return 0; +} + +static void msa311_powerdown(void *msa311) +{ + msa311_set_pwr_mode(msa311, MSA311_PWR_MODE_SUSPEND); +} + +static void msa311_vdd_disable(void *vdd) +{ + regulator_disable(vdd); +} + +static int msa311_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct msa311_priv *msa311; + struct iio_dev *indio_dev; + int err; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*msa311)); + if (!indio_dev) + return dev_err_probe(dev, -ENOMEM, + "IIO device allocation failed\n"); + + msa311 = iio_priv(indio_dev); + msa311->dev = dev; + i2c_set_clientdata(i2c, indio_dev); + + err = msa311_regmap_init(msa311); + if (err) + return err; + + mutex_init(&msa311->lock); + + msa311->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(msa311->vdd)) + return dev_err_probe(dev, PTR_ERR(msa311->vdd), + "can't get vdd supply\n"); + + err = regulator_enable(msa311->vdd); + if (err) + return dev_err_probe(dev, err, "can't enable vdd supply\n"); + + err = devm_add_action_or_reset(dev, msa311_vdd_disable, msa311->vdd); + if (err) + return dev_err_probe(dev, err, + "can't add vdd disable action\n"); + + err = msa311_check_partid(msa311); + if (err) + return err; + + err = msa311_soft_reset(msa311); + if (err) + return err; + + err = msa311_set_pwr_mode(msa311, MSA311_PWR_MODE_NORMAL); + if (err) + return dev_err_probe(dev, err, "failed to power on device\n"); + + /* + * Register powerdown deferred callback which suspends the chip + * after module unloaded. + * + * MSA311 should be in SUSPEND mode in the two cases: + * 1) When driver is loaded, but we do not have any data or + * configuration requests to it (we are solving it using + * autosuspend feature). + * 2) When driver is unloaded and device is not used (devm action is + * used in this case). + */ + err = devm_add_action_or_reset(dev, msa311_powerdown, msa311); + if (err) + return dev_err_probe(dev, err, "can't add powerdown action\n"); + + err = pm_runtime_set_active(dev); + if (err) + return err; + + err = devm_pm_runtime_enable(dev); + if (err) + return err; + + pm_runtime_get_noresume(dev); + pm_runtime_set_autosuspend_delay(dev, MSA311_PWR_SLEEP_DELAY_MS); + pm_runtime_use_autosuspend(dev); + + err = msa311_chip_init(msa311); + if (err) + return err; + + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = msa311_channels; + indio_dev->num_channels = ARRAY_SIZE(msa311_channels); + indio_dev->name = msa311->chip_name; + indio_dev->info = &msa311_info; + + err = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + msa311_buffer_thread, + &msa311_buffer_setup_ops); + if (err) + return dev_err_probe(dev, err, + "can't setup IIO trigger buffer\n"); + + err = msa311_setup_interrupts(msa311); + if (err) + return err; + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + err = devm_iio_device_register(dev, indio_dev); + if (err) + return dev_err_probe(dev, err, "IIO device register failed\n"); + + return 0; +} + +static int msa311_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct msa311_priv *msa311 = iio_priv(indio_dev); + int err; + + mutex_lock(&msa311->lock); + err = msa311_set_pwr_mode(msa311, MSA311_PWR_MODE_SUSPEND); + mutex_unlock(&msa311->lock); + if (err) + dev_err(dev, "failed to power off device (%pe)\n", + ERR_PTR(err)); + + return err; +} + +static int msa311_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct msa311_priv *msa311 = iio_priv(indio_dev); + int err; + + mutex_lock(&msa311->lock); + err = msa311_set_pwr_mode(msa311, MSA311_PWR_MODE_NORMAL); + mutex_unlock(&msa311->lock); + if (err) + dev_err(dev, "failed to power on device (%pe)\n", + ERR_PTR(err)); + + return err; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(msa311_pm_ops, msa311_runtime_suspend, + msa311_runtime_resume, NULL); + +static const struct i2c_device_id msa311_i2c_id[] = { + { .name = "msa311" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, msa311_i2c_id); + +static const struct of_device_id msa311_of_match[] = { + { .compatible = "memsensing,msa311" }, + { } +}; +MODULE_DEVICE_TABLE(of, msa311_of_match); + +static struct i2c_driver msa311_driver = { + .driver = { + .name = "msa311", + .of_match_table = msa311_of_match, + .pm = pm_ptr(&msa311_pm_ops), + }, + .probe_new = msa311_probe, + .id_table = msa311_i2c_id, +}; +module_i2c_driver(msa311_driver); + +MODULE_AUTHOR("Dmitry Rokosov "); +MODULE_DESCRIPTION("MEMSensing MSA311 3-axis accelerometer driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 4eba8239c8ce419e53813d02251a46c27597fe73 Mon Sep 17 00:00:00 2001 From: Dmitry Rokosov Date: Mon, 22 Aug 2022 17:49:26 +0000 Subject: dt-bindings: iio: accel: add dt-binding schema for msa311 accel driver Introduce devicetree binding json-schema for MSA311 tri-axial, low-g accelerometer driver. Signed-off-by: Dmitry Rokosov Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220822175011.2886-5-ddrokosov@sberdevices.ru Signed-off-by: Jonathan Cameron --- .../bindings/iio/accel/memsensing,msa311.yaml | 53 ++++++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 54 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/accel/memsensing,msa311.yaml diff --git a/Documentation/devicetree/bindings/iio/accel/memsensing,msa311.yaml b/Documentation/devicetree/bindings/iio/accel/memsensing,msa311.yaml new file mode 100644 index 000000000000..23528dcaa073 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/accel/memsensing,msa311.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause + +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/iio/accel/memsensing,msa311.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: MEMSensing digital 3-Axis accelerometer + +maintainers: + - Dmitry Rokosov + +description: | + MSA311 is a tri-axial, low-g accelerometer with I2C digital output for + sensitivity consumer applications. It has dynamical user selectable full + scales range of +-2g/+-4g/+-8g/+-16g and allows acceleration measurements + with output data rates from 1Hz to 1000Hz. + Datasheet can be found at following URL + https://cdn-shop.adafruit.com/product-files/5309/MSA311-V1.1-ENG.pdf + +properties: + compatible: + const: memsensing,msa311 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + vdd-supply: true + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + accelerometer@62 { + compatible = "memsensing,msa311"; + reg = <0x62>; + interrupt-parent = <&gpio_intc>; + interrupts = <29 IRQ_TYPE_EDGE_RISING>; + vdd-supply = <&vcc_5v>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index b8a621941db9..00cf4a00bb6d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13180,6 +13180,7 @@ MEMSENSING MICROSYSTEMS MSA311 DRIVER M: Dmitry Rokosov L: linux-iio@vger.kernel.org S: Maintained +F: Documentation/devicetree/bindings/iio/accel/memsensing,msa311.yaml F: drivers/iio/accel/msa311.c MEN A21 WATCHDOG DRIVER -- cgit v1.2.3 From 1cf507cfddd38724abeed2298c994c606aff89a9 Mon Sep 17 00:00:00 2001 From: wangjianli Date: Tue, 23 Aug 2022 22:00:23 +0800 Subject: iio/accel: fix repeated words in comments Delete the redundant word 'in'. Signed-off-by: wangjianli Link: https://lore.kernel.org/r/20220823140023.2567-1-wangjianli@cdjrlc.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/kxcjk-1013.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index 748b35c2f0c3..b073ab12e6a4 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -1064,7 +1064,7 @@ static int kxcjk1013_write_event_config(struct iio_dev *indio_dev, /* * We will expect the enable and disable to do operation in - * in reverse order. This will happen here anyway as our + * reverse order. This will happen here anyway as our * resume operation uses sync mode runtime pm calls, the * suspend operation will be delayed by autosuspend delay * So the disable operation will still happen in reverse of -- cgit v1.2.3 From 1b55d33e027478f2234ef5c4f6d07be990539928 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Wed, 24 Aug 2022 20:09:13 +0300 Subject: MAINTAINERS: fix Analog Devices forum links Fix the links to redirect to the correct forum subsection for the latest Analog Devices drivers added. Signed-off-by: Antoniu Miclaus Link: https://lore.kernel.org/r/20220824170913.13411-1-antoniu.miclaus@analog.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 00cf4a00bb6d..d9c8596ee28b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -620,7 +620,7 @@ ADXL367 THREE-AXIS DIGITAL ACCELEROMETER DRIVER M: Cosmin Tanislav L: linux-iio@vger.kernel.org S: Supported -W: http://ez.analog.com/community/linux-device-drivers +W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/accel/adi,adxl367.yaml F: drivers/iio/accel/adxl367* @@ -1144,7 +1144,7 @@ ANALOG DEVICES INC AD74413R DRIVER M: Cosmin Tanislav L: linux-iio@vger.kernel.org S: Supported -W: http://ez.analog.com/community/linux-device-drivers +W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml F: drivers/iio/addac/ad74413r.c F: include/dt-bindings/iio/addac/adi,ad74413r.h @@ -11915,7 +11915,7 @@ LTC2688 IIO DAC DRIVER M: Nuno Sá L: linux-iio@vger.kernel.org S: Supported -W: http://ez.analog.com/community/linux-device-drivers +W: https://ez.analog.com/linux-software-drivers F: Documentation/ABI/testing/sysfs-bus-iio-dac-ltc2688 F: Documentation/devicetree/bindings/iio/dac/adi,ltc2688.yaml F: drivers/iio/dac/ltc2688.c -- cgit v1.2.3 From c682c96334cb790930c814e58e33d5e0df600a63 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 23 Aug 2022 09:56:33 -0500 Subject: dt-bindings: iio: Add missing (unevaluated|additional)Properties on child nodes In order to ensure only documented properties are present, node schemas must have unevaluatedProperties or additionalProperties set to false (typically). Signed-off-by: Rob Herring Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220823145649.3118479-2-robh@kernel.org Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml | 1 + Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.yaml | 2 ++ Documentation/devicetree/bindings/iio/adc/ti,ads1015.yaml | 1 + Documentation/devicetree/bindings/iio/adc/xlnx,zynqmp-ams.yaml | 1 + Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml | 1 + Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml | 1 + Documentation/devicetree/bindings/iio/dac/adi,ad5770r.yaml | 4 ++++ Documentation/devicetree/bindings/iio/dac/adi,ltc2688.yaml | 1 + 8 files changed, 12 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml index 74a4a9d95798..8bac0c4120dd 100644 --- a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml @@ -58,6 +58,7 @@ required: patternProperties: "^.*@[0-9a-f]+$": type: object + additionalProperties: false description: | Represents the external channels which are connected to the ADC. For compatible property "qcom,spmi-vadc" following channels, also known as diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.yaml index 92f9472a77ae..1970503389aa 100644 --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.yaml @@ -72,6 +72,7 @@ additionalProperties: false patternProperties: "^filter@[0-9]+$": type: object + unevaluatedProperties: false description: child node properties: @@ -225,6 +226,7 @@ patternProperties: patternProperties: "^dfsdm-dai+$": type: object + additionalProperties: false description: child node properties: diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads1015.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads1015.yaml index a3b79438a13a..2c3c2cf2145c 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,ads1015.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,ads1015.yaml @@ -43,6 +43,7 @@ additionalProperties: false patternProperties: "^channel@[0-7]+$": type: object + additionalProperties: false description: Child nodes needed for each channel that the platform uses. diff --git a/Documentation/devicetree/bindings/iio/adc/xlnx,zynqmp-ams.yaml b/Documentation/devicetree/bindings/iio/adc/xlnx,zynqmp-ams.yaml index 3698b4b0900f..be93c109d6ac 100644 --- a/Documentation/devicetree/bindings/iio/adc/xlnx,zynqmp-ams.yaml +++ b/Documentation/devicetree/bindings/iio/adc/xlnx,zynqmp-ams.yaml @@ -134,6 +134,7 @@ properties: ams-pl@400: type: object + additionalProperties: false description: PL-SYSMON is capable of monitoring off chip voltage and temperature. PL-SYSMON block has DRP, JTAG and I2C interface to enable monitoring diff --git a/Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml b/Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml index baa65a521bad..03bb90a7f4f8 100644 --- a/Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml +++ b/Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml @@ -63,6 +63,7 @@ additionalProperties: false patternProperties: "^channel@[0-3]$": type: object + additionalProperties: false description: Represents the external channels which are connected to the device. properties: diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml index 9c48c76993fe..fee0f023a8c8 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml @@ -78,6 +78,7 @@ patternProperties: custom-output-range-config: type: object + additionalProperties: false description: Configuration of custom range when adi,output-range-microvolt is not present. The formulas for calculation the output voltages are diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5770r.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5770r.yaml index ca5432ffdedb..8e7da0de918f 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5770r.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5770r.yaml @@ -60,6 +60,7 @@ properties: connected to the DAC. Channel 0 can act both as a current source and sink. type: object + additionalProperties: false properties: reg: @@ -83,6 +84,7 @@ properties: description: Represents an external channel which are connected to the DAC. type: object + additionalProperties: false properties: reg: @@ -99,6 +101,7 @@ properties: description: Represents an external channel which are connected to the DAC. type: object + additionalProperties: false properties: reg: @@ -114,6 +117,7 @@ properties: patternProperties: "^channel@([3-5])$": type: object + additionalProperties: false description: Represents the external channels which are connected to the DAC. properties: reg: diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ltc2688.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ltc2688.yaml index 48f9e7d29423..15cc6bf59b13 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ltc2688.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ltc2688.yaml @@ -48,6 +48,7 @@ properties: patternProperties: "^channel@([0-9]|1[0-5])$": type: object + additionalProperties: false properties: reg: -- cgit v1.2.3 From f5ffeca5086fef68765f3c1dbc9a12183833adf0 Mon Sep 17 00:00:00 2001 From: Shreeya Patel Date: Fri, 12 Aug 2022 15:34:24 +0530 Subject: iio: light: ltrf216a: Add raw attribute Add IIO_CHAN_INFO_RAW to the mask to be able to read raw values from the light sensor. The userspace code for brightness control in steam deck uses the in_illuminance_input value through sysfs and multiplies it with a constant stored in BIOS at factory calibration time. The downstream driver for LTRF216A that we have been using has incorrect formula for LUX calculation which we corrected in the upstreamed driver. Now to be able to use the upstreamed driver, we need to add some magic in userspace so that the brightness control works like before even with the updated LUX formula. Hence, we need the raw data to calculate a constant that can be added in userspace code. Downstream driver LUX formula :- (greendata*8*LTRF216A_WIN_FAC) / (data->als_gain_fac*data->int_time_fac*10) Upstreamed driver LUX formula :- (greendata*45*LTRF216A_WIN_FAC) / (data->als_gain_fac*data->int_time_fac) greendata is the ALS_DATA which we would like to get through sysfs using the raw attribute. Signed-off-by: Shreeya Patel Link: https://lore.kernel.org/r/20220812100424.529425-1-shreeya.patel@collabora.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/ltrf216a.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/iio/light/ltrf216a.c b/drivers/iio/light/ltrf216a.c index e6e24e70d2b9..4b8ef36b6912 100644 --- a/drivers/iio/light/ltrf216a.c +++ b/drivers/iio/light/ltrf216a.c @@ -93,6 +93,7 @@ static const struct iio_chan_spec ltrf216a_channels[] = { { .type = IIO_LIGHT, .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED) | BIT(IIO_CHAN_INFO_INT_TIME), .info_mask_separate_available = @@ -259,6 +260,18 @@ static int ltrf216a_read_raw(struct iio_dev *indio_dev, int ret; switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = ltrf216a_set_power_state(data, true); + if (ret) + return ret; + mutex_lock(&data->lock); + ret = ltrf216a_read_data(data, LTRF216A_ALS_DATA_0); + mutex_unlock(&data->lock); + ltrf216a_set_power_state(data, false); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; case IIO_CHAN_INFO_PROCESSED: mutex_lock(&data->lock); ret = ltrf216a_get_lux(data); -- cgit v1.2.3 From 3727f03e2bc63de56fd00acb8846393106318156 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 30 Aug 2022 18:26:09 +0100 Subject: coresight: Remove unused function parameter The ability to use a custom function in this sysfs show function isn't used so remove it. No functional changes. Signed-off-by: James Clark Reviewed-by: Mike Leach Link: https://lore.kernel.org/r/20220830172614.340962-2-james.clark@arm.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-priv.h | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index ff1dd2092ac5..f2458b794ef3 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -40,31 +40,23 @@ #define ETM_MODE_EXCL_KERN BIT(30) #define ETM_MODE_EXCL_USER BIT(31) -typedef u32 (*coresight_read_fn)(const struct device *, u32 offset); -#define __coresight_simple_func(type, func, name, lo_off, hi_off) \ +#define __coresight_simple_show(type, name, lo_off, hi_off) \ static ssize_t name##_show(struct device *_dev, \ struct device_attribute *attr, char *buf) \ { \ type *drvdata = dev_get_drvdata(_dev->parent); \ - coresight_read_fn fn = func; \ u64 val; \ pm_runtime_get_sync(_dev->parent); \ - if (fn) \ - val = (u64)fn(_dev->parent, lo_off); \ - else \ - val = coresight_read_reg_pair(drvdata->base, \ - lo_off, hi_off); \ + val = coresight_read_reg_pair(drvdata->base, lo_off, hi_off); \ pm_runtime_put_sync(_dev->parent); \ return scnprintf(buf, PAGE_SIZE, "0x%llx\n", val); \ } \ static DEVICE_ATTR_RO(name) -#define coresight_simple_func(type, func, name, offset) \ - __coresight_simple_func(type, func, name, offset, -1) #define coresight_simple_reg32(type, name, offset) \ - __coresight_simple_func(type, NULL, name, offset, -1) + __coresight_simple_show(type, name, offset, -1) #define coresight_simple_reg64(type, name, lo_off, hi_off) \ - __coresight_simple_func(type, NULL, name, lo_off, hi_off) + __coresight_simple_show(type, name, lo_off, hi_off) extern const u32 coresight_barrier_pkt[4]; #define CORESIGHT_BARRIER_PKT_SIZE (sizeof(coresight_barrier_pkt)) -- cgit v1.2.3 From b6df1cbb415e543f2908f9c59a8fb20714b86879 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 30 Aug 2022 18:26:10 +0100 Subject: coresight: Simplify sysfs accessors by using csdev_access abstraction The coresight_device struct is available in the sysfs accessor, and this contains a csdev_access struct which can be used to access registers. Use this instead of passing in the type of each drvdata so that a common function can be shared between all the cs drivers. No functional changes. Signed-off-by: James Clark Reviewed-by: Mike Leach Link: https://lore.kernel.org/r/20220830172614.340962-3-james.clark@arm.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-catu.c | 18 ++++++------ drivers/hwtracing/coresight/coresight-etb10.c | 19 ++++++------- .../hwtracing/coresight/coresight-etm3x-sysfs.c | 23 +++++++-------- drivers/hwtracing/coresight/coresight-priv.h | 14 ++++----- drivers/hwtracing/coresight/coresight-replicator.c | 7 ++--- drivers/hwtracing/coresight/coresight-stm.c | 27 ++++++++---------- drivers/hwtracing/coresight/coresight-tmc-core.c | 33 +++++++++------------- include/linux/coresight.h | 18 ++++++++++++ 8 files changed, 79 insertions(+), 80 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-catu.c b/drivers/hwtracing/coresight/coresight-catu.c index e0740c6dbd54..9d89c4054046 100644 --- a/drivers/hwtracing/coresight/coresight-catu.c +++ b/drivers/hwtracing/coresight/coresight-catu.c @@ -365,16 +365,14 @@ static const struct etr_buf_operations etr_catu_buf_ops = { .get_data = catu_get_data_etr_buf, }; -coresight_simple_reg32(struct catu_drvdata, devid, CORESIGHT_DEVID); -coresight_simple_reg32(struct catu_drvdata, control, CATU_CONTROL); -coresight_simple_reg32(struct catu_drvdata, status, CATU_STATUS); -coresight_simple_reg32(struct catu_drvdata, mode, CATU_MODE); -coresight_simple_reg32(struct catu_drvdata, axictrl, CATU_AXICTRL); -coresight_simple_reg32(struct catu_drvdata, irqen, CATU_IRQEN); -coresight_simple_reg64(struct catu_drvdata, sladdr, - CATU_SLADDRLO, CATU_SLADDRHI); -coresight_simple_reg64(struct catu_drvdata, inaddr, - CATU_INADDRLO, CATU_INADDRHI); +coresight_simple_reg32(devid, CORESIGHT_DEVID); +coresight_simple_reg32(control, CATU_CONTROL); +coresight_simple_reg32(status, CATU_STATUS); +coresight_simple_reg32(mode, CATU_MODE); +coresight_simple_reg32(axictrl, CATU_AXICTRL); +coresight_simple_reg32(irqen, CATU_IRQEN); +coresight_simple_reg64(sladdr, CATU_SLADDRLO, CATU_SLADDRHI); +coresight_simple_reg64(inaddr, CATU_INADDRLO, CATU_INADDRHI); static struct attribute *catu_mgmt_attrs[] = { &dev_attr_devid.attr, diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index efa39820acec..405bb3355cb1 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -655,17 +655,14 @@ static const struct file_operations etb_fops = { .llseek = no_llseek, }; -#define coresight_etb10_reg(name, offset) \ - coresight_simple_reg32(struct etb_drvdata, name, offset) - -coresight_etb10_reg(rdp, ETB_RAM_DEPTH_REG); -coresight_etb10_reg(sts, ETB_STATUS_REG); -coresight_etb10_reg(rrp, ETB_RAM_READ_POINTER); -coresight_etb10_reg(rwp, ETB_RAM_WRITE_POINTER); -coresight_etb10_reg(trg, ETB_TRG); -coresight_etb10_reg(ctl, ETB_CTL_REG); -coresight_etb10_reg(ffsr, ETB_FFSR); -coresight_etb10_reg(ffcr, ETB_FFCR); +coresight_simple_reg32(rdp, ETB_RAM_DEPTH_REG); +coresight_simple_reg32(sts, ETB_STATUS_REG); +coresight_simple_reg32(rrp, ETB_RAM_READ_POINTER); +coresight_simple_reg32(rwp, ETB_RAM_WRITE_POINTER); +coresight_simple_reg32(trg, ETB_TRG); +coresight_simple_reg32(ctl, ETB_CTL_REG); +coresight_simple_reg32(ffsr, ETB_FFSR); +coresight_simple_reg32(ffcr, ETB_FFCR); static struct attribute *coresight_etb_mgmt_attrs[] = { &dev_attr_rdp.attr, diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index 68fcbf4ce7a8..12f8e8176c7e 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -1252,19 +1252,16 @@ static struct attribute *coresight_etm_attrs[] = { NULL, }; -#define coresight_etm3x_reg(name, offset) \ - coresight_simple_reg32(struct etm_drvdata, name, offset) - -coresight_etm3x_reg(etmccr, ETMCCR); -coresight_etm3x_reg(etmccer, ETMCCER); -coresight_etm3x_reg(etmscr, ETMSCR); -coresight_etm3x_reg(etmidr, ETMIDR); -coresight_etm3x_reg(etmcr, ETMCR); -coresight_etm3x_reg(etmtraceidr, ETMTRACEIDR); -coresight_etm3x_reg(etmteevr, ETMTEEVR); -coresight_etm3x_reg(etmtssvr, ETMTSSCR); -coresight_etm3x_reg(etmtecr1, ETMTECR1); -coresight_etm3x_reg(etmtecr2, ETMTECR2); +coresight_simple_reg32(etmccr, ETMCCR); +coresight_simple_reg32(etmccer, ETMCCER); +coresight_simple_reg32(etmscr, ETMSCR); +coresight_simple_reg32(etmidr, ETMIDR); +coresight_simple_reg32(etmcr, ETMCR); +coresight_simple_reg32(etmtraceidr, ETMTRACEIDR); +coresight_simple_reg32(etmteevr, ETMTEEVR); +coresight_simple_reg32(etmtssvr, ETMTSSCR); +coresight_simple_reg32(etmtecr1, ETMTECR1); +coresight_simple_reg32(etmtecr2, ETMTECR2); static struct attribute *coresight_etm_mgmt_attrs[] = { &dev_attr_etmccr.attr, diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index f2458b794ef3..cf8ae768106e 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -40,23 +40,23 @@ #define ETM_MODE_EXCL_KERN BIT(30) #define ETM_MODE_EXCL_USER BIT(31) -#define __coresight_simple_show(type, name, lo_off, hi_off) \ +#define __coresight_simple_show(name, lo_off, hi_off) \ static ssize_t name##_show(struct device *_dev, \ struct device_attribute *attr, char *buf) \ { \ - type *drvdata = dev_get_drvdata(_dev->parent); \ + struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev); \ u64 val; \ pm_runtime_get_sync(_dev->parent); \ - val = coresight_read_reg_pair(drvdata->base, lo_off, hi_off); \ + val = csdev_access_relaxed_read_pair(&csdev->access, lo_off, hi_off); \ pm_runtime_put_sync(_dev->parent); \ return scnprintf(buf, PAGE_SIZE, "0x%llx\n", val); \ } \ static DEVICE_ATTR_RO(name) -#define coresight_simple_reg32(type, name, offset) \ - __coresight_simple_show(type, name, offset, -1) -#define coresight_simple_reg64(type, name, lo_off, hi_off) \ - __coresight_simple_show(type, name, lo_off, hi_off) +#define coresight_simple_reg32(name, offset) \ + __coresight_simple_show(name, offset, -1) +#define coresight_simple_reg64(name, lo_off, hi_off) \ + __coresight_simple_show(name, lo_off, hi_off) extern const u32 coresight_barrier_pkt[4]; #define CORESIGHT_BARRIER_PKT_SIZE (sizeof(coresight_barrier_pkt)) diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index b86acbc74cf0..7cffcbb2ec42 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -196,11 +196,8 @@ static const struct coresight_ops replicator_cs_ops = { .link_ops = &replicator_link_ops, }; -#define coresight_replicator_reg(name, offset) \ - coresight_simple_reg32(struct replicator_drvdata, name, offset) - -coresight_replicator_reg(idfilter0, REPLICATOR_IDFILTER0); -coresight_replicator_reg(idfilter1, REPLICATOR_IDFILTER1); +coresight_simple_reg32(idfilter0, REPLICATOR_IDFILTER0); +coresight_simple_reg32(idfilter1, REPLICATOR_IDFILTER1); static struct attribute *replicator_mgmt_attrs[] = { &dev_attr_idfilter0.attr, diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index bb14a3a8a921..4a31905604fe 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -634,21 +634,18 @@ static ssize_t traceid_store(struct device *dev, } static DEVICE_ATTR_RW(traceid); -#define coresight_stm_reg(name, offset) \ - coresight_simple_reg32(struct stm_drvdata, name, offset) - -coresight_stm_reg(tcsr, STMTCSR); -coresight_stm_reg(tsfreqr, STMTSFREQR); -coresight_stm_reg(syncr, STMSYNCR); -coresight_stm_reg(sper, STMSPER); -coresight_stm_reg(spter, STMSPTER); -coresight_stm_reg(privmaskr, STMPRIVMASKR); -coresight_stm_reg(spscr, STMSPSCR); -coresight_stm_reg(spmscr, STMSPMSCR); -coresight_stm_reg(spfeat1r, STMSPFEAT1R); -coresight_stm_reg(spfeat2r, STMSPFEAT2R); -coresight_stm_reg(spfeat3r, STMSPFEAT3R); -coresight_stm_reg(devid, CORESIGHT_DEVID); +coresight_simple_reg32(tcsr, STMTCSR); +coresight_simple_reg32(tsfreqr, STMTSFREQR); +coresight_simple_reg32(syncr, STMSYNCR); +coresight_simple_reg32(sper, STMSPER); +coresight_simple_reg32(spter, STMSPTER); +coresight_simple_reg32(privmaskr, STMPRIVMASKR); +coresight_simple_reg32(spscr, STMSPSCR); +coresight_simple_reg32(spmscr, STMSPMSCR); +coresight_simple_reg32(spfeat1r, STMSPFEAT1R); +coresight_simple_reg32(spfeat2r, STMSPFEAT2R); +coresight_simple_reg32(spfeat3r, STMSPFEAT3R); +coresight_simple_reg32(devid, CORESIGHT_DEVID); static struct attribute *coresight_stm_attrs[] = { &dev_attr_hwevent_enable.attr, diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c index d0276af82494..781d213526b7 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-core.c +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c @@ -251,25 +251,20 @@ static enum tmc_mem_intf_width tmc_get_memwidth(u32 devid) return memwidth; } -#define coresight_tmc_reg(name, offset) \ - coresight_simple_reg32(struct tmc_drvdata, name, offset) -#define coresight_tmc_reg64(name, lo_off, hi_off) \ - coresight_simple_reg64(struct tmc_drvdata, name, lo_off, hi_off) - -coresight_tmc_reg(rsz, TMC_RSZ); -coresight_tmc_reg(sts, TMC_STS); -coresight_tmc_reg(trg, TMC_TRG); -coresight_tmc_reg(ctl, TMC_CTL); -coresight_tmc_reg(ffsr, TMC_FFSR); -coresight_tmc_reg(ffcr, TMC_FFCR); -coresight_tmc_reg(mode, TMC_MODE); -coresight_tmc_reg(pscr, TMC_PSCR); -coresight_tmc_reg(axictl, TMC_AXICTL); -coresight_tmc_reg(authstatus, TMC_AUTHSTATUS); -coresight_tmc_reg(devid, CORESIGHT_DEVID); -coresight_tmc_reg64(rrp, TMC_RRP, TMC_RRPHI); -coresight_tmc_reg64(rwp, TMC_RWP, TMC_RWPHI); -coresight_tmc_reg64(dba, TMC_DBALO, TMC_DBAHI); +coresight_simple_reg32(rsz, TMC_RSZ); +coresight_simple_reg32(sts, TMC_STS); +coresight_simple_reg32(trg, TMC_TRG); +coresight_simple_reg32(ctl, TMC_CTL); +coresight_simple_reg32(ffsr, TMC_FFSR); +coresight_simple_reg32(ffcr, TMC_FFCR); +coresight_simple_reg32(mode, TMC_MODE); +coresight_simple_reg32(pscr, TMC_PSCR); +coresight_simple_reg32(axictl, TMC_AXICTL); +coresight_simple_reg32(authstatus, TMC_AUTHSTATUS); +coresight_simple_reg32(devid, CORESIGHT_DEVID); +coresight_simple_reg64(rrp, TMC_RRP, TMC_RRPHI); +coresight_simple_reg64(rwp, TMC_RWP, TMC_RWPHI); +coresight_simple_reg64(dba, TMC_DBALO, TMC_DBAHI); static struct attribute *coresight_tmc_mgmt_attrs[] = { &dev_attr_rsz.attr, diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 9f445f09fcfe..a47dd1f62216 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -372,6 +372,24 @@ static inline u32 csdev_access_relaxed_read32(struct csdev_access *csa, return csa->read(offset, true, false); } +static inline u64 csdev_access_relaxed_read_pair(struct csdev_access *csa, + s32 lo_offset, s32 hi_offset) +{ + u64 val; + + if (likely(csa->io_mem)) { + val = readl_relaxed(csa->base + lo_offset); + val |= (hi_offset < 0) ? 0 : + (u64)readl_relaxed(csa->base + hi_offset) << 32; + return val; + } + + val = csa->read(lo_offset, true, false); + val |= (hi_offset < 0) ? 0 : + (u64)csa->read(hi_offset, true, false) << 32; + return val; +} + static inline u32 csdev_access_read32(struct csdev_access *csa, u32 offset) { if (likely(csa->io_mem)) -- cgit v1.2.3 From 08e9fa5f3663eaab20ea3430023d1dfbf60d29f5 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 30 Aug 2022 18:26:11 +0100 Subject: coresight: Re-use same function for similar sysfs register accessors Currently each accessor macro creates an identical function which wastes space in the text area and pollutes the ftrace function names. Change it so that the same function is used, but the register to access is passed in as parameter rather than baked into each function. Signed-off-by: James Clark Reviewed-by: Mike Leach Link: https://lore.kernel.org/r/20220830172614.340962-4-james.clark@arm.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-catu.c | 25 ++++--------- drivers/hwtracing/coresight/coresight-core.c | 14 +++++++ drivers/hwtracing/coresight/coresight-etb10.c | 25 ++++--------- .../hwtracing/coresight/coresight-etm3x-sysfs.c | 31 +++++----------- drivers/hwtracing/coresight/coresight-priv.h | 40 +++++++++++--------- drivers/hwtracing/coresight/coresight-replicator.c | 7 +--- drivers/hwtracing/coresight/coresight-stm.c | 37 ++++++------------- drivers/hwtracing/coresight/coresight-tmc-core.c | 43 +++++++--------------- 8 files changed, 91 insertions(+), 131 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-catu.c b/drivers/hwtracing/coresight/coresight-catu.c index 9d89c4054046..bc90a03f478f 100644 --- a/drivers/hwtracing/coresight/coresight-catu.c +++ b/drivers/hwtracing/coresight/coresight-catu.c @@ -365,24 +365,15 @@ static const struct etr_buf_operations etr_catu_buf_ops = { .get_data = catu_get_data_etr_buf, }; -coresight_simple_reg32(devid, CORESIGHT_DEVID); -coresight_simple_reg32(control, CATU_CONTROL); -coresight_simple_reg32(status, CATU_STATUS); -coresight_simple_reg32(mode, CATU_MODE); -coresight_simple_reg32(axictrl, CATU_AXICTRL); -coresight_simple_reg32(irqen, CATU_IRQEN); -coresight_simple_reg64(sladdr, CATU_SLADDRLO, CATU_SLADDRHI); -coresight_simple_reg64(inaddr, CATU_INADDRLO, CATU_INADDRHI); - static struct attribute *catu_mgmt_attrs[] = { - &dev_attr_devid.attr, - &dev_attr_control.attr, - &dev_attr_status.attr, - &dev_attr_mode.attr, - &dev_attr_axictrl.attr, - &dev_attr_irqen.attr, - &dev_attr_sladdr.attr, - &dev_attr_inaddr.attr, + coresight_simple_reg32(devid, CORESIGHT_DEVID), + coresight_simple_reg32(control, CATU_CONTROL), + coresight_simple_reg32(status, CATU_STATUS), + coresight_simple_reg32(mode, CATU_MODE), + coresight_simple_reg32(axictrl, CATU_AXICTRL), + coresight_simple_reg32(irqen, CATU_IRQEN), + coresight_simple_reg64(sladdr, CATU_SLADDRLO, CATU_SLADDRHI), + coresight_simple_reg64(inaddr, CATU_INADDRLO, CATU_INADDRHI), NULL, }; diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 1edfec1e9d18..c63b2167a69f 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -60,6 +60,20 @@ EXPORT_SYMBOL_GPL(coresight_barrier_pkt); static const struct cti_assoc_op *cti_assoc_ops; +ssize_t coresight_simple_show(struct device *_dev, + struct device_attribute *attr, char *buf) +{ + struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev); + struct cs_pair_attribute *cs_attr = container_of(attr, struct cs_pair_attribute, attr); + u64 val; + + pm_runtime_get_sync(_dev->parent); + val = csdev_access_relaxed_read_pair(&csdev->access, cs_attr->lo_off, cs_attr->hi_off); + pm_runtime_put_sync(_dev->parent); + return sysfs_emit(buf, "0x%llx\n", val); +} +EXPORT_SYMBOL_GPL(coresight_simple_show); + void coresight_set_cti_ops(const struct cti_assoc_op *cti_op) { cti_assoc_ops = cti_op; diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 405bb3355cb1..8aa6e4f83e42 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -655,24 +655,15 @@ static const struct file_operations etb_fops = { .llseek = no_llseek, }; -coresight_simple_reg32(rdp, ETB_RAM_DEPTH_REG); -coresight_simple_reg32(sts, ETB_STATUS_REG); -coresight_simple_reg32(rrp, ETB_RAM_READ_POINTER); -coresight_simple_reg32(rwp, ETB_RAM_WRITE_POINTER); -coresight_simple_reg32(trg, ETB_TRG); -coresight_simple_reg32(ctl, ETB_CTL_REG); -coresight_simple_reg32(ffsr, ETB_FFSR); -coresight_simple_reg32(ffcr, ETB_FFCR); - static struct attribute *coresight_etb_mgmt_attrs[] = { - &dev_attr_rdp.attr, - &dev_attr_sts.attr, - &dev_attr_rrp.attr, - &dev_attr_rwp.attr, - &dev_attr_trg.attr, - &dev_attr_ctl.attr, - &dev_attr_ffsr.attr, - &dev_attr_ffcr.attr, + coresight_simple_reg32(rdp, ETB_RAM_DEPTH_REG), + coresight_simple_reg32(sts, ETB_STATUS_REG), + coresight_simple_reg32(rrp, ETB_RAM_READ_POINTER), + coresight_simple_reg32(rwp, ETB_RAM_WRITE_POINTER), + coresight_simple_reg32(trg, ETB_TRG), + coresight_simple_reg32(ctl, ETB_CTL_REG), + coresight_simple_reg32(ffsr, ETB_FFSR), + coresight_simple_reg32(ffcr, ETB_FFCR), NULL, }; diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index 12f8e8176c7e..fd81eca3ec18 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -1252,28 +1252,17 @@ static struct attribute *coresight_etm_attrs[] = { NULL, }; -coresight_simple_reg32(etmccr, ETMCCR); -coresight_simple_reg32(etmccer, ETMCCER); -coresight_simple_reg32(etmscr, ETMSCR); -coresight_simple_reg32(etmidr, ETMIDR); -coresight_simple_reg32(etmcr, ETMCR); -coresight_simple_reg32(etmtraceidr, ETMTRACEIDR); -coresight_simple_reg32(etmteevr, ETMTEEVR); -coresight_simple_reg32(etmtssvr, ETMTSSCR); -coresight_simple_reg32(etmtecr1, ETMTECR1); -coresight_simple_reg32(etmtecr2, ETMTECR2); - static struct attribute *coresight_etm_mgmt_attrs[] = { - &dev_attr_etmccr.attr, - &dev_attr_etmccer.attr, - &dev_attr_etmscr.attr, - &dev_attr_etmidr.attr, - &dev_attr_etmcr.attr, - &dev_attr_etmtraceidr.attr, - &dev_attr_etmteevr.attr, - &dev_attr_etmtssvr.attr, - &dev_attr_etmtecr1.attr, - &dev_attr_etmtecr2.attr, + coresight_simple_reg32(etmccr, ETMCCR), + coresight_simple_reg32(etmccer, ETMCCER), + coresight_simple_reg32(etmscr, ETMSCR), + coresight_simple_reg32(etmidr, ETMIDR), + coresight_simple_reg32(etmcr, ETMCR), + coresight_simple_reg32(etmtraceidr, ETMTRACEIDR), + coresight_simple_reg32(etmteevr, ETMTEEVR), + coresight_simple_reg32(etmtssvr, ETMTSSCR), + coresight_simple_reg32(etmtecr1, ETMTECR1), + coresight_simple_reg32(etmtecr2, ETMTECR2), NULL, }; diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index cf8ae768106e..07b392bfdbcd 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -39,24 +39,30 @@ #define ETM_MODE_EXCL_KERN BIT(30) #define ETM_MODE_EXCL_USER BIT(31) +struct cs_pair_attribute { + struct device_attribute attr; + s32 lo_off; + s32 hi_off; +}; -#define __coresight_simple_show(name, lo_off, hi_off) \ -static ssize_t name##_show(struct device *_dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev); \ - u64 val; \ - pm_runtime_get_sync(_dev->parent); \ - val = csdev_access_relaxed_read_pair(&csdev->access, lo_off, hi_off); \ - pm_runtime_put_sync(_dev->parent); \ - return scnprintf(buf, PAGE_SIZE, "0x%llx\n", val); \ -} \ -static DEVICE_ATTR_RO(name) - -#define coresight_simple_reg32(name, offset) \ - __coresight_simple_show(name, offset, -1) -#define coresight_simple_reg64(name, lo_off, hi_off) \ - __coresight_simple_show(name, lo_off, hi_off) +extern ssize_t coresight_simple_show(struct device *_dev, + struct device_attribute *attr, char *buf); + +#define coresight_simple_reg32(name, offset) \ + (&((struct cs_pair_attribute[]) { \ + { \ + __ATTR(name, 0444, coresight_simple_show, NULL), \ + offset, -1 \ + } \ + })[0].attr.attr) + +#define coresight_simple_reg64(name, lo_off, hi_off) \ + (&((struct cs_pair_attribute[]) { \ + { \ + __ATTR(name, 0444, coresight_simple_show, NULL), \ + lo_off, hi_off \ + } \ + })[0].attr.attr) extern const u32 coresight_barrier_pkt[4]; #define CORESIGHT_BARRIER_PKT_SIZE (sizeof(coresight_barrier_pkt)) diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index 7cffcbb2ec42..4dd50546d7e4 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -196,12 +196,9 @@ static const struct coresight_ops replicator_cs_ops = { .link_ops = &replicator_link_ops, }; -coresight_simple_reg32(idfilter0, REPLICATOR_IDFILTER0); -coresight_simple_reg32(idfilter1, REPLICATOR_IDFILTER1); - static struct attribute *replicator_mgmt_attrs[] = { - &dev_attr_idfilter0.attr, - &dev_attr_idfilter1.attr, + coresight_simple_reg32(idfilter0, REPLICATOR_IDFILTER0), + coresight_simple_reg32(idfilter1, REPLICATOR_IDFILTER1), NULL, }; diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index 4a31905604fe..463f449cfb79 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -634,19 +634,6 @@ static ssize_t traceid_store(struct device *dev, } static DEVICE_ATTR_RW(traceid); -coresight_simple_reg32(tcsr, STMTCSR); -coresight_simple_reg32(tsfreqr, STMTSFREQR); -coresight_simple_reg32(syncr, STMSYNCR); -coresight_simple_reg32(sper, STMSPER); -coresight_simple_reg32(spter, STMSPTER); -coresight_simple_reg32(privmaskr, STMPRIVMASKR); -coresight_simple_reg32(spscr, STMSPSCR); -coresight_simple_reg32(spmscr, STMSPMSCR); -coresight_simple_reg32(spfeat1r, STMSPFEAT1R); -coresight_simple_reg32(spfeat2r, STMSPFEAT2R); -coresight_simple_reg32(spfeat3r, STMSPFEAT3R); -coresight_simple_reg32(devid, CORESIGHT_DEVID); - static struct attribute *coresight_stm_attrs[] = { &dev_attr_hwevent_enable.attr, &dev_attr_hwevent_select.attr, @@ -657,18 +644,18 @@ static struct attribute *coresight_stm_attrs[] = { }; static struct attribute *coresight_stm_mgmt_attrs[] = { - &dev_attr_tcsr.attr, - &dev_attr_tsfreqr.attr, - &dev_attr_syncr.attr, - &dev_attr_sper.attr, - &dev_attr_spter.attr, - &dev_attr_privmaskr.attr, - &dev_attr_spscr.attr, - &dev_attr_spmscr.attr, - &dev_attr_spfeat1r.attr, - &dev_attr_spfeat2r.attr, - &dev_attr_spfeat3r.attr, - &dev_attr_devid.attr, + coresight_simple_reg32(tcsr, STMTCSR), + coresight_simple_reg32(tsfreqr, STMTSFREQR), + coresight_simple_reg32(syncr, STMSYNCR), + coresight_simple_reg32(sper, STMSPER), + coresight_simple_reg32(spter, STMSPTER), + coresight_simple_reg32(privmaskr, STMPRIVMASKR), + coresight_simple_reg32(spscr, STMSPSCR), + coresight_simple_reg32(spmscr, STMSPMSCR), + coresight_simple_reg32(spfeat1r, STMSPFEAT1R), + coresight_simple_reg32(spfeat2r, STMSPFEAT2R), + coresight_simple_reg32(spfeat3r, STMSPFEAT3R), + coresight_simple_reg32(devid, CORESIGHT_DEVID), NULL, }; diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c index 781d213526b7..07abf28ad725 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-core.c +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c @@ -251,36 +251,21 @@ static enum tmc_mem_intf_width tmc_get_memwidth(u32 devid) return memwidth; } -coresight_simple_reg32(rsz, TMC_RSZ); -coresight_simple_reg32(sts, TMC_STS); -coresight_simple_reg32(trg, TMC_TRG); -coresight_simple_reg32(ctl, TMC_CTL); -coresight_simple_reg32(ffsr, TMC_FFSR); -coresight_simple_reg32(ffcr, TMC_FFCR); -coresight_simple_reg32(mode, TMC_MODE); -coresight_simple_reg32(pscr, TMC_PSCR); -coresight_simple_reg32(axictl, TMC_AXICTL); -coresight_simple_reg32(authstatus, TMC_AUTHSTATUS); -coresight_simple_reg32(devid, CORESIGHT_DEVID); -coresight_simple_reg64(rrp, TMC_RRP, TMC_RRPHI); -coresight_simple_reg64(rwp, TMC_RWP, TMC_RWPHI); -coresight_simple_reg64(dba, TMC_DBALO, TMC_DBAHI); - static struct attribute *coresight_tmc_mgmt_attrs[] = { - &dev_attr_rsz.attr, - &dev_attr_sts.attr, - &dev_attr_rrp.attr, - &dev_attr_rwp.attr, - &dev_attr_trg.attr, - &dev_attr_ctl.attr, - &dev_attr_ffsr.attr, - &dev_attr_ffcr.attr, - &dev_attr_mode.attr, - &dev_attr_pscr.attr, - &dev_attr_devid.attr, - &dev_attr_dba.attr, - &dev_attr_axictl.attr, - &dev_attr_authstatus.attr, + coresight_simple_reg32(rsz, TMC_RSZ), + coresight_simple_reg32(sts, TMC_STS), + coresight_simple_reg64(rrp, TMC_RRP, TMC_RRPHI), + coresight_simple_reg64(rwp, TMC_RWP, TMC_RWPHI), + coresight_simple_reg32(trg, TMC_TRG), + coresight_simple_reg32(ctl, TMC_CTL), + coresight_simple_reg32(ffsr, TMC_FFSR), + coresight_simple_reg32(ffcr, TMC_FFCR), + coresight_simple_reg32(mode, TMC_MODE), + coresight_simple_reg32(pscr, TMC_PSCR), + coresight_simple_reg32(devid, CORESIGHT_DEVID), + coresight_simple_reg64(dba, TMC_DBALO, TMC_DBAHI), + coresight_simple_reg32(axictl, TMC_AXICTL), + coresight_simple_reg32(authstatus, TMC_AUTHSTATUS), NULL, }; -- cgit v1.2.3 From fbca79e55429fe0f73b2d2d190fd86bfda52c41b Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 30 Aug 2022 18:26:12 +0100 Subject: coresight: cti-sysfs: Re-use same functions for similar sysfs register accessors Currently each accessor macro creates an identical function which wastes space in the text area and pollutes the ftrace function name list. Change it so that the same function is used, but the register to access is passed in as parameter rather than baked into each function. Note that only the single accessor is used here and not csdev_access_relaxed_read_pair() like in the previous commit, so so a single unsigned offset value is stored instead. Signed-off-by: James Clark Reviewed-by: Mike Leach Link: https://lore.kernel.org/r/20220830172614.340962-5-james.clark@arm.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-cti-sysfs.c | 213 +++++++++------------- drivers/hwtracing/coresight/coresight-priv.h | 5 + 2 files changed, 91 insertions(+), 127 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index 7ff7e7780bbf..478b8d38b744 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -163,48 +163,82 @@ static struct attribute *coresight_cti_attrs[] = { /* register based attributes */ -/* macro to access RO registers with power check only (no enable check). */ -#define coresight_cti_reg(name, offset) \ -static ssize_t name##_show(struct device *dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); \ - u32 val = 0; \ - pm_runtime_get_sync(dev->parent); \ - spin_lock(&drvdata->spinlock); \ - if (drvdata->config.hw_powered) \ - val = readl_relaxed(drvdata->base + offset); \ - spin_unlock(&drvdata->spinlock); \ - pm_runtime_put_sync(dev->parent); \ - return sprintf(buf, "0x%x\n", val); \ -} \ -static DEVICE_ATTR_RO(name) +/* Read registers with power check only (no enable check). */ +static ssize_t coresight_cti_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cs_off_attribute *cti_attr = container_of(attr, struct cs_off_attribute, attr); + u32 val = 0; -/* coresight management registers */ -coresight_cti_reg(devaff0, CTIDEVAFF0); -coresight_cti_reg(devaff1, CTIDEVAFF1); -coresight_cti_reg(authstatus, CORESIGHT_AUTHSTATUS); -coresight_cti_reg(devarch, CORESIGHT_DEVARCH); -coresight_cti_reg(devid, CORESIGHT_DEVID); -coresight_cti_reg(devtype, CORESIGHT_DEVTYPE); -coresight_cti_reg(pidr0, CORESIGHT_PERIPHIDR0); -coresight_cti_reg(pidr1, CORESIGHT_PERIPHIDR1); -coresight_cti_reg(pidr2, CORESIGHT_PERIPHIDR2); -coresight_cti_reg(pidr3, CORESIGHT_PERIPHIDR3); -coresight_cti_reg(pidr4, CORESIGHT_PERIPHIDR4); + pm_runtime_get_sync(dev->parent); + spin_lock(&drvdata->spinlock); + if (drvdata->config.hw_powered) + val = readl_relaxed(drvdata->base + cti_attr->off); + spin_unlock(&drvdata->spinlock); + pm_runtime_put_sync(dev->parent); + return sysfs_emit(buf, "0x%x\n", val); +} + +/* Write registers with power check only (no enable check). */ +static ssize_t coresight_cti_reg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cs_off_attribute *cti_attr = container_of(attr, struct cs_off_attribute, attr); + unsigned long val = 0; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + pm_runtime_get_sync(dev->parent); + spin_lock(&drvdata->spinlock); + if (drvdata->config.hw_powered) + cti_write_single_reg(drvdata, cti_attr->off, val); + spin_unlock(&drvdata->spinlock); + pm_runtime_put_sync(dev->parent); + return size; +} + +#define coresight_cti_reg(name, offset) \ + (&((struct cs_off_attribute[]) { \ + { \ + __ATTR(name, 0444, coresight_cti_reg_show, NULL), \ + offset \ + } \ + })[0].attr.attr) + +#define coresight_cti_reg_rw(name, offset) \ + (&((struct cs_off_attribute[]) { \ + { \ + __ATTR(name, 0644, coresight_cti_reg_show, \ + coresight_cti_reg_store), \ + offset \ + } \ + })[0].attr.attr) + +#define coresight_cti_reg_wo(name, offset) \ + (&((struct cs_off_attribute[]) { \ + { \ + __ATTR(name, 0200, NULL, coresight_cti_reg_store), \ + offset \ + } \ + })[0].attr.attr) + +/* coresight management registers */ static struct attribute *coresight_cti_mgmt_attrs[] = { - &dev_attr_devaff0.attr, - &dev_attr_devaff1.attr, - &dev_attr_authstatus.attr, - &dev_attr_devarch.attr, - &dev_attr_devid.attr, - &dev_attr_devtype.attr, - &dev_attr_pidr0.attr, - &dev_attr_pidr1.attr, - &dev_attr_pidr2.attr, - &dev_attr_pidr3.attr, - &dev_attr_pidr4.attr, + coresight_cti_reg(devaff0, CTIDEVAFF0), + coresight_cti_reg(devaff1, CTIDEVAFF1), + coresight_cti_reg(authstatus, CORESIGHT_AUTHSTATUS), + coresight_cti_reg(devarch, CORESIGHT_DEVARCH), + coresight_cti_reg(devid, CORESIGHT_DEVID), + coresight_cti_reg(devtype, CORESIGHT_DEVTYPE), + coresight_cti_reg(pidr0, CORESIGHT_PERIPHIDR0), + coresight_cti_reg(pidr1, CORESIGHT_PERIPHIDR1), + coresight_cti_reg(pidr2, CORESIGHT_PERIPHIDR2), + coresight_cti_reg(pidr3, CORESIGHT_PERIPHIDR3), + coresight_cti_reg(pidr4, CORESIGHT_PERIPHIDR4), NULL, }; @@ -454,86 +488,11 @@ static ssize_t apppulse_store(struct device *dev, } static DEVICE_ATTR_WO(apppulse); -coresight_cti_reg(triginstatus, CTITRIGINSTATUS); -coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS); -coresight_cti_reg(chinstatus, CTICHINSTATUS); -coresight_cti_reg(choutstatus, CTICHOUTSTATUS); - /* * Define CONFIG_CORESIGHT_CTI_INTEGRATION_REGS to enable the access to the * integration control registers. Normally only used to investigate connection * data. */ -#ifdef CONFIG_CORESIGHT_CTI_INTEGRATION_REGS - -/* macro to access RW registers with power check only (no enable check). */ -#define coresight_cti_reg_rw(name, offset) \ -static ssize_t name##_show(struct device *dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); \ - u32 val = 0; \ - pm_runtime_get_sync(dev->parent); \ - spin_lock(&drvdata->spinlock); \ - if (drvdata->config.hw_powered) \ - val = readl_relaxed(drvdata->base + offset); \ - spin_unlock(&drvdata->spinlock); \ - pm_runtime_put_sync(dev->parent); \ - return sprintf(buf, "0x%x\n", val); \ -} \ - \ -static ssize_t name##_store(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t size) \ -{ \ - struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); \ - unsigned long val = 0; \ - if (kstrtoul(buf, 0, &val)) \ - return -EINVAL; \ - \ - pm_runtime_get_sync(dev->parent); \ - spin_lock(&drvdata->spinlock); \ - if (drvdata->config.hw_powered) \ - cti_write_single_reg(drvdata, offset, val); \ - spin_unlock(&drvdata->spinlock); \ - pm_runtime_put_sync(dev->parent); \ - return size; \ -} \ -static DEVICE_ATTR_RW(name) - -/* macro to access WO registers with power check only (no enable check). */ -#define coresight_cti_reg_wo(name, offset) \ -static ssize_t name##_store(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t size) \ -{ \ - struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); \ - unsigned long val = 0; \ - if (kstrtoul(buf, 0, &val)) \ - return -EINVAL; \ - \ - pm_runtime_get_sync(dev->parent); \ - spin_lock(&drvdata->spinlock); \ - if (drvdata->config.hw_powered) \ - cti_write_single_reg(drvdata, offset, val); \ - spin_unlock(&drvdata->spinlock); \ - pm_runtime_put_sync(dev->parent); \ - return size; \ -} \ -static DEVICE_ATTR_WO(name) - -coresight_cti_reg_rw(itchout, ITCHOUT); -coresight_cti_reg_rw(ittrigout, ITTRIGOUT); -coresight_cti_reg_rw(itctrl, CORESIGHT_ITCTRL); -coresight_cti_reg_wo(itchinack, ITCHINACK); -coresight_cti_reg_wo(ittriginack, ITTRIGINACK); -coresight_cti_reg(ittrigin, ITTRIGIN); -coresight_cti_reg(itchin, ITCHIN); -coresight_cti_reg(itchoutack, ITCHOUTACK); -coresight_cti_reg(ittrigoutack, ITTRIGOUTACK); - -#endif /* CORESIGHT_CTI_INTEGRATION_REGS */ - static struct attribute *coresight_cti_regs_attrs[] = { &dev_attr_inout_sel.attr, &dev_attr_inen.attr, @@ -544,20 +503,20 @@ static struct attribute *coresight_cti_regs_attrs[] = { &dev_attr_appset.attr, &dev_attr_appclear.attr, &dev_attr_apppulse.attr, - &dev_attr_triginstatus.attr, - &dev_attr_trigoutstatus.attr, - &dev_attr_chinstatus.attr, - &dev_attr_choutstatus.attr, + coresight_cti_reg(triginstatus, CTITRIGINSTATUS), + coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS), + coresight_cti_reg(chinstatus, CTICHINSTATUS), + coresight_cti_reg(choutstatus, CTICHOUTSTATUS), #ifdef CONFIG_CORESIGHT_CTI_INTEGRATION_REGS - &dev_attr_itctrl.attr, - &dev_attr_ittrigin.attr, - &dev_attr_itchin.attr, - &dev_attr_ittrigout.attr, - &dev_attr_itchout.attr, - &dev_attr_itchoutack.attr, - &dev_attr_ittrigoutack.attr, - &dev_attr_ittriginack.attr, - &dev_attr_itchinack.attr, + coresight_cti_reg_rw(itctrl, CORESIGHT_ITCTRL), + coresight_cti_reg(ittrigin, ITTRIGIN), + coresight_cti_reg(itchin, ITCHIN), + coresight_cti_reg_rw(ittrigout, ITTRIGOUT), + coresight_cti_reg_rw(itchout, ITCHOUT), + coresight_cti_reg(itchoutack, ITCHOUTACK), + coresight_cti_reg(ittrigoutack, ITTRIGOUTACK), + coresight_cti_reg_wo(ittriginack, ITTRIGINACK), + coresight_cti_reg_wo(itchinack, ITCHINACK), #endif NULL, }; diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 07b392bfdbcd..c211979deca5 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -45,6 +45,11 @@ struct cs_pair_attribute { s32 hi_off; }; +struct cs_off_attribute { + struct device_attribute attr; + u32 off; +}; + extern ssize_t coresight_simple_show(struct device *_dev, struct device_attribute *attr, char *buf); -- cgit v1.2.3 From 0a98181f805058773961c5ab3172ecf1bf1ed0e1 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 30 Aug 2022 18:26:13 +0100 Subject: coresight: Make new csdev_access offsets unsigned New csdev_access functions were added as part of the previous refactor. In order to make them more consistent with the existing ones, change any signed offset types to be unsigned. Now that they are unsigned, stop using hi_off = -1 to signify a single 32bit access. Instead just call the existing 32bit accessors. This is also applied to other parts of the codebase, and the coresight_{read,write}_reg_pair() functions can be deleted. Signed-off-by: James Clark Reviewed-by: Mike Leach Link: https://lore.kernel.org/r/20220830172614.340962-6-james.clark@arm.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-catu.h | 8 +++---- drivers/hwtracing/coresight/coresight-core.c | 18 ++++++++++++-- drivers/hwtracing/coresight/coresight-priv.h | 35 +++++++--------------------- drivers/hwtracing/coresight/coresight-tmc.h | 4 ++-- include/linux/coresight.h | 27 ++++++++++++--------- 5 files changed, 47 insertions(+), 45 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-catu.h b/drivers/hwtracing/coresight/coresight-catu.h index 6160c2d75a56..442e034bbfba 100644 --- a/drivers/hwtracing/coresight/coresight-catu.h +++ b/drivers/hwtracing/coresight/coresight-catu.h @@ -70,24 +70,24 @@ struct catu_drvdata { static inline u32 \ catu_read_##name(struct catu_drvdata *drvdata) \ { \ - return coresight_read_reg_pair(drvdata->base, offset, -1); \ + return csdev_access_relaxed_read32(&drvdata->csdev->access, offset); \ } \ static inline void \ catu_write_##name(struct catu_drvdata *drvdata, u32 val) \ { \ - coresight_write_reg_pair(drvdata->base, val, offset, -1); \ + csdev_access_relaxed_write32(&drvdata->csdev->access, val, offset); \ } #define CATU_REG_PAIR(name, lo_off, hi_off) \ static inline u64 \ catu_read_##name(struct catu_drvdata *drvdata) \ { \ - return coresight_read_reg_pair(drvdata->base, lo_off, hi_off); \ + return csdev_access_relaxed_read_pair(&drvdata->csdev->access, lo_off, hi_off); \ } \ static inline void \ catu_write_##name(struct catu_drvdata *drvdata, u64 val) \ { \ - coresight_write_reg_pair(drvdata->base, val, lo_off, hi_off); \ + csdev_access_relaxed_write_pair(&drvdata->csdev->access, val, lo_off, hi_off); \ } CATU_REG32(control, CATU_CONTROL); diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index c63b2167a69f..d5dbc67bacb4 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -60,7 +60,7 @@ EXPORT_SYMBOL_GPL(coresight_barrier_pkt); static const struct cti_assoc_op *cti_assoc_ops; -ssize_t coresight_simple_show(struct device *_dev, +ssize_t coresight_simple_show_pair(struct device *_dev, struct device_attribute *attr, char *buf) { struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev); @@ -72,7 +72,21 @@ ssize_t coresight_simple_show(struct device *_dev, pm_runtime_put_sync(_dev->parent); return sysfs_emit(buf, "0x%llx\n", val); } -EXPORT_SYMBOL_GPL(coresight_simple_show); +EXPORT_SYMBOL_GPL(coresight_simple_show_pair); + +ssize_t coresight_simple_show32(struct device *_dev, + struct device_attribute *attr, char *buf) +{ + struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev); + struct cs_off_attribute *cs_attr = container_of(attr, struct cs_off_attribute, attr); + u64 val; + + pm_runtime_get_sync(_dev->parent); + val = csdev_access_relaxed_read32(&csdev->access, cs_attr->off); + pm_runtime_put_sync(_dev->parent); + return sysfs_emit(buf, "0x%llx\n", val); +} +EXPORT_SYMBOL_GPL(coresight_simple_show32); void coresight_set_cti_ops(const struct cti_assoc_op *cti_op) { diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index c211979deca5..595ce5862056 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -41,8 +41,8 @@ #define ETM_MODE_EXCL_USER BIT(31) struct cs_pair_attribute { struct device_attribute attr; - s32 lo_off; - s32 hi_off; + u32 lo_off; + u32 hi_off; }; struct cs_off_attribute { @@ -50,21 +50,23 @@ struct cs_off_attribute { u32 off; }; -extern ssize_t coresight_simple_show(struct device *_dev, +extern ssize_t coresight_simple_show32(struct device *_dev, + struct device_attribute *attr, char *buf); +extern ssize_t coresight_simple_show_pair(struct device *_dev, struct device_attribute *attr, char *buf); #define coresight_simple_reg32(name, offset) \ - (&((struct cs_pair_attribute[]) { \ + (&((struct cs_off_attribute[]) { \ { \ - __ATTR(name, 0444, coresight_simple_show, NULL), \ - offset, -1 \ + __ATTR(name, 0444, coresight_simple_show32, NULL), \ + offset \ } \ })[0].attr.attr) #define coresight_simple_reg64(name, lo_off, hi_off) \ (&((struct cs_pair_attribute[]) { \ { \ - __ATTR(name, 0444, coresight_simple_show, NULL), \ + __ATTR(name, 0444, coresight_simple_show_pair, NULL), \ lo_off, hi_off \ } \ })[0].attr.attr) @@ -130,25 +132,6 @@ static inline void CS_UNLOCK(void __iomem *addr) } while (0); } -static inline u64 -coresight_read_reg_pair(void __iomem *addr, s32 lo_offset, s32 hi_offset) -{ - u64 val; - - val = readl_relaxed(addr + lo_offset); - val |= (hi_offset < 0) ? 0 : - (u64)readl_relaxed(addr + hi_offset) << 32; - return val; -} - -static inline void coresight_write_reg_pair(void __iomem *addr, u64 val, - s32 lo_offset, s32 hi_offset) -{ - writel_relaxed((u32)val, addr + lo_offset); - if (hi_offset >= 0) - writel_relaxed((u32)(val >> 32), addr + hi_offset); -} - void coresight_disable_path(struct list_head *path); int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data); struct coresight_device *coresight_get_sink(struct list_head *path); diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 6bec20a392b3..66959557cf39 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -282,12 +282,12 @@ ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata, static inline u64 \ tmc_read_##name(struct tmc_drvdata *drvdata) \ { \ - return coresight_read_reg_pair(drvdata->base, lo_off, hi_off); \ + return csdev_access_relaxed_read_pair(&drvdata->csdev->access, lo_off, hi_off); \ } \ static inline void \ tmc_write_##name(struct tmc_drvdata *drvdata, u64 val) \ { \ - coresight_write_reg_pair(drvdata->base, val, lo_off, hi_off); \ + csdev_access_relaxed_write_pair(&drvdata->csdev->access, val, lo_off, hi_off); \ } TMC_REG_PAIR(rrp, TMC_RRP, TMC_RRPHI) diff --git a/include/linux/coresight.h b/include/linux/coresight.h index a47dd1f62216..1554021231f9 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -373,21 +373,26 @@ static inline u32 csdev_access_relaxed_read32(struct csdev_access *csa, } static inline u64 csdev_access_relaxed_read_pair(struct csdev_access *csa, - s32 lo_offset, s32 hi_offset) + u32 lo_offset, u32 hi_offset) { - u64 val; - if (likely(csa->io_mem)) { - val = readl_relaxed(csa->base + lo_offset); - val |= (hi_offset < 0) ? 0 : - (u64)readl_relaxed(csa->base + hi_offset) << 32; - return val; + return readl_relaxed(csa->base + lo_offset) | + ((u64)readl_relaxed(csa->base + hi_offset) << 32); } - val = csa->read(lo_offset, true, false); - val |= (hi_offset < 0) ? 0 : - (u64)csa->read(hi_offset, true, false) << 32; - return val; + return csa->read(lo_offset, true, false) | (csa->read(hi_offset, true, false) << 32); +} + +static inline void csdev_access_relaxed_write_pair(struct csdev_access *csa, u64 val, + u32 lo_offset, u32 hi_offset) +{ + if (likely(csa->io_mem)) { + writel_relaxed((u32)val, csa->base + lo_offset); + writel_relaxed((u32)(val >> 32), csa->base + hi_offset); + } else { + csa->write((u32)val, lo_offset, true, false); + csa->write((u32)(val >> 32), hi_offset, true, false); + } } static inline u32 csdev_access_read32(struct csdev_access *csa, u32 offset) -- cgit v1.2.3 From 76ff33468beaabbd0fe141b3ea1c6f514a8ef7d4 Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Mon, 29 Aug 2022 20:12:49 +0000 Subject: binder: fix trivial kernel-doc typo Correct the misspelling of 'invariant' in kernel-doc section. No functional changes in this patch. Reviewed-by: Christian Brauner (Microsoft) Acked-by: Todd Kjos Signed-off-by: Carlos Llamas Link: https://lore.kernel.org/r/20220829201254.1814484-3-cmllamas@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder_alloc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h index 1e4fd37af5e0..0c37935ff7a2 100644 --- a/drivers/android/binder_alloc.h +++ b/drivers/android/binder_alloc.h @@ -75,10 +75,10 @@ struct binder_lru_page { /** * struct binder_alloc - per-binder proc state for binder allocator * @vma: vm_area_struct passed to mmap_handler - * (invarient after mmap) + * (invariant after mmap) * @tsk: tid for task that called init for this proc * (invariant after init) - * @vma_vm_mm: copy of vma->vm_mm (invarient after mmap) + * @vma_vm_mm: copy of vma->vm_mm (invariant after mmap) * @buffer: base of per-proc address space mapped via mmap * @buffers: list of all buffers for this proc * @free_buffers: rb tree of buffers available for allocation -- cgit v1.2.3 From 22534a44cb8ca660a14d62e320e45fde962e9410 Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Mon, 29 Aug 2022 20:12:52 +0000 Subject: binder: remove unused binder_alloc->buffer_free The ->buffer_free member was introduced in the first revision of the driver under staging but it appears like it was never actually used according to git's history. Remove it from binder_alloc. Reviewed-by: Christian Brauner (Microsoft) Acked-by: Todd Kjos Signed-off-by: Carlos Llamas Link: https://lore.kernel.org/r/20220829201254.1814484-6-cmllamas@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder_alloc.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h index 0c37935ff7a2..f61a12d5c1e7 100644 --- a/drivers/android/binder_alloc.h +++ b/drivers/android/binder_alloc.h @@ -109,7 +109,6 @@ struct binder_alloc { size_t free_async_space; struct binder_lru_page *pages; size_t buffer_size; - uint32_t buffer_free; int pid; size_t pages_high; bool oneway_spam_detected; -- cgit v1.2.3 From eaf271ea844b8dea5256bd3c73e642ef13ce68a2 Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Mon, 29 Aug 2022 20:12:54 +0000 Subject: binderfs: remove unused INTSTRLEN macro Fix the following W=1 build error: drivers/android/binderfs.c:42: error: macro "INTSTRLEN" is not used [-Werror=unused-macros] 42 | #define INTSTRLEN 21 | No functional changes in this patch. Reviewed-by: Christian Brauner (Microsoft) Signed-off-by: Carlos Llamas Link: https://lore.kernel.org/r/20220829201254.1814484-8-cmllamas@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binderfs.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index 588d753a7a19..44939ea1874f 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -39,7 +39,6 @@ #define FIRST_INODE 1 #define SECOND_INODE 2 #define INODE_OFFSET 3 -#define INTSTRLEN 21 #define BINDERFS_MAX_MINOR (1U << MINORBITS) /* Ensure that the initial ipc namespace always has devices available. */ #define BINDERFS_MAX_MINOR_CAPPED (BINDERFS_MAX_MINOR - 4) -- cgit v1.2.3 From 9d64d2405f7d30d49818f6682acd0392348f0fdb Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 23 Aug 2022 11:53:39 +0200 Subject: binderfs: rework superblock destruction So far we relied on .put_super = binderfs_put_super() to destroy info we stashed in sb->s_fs_info. This gave us the required ordering between ->evict_inode() and sb->s_fs_info destruction. But the current implementation of binderfs_fill_super() has a memory leak in the rare circumstance that d_make_root() fails because ->put_super() is only called when sb->s_root is initialized. Fix this by removing ->put_super() and simply do all that work in binderfs_kill_super(). Reported-by: Dongliang Mu Signed-off-by: Al Viro Signed-off-by: Christian Brauner (Microsoft) Link: https://lore.kernel.org/r/20220823095339.853371-1-brauner@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/android/binderfs.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index 44939ea1874f..09b2ce7e4c34 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -339,22 +339,10 @@ static int binderfs_show_options(struct seq_file *seq, struct dentry *root) return 0; } -static void binderfs_put_super(struct super_block *sb) -{ - struct binderfs_info *info = sb->s_fs_info; - - if (info && info->ipc_ns) - put_ipc_ns(info->ipc_ns); - - kfree(info); - sb->s_fs_info = NULL; -} - static const struct super_operations binderfs_super_ops = { .evict_inode = binderfs_evict_inode, .show_options = binderfs_show_options, .statfs = simple_statfs, - .put_super = binderfs_put_super, }; static inline bool is_binderfs_control_device(const struct dentry *dentry) @@ -784,11 +772,27 @@ static int binderfs_init_fs_context(struct fs_context *fc) return 0; } +static void binderfs_kill_super(struct super_block *sb) +{ + struct binderfs_info *info = sb->s_fs_info; + + /* + * During inode eviction struct binderfs_info is needed. + * So first wipe the super_block then free struct binderfs_info. + */ + kill_litter_super(sb); + + if (info && info->ipc_ns) + put_ipc_ns(info->ipc_ns); + + kfree(info); +} + static struct file_system_type binder_fs_type = { .name = "binder", .init_fs_context = binderfs_init_fs_context, .parameters = binderfs_fs_parameters, - .kill_sb = kill_litter_super, + .kill_sb = binderfs_kill_super, .fs_flags = FS_USERNS_MOUNT, }; -- cgit v1.2.3 From fccf202e01e0e5c9d5f1fdd00009614c04c82818 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Fri, 12 Aug 2022 02:40:11 -0700 Subject: misc: bcm-vk: Specify the minimum number of IRQ vecs During bcm_vk_probe(), pci_alloc_irq_vectors() is called passing the number of IRQ vectors as 1, but, later, check how many IRQ vectors it got, and fails if it is smaller than VK_MSIX_IRQ_MIN_REQ. The most appropriated way to do it is setting the 'min_vecs' param as VK_MSIX_IRQ_MIN_REQ, instead of one. pci_alloc_irq_vectors() should know the requirements when called. The test was done by just loading this module on a machine with a Valkyrie offload engine hardware. Acked-by: Scott Branden Signed-off-by: Breno Leitao Link: https://lore.kernel.org/r/20220812094011.4064729-1-leitao@debian.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/bcm-vk/bcm_vk_dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/bcm-vk/bcm_vk_dev.c b/drivers/misc/bcm-vk/bcm_vk_dev.c index a16b99bdaa13..bedab17884b8 100644 --- a/drivers/misc/bcm-vk/bcm_vk_dev.c +++ b/drivers/misc/bcm-vk/bcm_vk_dev.c @@ -1339,7 +1339,7 @@ static int bcm_vk_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_drvdata(pdev, vk); irq = pci_alloc_irq_vectors(pdev, - 1, + VK_MSIX_IRQ_MIN_REQ, VK_MSIX_IRQ_MAX, PCI_IRQ_MSI | PCI_IRQ_MSIX); -- cgit v1.2.3 From 5cb14f15d79a8edfb9197deb97c7061d55b819ab Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Fri, 12 Aug 2022 02:47:17 -0700 Subject: misc: bcm_vk: Remove usage of deprecated functions ida_simple_get() and ida_simple_remove() functions are deprecated now. These functions were replaced by ida_alloc() and ida_free() respectively. This patch modernize bcm_vk to use the replacement functions. Acked-by: Scott Branden Signed-off-by: Breno Leitao Link: https://lore.kernel.org/r/20220812094717.4097179-1-leitao@debian.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/bcm-vk/bcm_vk_dev.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/misc/bcm-vk/bcm_vk_dev.c b/drivers/misc/bcm-vk/bcm_vk_dev.c index bedab17884b8..d4a96137728d 100644 --- a/drivers/misc/bcm-vk/bcm_vk_dev.c +++ b/drivers/misc/bcm-vk/bcm_vk_dev.c @@ -1401,7 +1401,7 @@ static int bcm_vk_probe(struct pci_dev *pdev, const struct pci_device_id *ent) bcm_vk_tty_set_irq_enabled(vk, i); } - id = ida_simple_get(&bcm_vk_ida, 0, 0, GFP_KERNEL); + id = ida_alloc(&bcm_vk_ida, GFP_KERNEL); if (id < 0) { err = id; dev_err(dev, "unable to get id\n"); @@ -1500,7 +1500,7 @@ err_kfree_name: misc_device->name = NULL; err_ida_remove: - ida_simple_remove(&bcm_vk_ida, id); + ida_free(&bcm_vk_ida, id); err_irq: for (i = 0; i < vk->num_irqs; i++) @@ -1573,7 +1573,7 @@ static void bcm_vk_remove(struct pci_dev *pdev) if (misc_device->name) { misc_deregister(misc_device); kfree(misc_device->name); - ida_simple_remove(&bcm_vk_ida, vk->devid); + ida_free(&bcm_vk_ida, vk->devid); } for (i = 0; i < vk->num_irqs; i++) devm_free_irq(&pdev->dev, pci_irq_vector(pdev, i), vk); -- cgit v1.2.3 From f667f56b2f55881a705e5a67d30d71e5fb75dbe3 Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Tue, 16 Aug 2022 13:55:28 +0300 Subject: misc: fastrpc: Use USER_PD define in fastrpc_get_info_from_dsp There are defines for each type of protection domain now. Use the USER_PD instead of magic value in fastrpc_get_info_from_dsp. Cc: Srinivas Kandagatla Cc: Amol Maheshwari Reviewed-by: Andrew Halaney Reviewed-by: Srinivas Kandagatla Signed-off-by: Abel Vesa Link: https://lore.kernel.org/r/20220816105528.3222763-1-abel.vesa@linaro.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 93ebd174d848..8895ca593911 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -1515,7 +1515,7 @@ static int fastrpc_get_info_from_dsp(struct fastrpc_user *fl, uint32_t *dsp_attr args[1].ptr = (u64)(uintptr_t)&dsp_attr_buf[1]; args[1].length = dsp_attr_buf_len; args[1].fd = -1; - fl->pd = 1; + fl->pd = USER_PD; return fastrpc_internal_invoke(fl, true, FASTRPC_DSP_UTILITIES_HANDLE, FASTRPC_SCALARS(0, 1, 1), args); -- cgit v1.2.3 From 5192e395c5cfa6f3d23165db38365ec7f8c5ee7a Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 18 Aug 2022 23:00:31 +0200 Subject: misc: move from strlcpy with unused retval to strscpy Follow the advice of the below link and prefer 'strscpy' in this subsystem. Conversion is 1:1 because the return value is not used. Generated by a coccinelle script. Link: https://lore.kernel.org/r/CAHk-=wgfRnXz0W3D37d01q3JFkr_i_uTL=V6A6G1oUZcprmknw@mail.gmail.com/ Signed-off-by: Wolfram Sang Link: https://lore.kernel.org/r/20220818210031.7036-1-wsa+renesas@sang-engineering.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/altera-stapl/altera.c | 8 ++++---- drivers/misc/eeprom/eeprom.c | 2 +- drivers/misc/eeprom/idt_89hpesx.c | 2 +- drivers/misc/ics932s401.c | 2 +- drivers/misc/mei/bus-fixup.c | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/misc/altera-stapl/altera.c b/drivers/misc/altera-stapl/altera.c index 075f3a36d512..a58b7cb81d98 100644 --- a/drivers/misc/altera-stapl/altera.c +++ b/drivers/misc/altera-stapl/altera.c @@ -1014,7 +1014,7 @@ exit_done: * ...argument 0 is string ID */ count = strlen(msg_buff); - strlcpy(&msg_buff[count], + strscpy(&msg_buff[count], &p[str_table + args[0]], ALTERA_MESSAGE_LENGTH - count); break; @@ -2146,7 +2146,7 @@ static int altera_get_note(u8 *p, s32 program_size, s32 *offset, &p[note_table + (8 * i) + 4])]; if (value != NULL) - strlcpy(value, value_ptr, vallen); + strscpy(value, value_ptr, vallen); } } @@ -2162,13 +2162,13 @@ static int altera_get_note(u8 *p, s32 program_size, s32 *offset, status = 0; if (key != NULL) - strlcpy(key, &p[note_strings + + strscpy(key, &p[note_strings + get_unaligned_be32( &p[note_table + (8 * i)])], keylen); if (value != NULL) - strlcpy(value, &p[note_strings + + strscpy(value, &p[note_strings + get_unaligned_be32( &p[note_table + (8 * i) + 4])], vallen); diff --git a/drivers/misc/eeprom/eeprom.c b/drivers/misc/eeprom/eeprom.c index 34fa385dfd4b..adb08ae5062d 100644 --- a/drivers/misc/eeprom/eeprom.c +++ b/drivers/misc/eeprom/eeprom.c @@ -136,7 +136,7 @@ static int eeprom_detect(struct i2c_client *client, struct i2c_board_info *info) && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) return -ENODEV; - strlcpy(info->type, "eeprom", I2C_NAME_SIZE); + strscpy(info->type, "eeprom", I2C_NAME_SIZE); return 0; } diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c index 9aec3338e37d..01614fec40fe 100644 --- a/drivers/misc/eeprom/idt_89hpesx.c +++ b/drivers/misc/eeprom/idt_89hpesx.c @@ -1075,7 +1075,7 @@ static const struct i2c_device_id *idt_ee_match_id(struct fwnode_handle *fwnode) return NULL; p = strchr(compatible, ','); - strlcpy(devname, p ? p + 1 : compatible, sizeof(devname)); + strscpy(devname, p ? p + 1 : compatible, sizeof(devname)); /* Search through the device name */ while (id->name[0]) { if (strcmp(devname, id->name) == 0) diff --git a/drivers/misc/ics932s401.c b/drivers/misc/ics932s401.c index 0f9ea75b0b18..d88fe136155b 100644 --- a/drivers/misc/ics932s401.c +++ b/drivers/misc/ics932s401.c @@ -424,7 +424,7 @@ static int ics932s401_detect(struct i2c_client *client, if (revision != ICS932S401_REV) dev_info(&adapter->dev, "Unknown revision %d\n", revision); - strlcpy(info->type, "ics932s401", I2C_NAME_SIZE); + strscpy(info->type, "ics932s401", I2C_NAME_SIZE); return 0; } diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index 59506ba6fc48..4cb341fcf543 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -471,7 +471,7 @@ static void mei_nfc(struct mei_cl_device *cldev) } dev_dbg(bus->dev, "nfc radio %s\n", radio_name); - strlcpy(cldev->name, radio_name, sizeof(cldev->name)); + strscpy(cldev->name, radio_name, sizeof(cldev->name)); disconnect: mutex_lock(&bus->device_lock); -- cgit v1.2.3 From d8baf6ca8c19ea2053be81750dd4adf8e6e3b4c3 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 18 Aug 2022 23:01:20 +0200 Subject: virt: move from strlcpy with unused retval to strscpy Follow the advice of the below link and prefer 'strscpy' in this subsystem. Conversion is 1:1 because the return value is not used. Generated by a coccinelle script. Link: https://lore.kernel.org/r/CAHk-=wgfRnXz0W3D37d01q3JFkr_i_uTL=V6A6G1oUZcprmknw@mail.gmail.com/ Reviewed-by: Hans de Goede Signed-off-by: Wolfram Sang Link: https://lore.kernel.org/r/20220818210120.7565-1-wsa+renesas@sang-engineering.com Signed-off-by: Greg Kroah-Hartman --- drivers/virt/vboxguest/vboxguest_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/virt/vboxguest/vboxguest_core.c b/drivers/virt/vboxguest/vboxguest_core.c index 0b43efddea22..dfd69bd77f53 100644 --- a/drivers/virt/vboxguest/vboxguest_core.c +++ b/drivers/virt/vboxguest/vboxguest_core.c @@ -198,7 +198,7 @@ static int vbg_report_guest_info(struct vbg_dev *gdev) req2->additions_revision = VBG_SVN_REV; req2->additions_features = VMMDEV_GUEST_INFO2_ADDITIONS_FEATURES_REQUESTOR_INFO; - strlcpy(req2->name, VBG_VERSION_STRING, + strscpy(req2->name, VBG_VERSION_STRING, sizeof(req2->name)); /* -- cgit v1.2.3 From c3b69ba5114c860d730870c03ab4ee45276e5e35 Mon Sep 17 00:00:00 2001 From: Hangyu Hua Date: Wed, 24 Aug 2022 16:26:00 +0800 Subject: misc: ocxl: fix possible refcount leak in afu_ioctl() eventfd_ctx_put need to be called to put the refcount that gotten by eventfd_ctx_fdget when ocxl_irq_set_handler fails. Fixes: 060146614643 ("ocxl: move event_fd handling to frontend") Acked-by: Frederic Barrat Signed-off-by: Hangyu Hua Link: https://lore.kernel.org/r/20220824082600.36159-1-hbh25y@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ocxl/file.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/misc/ocxl/file.c b/drivers/misc/ocxl/file.c index 6777c419a8da..d46dba2df5a1 100644 --- a/drivers/misc/ocxl/file.c +++ b/drivers/misc/ocxl/file.c @@ -257,6 +257,8 @@ static long afu_ioctl(struct file *file, unsigned int cmd, if (IS_ERR(ev_ctx)) return PTR_ERR(ev_ctx); rc = ocxl_irq_set_handler(ctx, irq_id, irq_handler, irq_free, ev_ctx); + if (rc) + eventfd_ctx_put(ev_ctx); break; case OCXL_IOCTL_GET_METADATA: -- cgit v1.2.3 From 621d5d6a83bc92a93c6653fd8a424a63c95269b0 Mon Sep 17 00:00:00 2001 From: Bo Liu Date: Fri, 5 Aug 2022 05:10:57 -0400 Subject: ipack: Check dev_set_name() return value It's possible that dev_set_name() returns -ENOMEM, catch and handle this. Acked-by: Samuel Iglesias Gonsalvez Signed-off-by: Bo Liu Link: https://lore.kernel.org/r/20220805091057.19951-1-liubo03@inspur.com Signed-off-by: Greg Kroah-Hartman --- drivers/ipack/ipack.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/ipack/ipack.c b/drivers/ipack/ipack.c index b1c3198355e7..74d449858a61 100644 --- a/drivers/ipack/ipack.c +++ b/drivers/ipack/ipack.c @@ -429,8 +429,11 @@ int ipack_device_init(struct ipack_device *dev) dev->dev.bus = &ipack_bus_type; dev->dev.release = ipack_device_release; dev->dev.parent = dev->bus->parent; - dev_set_name(&dev->dev, + ret = dev_set_name(&dev->dev, "ipack-dev.%u.%u", dev->bus->bus_nr, dev->slot); + if (ret) + return ret; + device_initialize(&dev->dev); if (dev->bus->ops->set_clockrate(dev, 8)) -- cgit v1.2.3 From 73392920f0e4bb8b04e9debf90cfe75a15fda9e7 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Wed, 24 Aug 2022 00:25:02 +0200 Subject: speakup-dummy: Add support for PUNCT variable This allows to debug the update of the punctuation level. Signed-off-by: Samuel Thibault =================================================================== Link: https://lore.kernel.org/r/20220823222514.929670068@ens-lyon.org Signed-off-by: Greg Kroah-Hartman --- drivers/accessibility/speakup/speakup_dummy.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/accessibility/speakup/speakup_dummy.c b/drivers/accessibility/speakup/speakup_dummy.c index 34f11cd47073..56419dbb28d3 100644 --- a/drivers/accessibility/speakup/speakup_dummy.c +++ b/drivers/accessibility/speakup/speakup_dummy.c @@ -27,6 +27,7 @@ static struct var_t vars[] = { { INFLECTION, .u.n = {"INFLECTION %d\n", 8, 0, 16, 0, 0, NULL } }, { VOL, .u.n = {"VOL %d\n", 8, 0, 16, 0, 0, NULL } }, { TONE, .u.n = {"TONE %d\n", 8, 0, 16, 0, 0, NULL } }, + { PUNCT, .u.n = {"PUNCT %d\n", 0, 0, 3, 0, 0, NULL } }, { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, V_LAST_VAR }; @@ -42,6 +43,8 @@ static struct kobj_attribute pitch_attribute = __ATTR(pitch, 0644, spk_var_show, spk_var_store); static struct kobj_attribute inflection_attribute = __ATTR(inflection, 0644, spk_var_show, spk_var_store); +static struct kobj_attribute punct_attribute = + __ATTR(punct, 0644, spk_var_show, spk_var_store); static struct kobj_attribute rate_attribute = __ATTR(rate, 0644, spk_var_show, spk_var_store); static struct kobj_attribute tone_attribute = @@ -69,6 +72,7 @@ static struct attribute *synth_attrs[] = { &caps_stop_attribute.attr, &pitch_attribute.attr, &inflection_attribute.attr, + &punct_attribute.attr, &rate_attribute.attr, &tone_attribute.attr, &vol_attribute.attr, -- cgit v1.2.3 From 3f132e02d289f9fa208970928a8a8d2d7b1b2b1d Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Wed, 24 Aug 2022 00:25:03 +0200 Subject: speakup: Notify synthesizers of the punctuation level change Even if speakup does the filtering itself, it does not filter out A_PUNC characters (because these are needed e.g. for prosody), so we have to tell synthesizers whether they should filter them out or not. Signed-off-by: Samuel Thibault Link: https://lore.kernel.org/r/20220823222515.412752202@ens-lyon.org Signed-off-by: Greg Kroah-Hartman --- drivers/accessibility/speakup/speakup_soft.c | 26 ++++++++++++++++++++++++-- drivers/accessibility/speakup/spk_types.h | 2 +- drivers/accessibility/speakup/varhandlers.c | 12 ++++++++---- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/drivers/accessibility/speakup/speakup_soft.c b/drivers/accessibility/speakup/speakup_soft.c index 99f1d4ac426a..2f75ac78764e 100644 --- a/drivers/accessibility/speakup/speakup_soft.c +++ b/drivers/accessibility/speakup/speakup_soft.c @@ -26,6 +26,7 @@ static int softsynth_probe(struct spk_synth *synth); static void softsynth_release(struct spk_synth *synth); static int softsynth_is_alive(struct spk_synth *synth); +static int softsynth_adjust(struct spk_synth *synth, struct st_var_header *var); static unsigned char get_index(struct spk_synth *synth); static struct miscdevice synth_device, synthu_device; @@ -41,7 +42,7 @@ static struct var_t vars[] = { { INFLECTION, .u.n = {"\x01%dr", 5, 0, 9, 0, 0, NULL } }, { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL } }, { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL } }, - { PUNCT, .u.n = {"\x01%db", 0, 0, 2, 0, 0, NULL } }, + { PUNCT, .u.n = {"\x01%db", 0, 0, 3, 0, 0, NULL } }, { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL } }, { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL } }, { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, @@ -133,7 +134,7 @@ static struct spk_synth synth_soft = { .catch_up = NULL, .flush = NULL, .is_alive = softsynth_is_alive, - .synth_adjust = NULL, + .synth_adjust = softsynth_adjust, .read_buff_add = NULL, .get_index = get_index, .indexing = { @@ -426,6 +427,27 @@ static int softsynth_is_alive(struct spk_synth *synth) return 0; } +static int softsynth_adjust(struct spk_synth *synth, struct st_var_header *var) +{ + struct st_var_header *punc_level_var; + struct var_t *var_data; + + if (var->var_id != PUNC_LEVEL) + return 0; + + /* We want to set the the speech synthesis punctuation level + * accordingly, so it properly tunes speaking A_PUNC characters */ + var_data = var->data; + if (!var_data) + return 0; + punc_level_var = spk_get_var_header(PUNCT); + if (!punc_level_var) + return 0; + spk_set_num_var(var_data->u.n.value, punc_level_var, E_SET); + + return 1; +} + module_param_named(start, synth_soft.startup, short, 0444); MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); diff --git a/drivers/accessibility/speakup/spk_types.h b/drivers/accessibility/speakup/spk_types.h index 6a96ad94bc3f..3a14d39bf896 100644 --- a/drivers/accessibility/speakup/spk_types.h +++ b/drivers/accessibility/speakup/spk_types.h @@ -195,7 +195,7 @@ struct spk_synth { void (*catch_up)(struct spk_synth *synth); void (*flush)(struct spk_synth *synth); int (*is_alive)(struct spk_synth *synth); - int (*synth_adjust)(struct st_var_header *var); + int (*synth_adjust)(struct spk_synth *synth, struct st_var_header *var); void (*read_buff_add)(u_char c); unsigned char (*get_index)(struct spk_synth *synth); struct synth_indexing indexing; diff --git a/drivers/accessibility/speakup/varhandlers.c b/drivers/accessibility/speakup/varhandlers.c index 067c0da97dcb..e1c9f42e39f0 100644 --- a/drivers/accessibility/speakup/varhandlers.c +++ b/drivers/accessibility/speakup/varhandlers.c @@ -138,6 +138,7 @@ struct st_var_header *spk_get_var_header(enum var_id_t var_id) return NULL; return p_header; } +EXPORT_SYMBOL_GPL(spk_get_var_header); struct st_var_header *spk_var_header_by_name(const char *name) { @@ -221,15 +222,17 @@ int spk_set_num_var(int input, struct st_var_header *var, int how) *p_val = val; if (var->var_id == PUNC_LEVEL) { spk_punc_mask = spk_punc_masks[val]; - return 0; } if (var_data->u.n.multiplier != 0) val *= var_data->u.n.multiplier; val += var_data->u.n.offset; - if (var->var_id < FIRST_SYNTH_VAR || !synth) + + if (!synth) + return 0; + if (synth->synth_adjust && synth->synth_adjust(synth, var)) + return 0; + if (var->var_id < FIRST_SYNTH_VAR) return 0; - if (synth->synth_adjust) - return synth->synth_adjust(var); if (!var_data->u.n.synth_fmt) return 0; @@ -245,6 +248,7 @@ int spk_set_num_var(int input, struct st_var_header *var, int how) synth_printf("%s", cp); return 0; } +EXPORT_SYMBOL_GPL(spk_set_num_var); int spk_set_string_var(const char *page, struct st_var_header *var, int len) { -- cgit v1.2.3 From 116769d385a81f874f9d407ae92c09b1af44023b Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Mon, 29 Aug 2022 22:36:25 +0200 Subject: speakup_soft: Add direct module parameter For non-english speech synthesis, it is often desirable to make speakup_soft pass text directly. Adding a direct parameter to the speakup_soft module allows to easily set that at boot, rather than setting the sys variable after boot. Signed-off-by: Samuel Thibault Link: https://lore.kernel.org/r/20220829203625.6s6x57miowu4p664@begin Signed-off-by: Greg Kroah-Hartman --- drivers/accessibility/speakup/speakup_soft.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/accessibility/speakup/speakup_soft.c b/drivers/accessibility/speakup/speakup_soft.c index 2f75ac78764e..28c8f60370cf 100644 --- a/drivers/accessibility/speakup/speakup_soft.c +++ b/drivers/accessibility/speakup/speakup_soft.c @@ -34,6 +34,9 @@ static int init_pos; static int misc_registered; static struct var_t vars[] = { + /* DIRECT is put first so that module_param_named can access it easily */ + { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, + { CAPS_START, .u.s = {"\x01+3p" } }, { CAPS_STOP, .u.s = {"\x01-3p" } }, { PAUSE, .u.n = {"\x01P" } }, @@ -45,7 +48,6 @@ static struct var_t vars[] = { { PUNCT, .u.n = {"\x01%db", 0, 0, 3, 0, 0, NULL } }, { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL } }, { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL } }, - { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, V_LAST_VAR }; @@ -449,8 +451,10 @@ static int softsynth_adjust(struct spk_synth *synth, struct st_var_header *var) } module_param_named(start, synth_soft.startup, short, 0444); +module_param_named(direct, vars[0].u.n.default_val, int, 0444); MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); +MODULE_PARM_DESC(direct, "Set the direct variable on load."); module_spk_synth(synth_soft); -- cgit v1.2.3 From d82a7aed83bacaee08cf77503e3d0e6e667f8526 Mon Sep 17 00:00:00 2001 From: Peter Colberg Date: Wed, 31 Aug 2022 16:48:51 -0400 Subject: uio: dfl: add IOPLL user-clock feature id Add a Device Feature List (DFL) feature id [1] for the configurable IOPLL user clock source, which can be used to configure the clock speeds that are used for RTL logic that is programmed into the Partial Reconfiguration (PR) region of an FPGA. The IOPLL user-space driver [2] contains frequency tables [3] with the specific user clock frequencies for an implementation. For each desired frequency, the table values are produced by calling the quartus tool, the same tool that generates the IOPLL RTL logic. The quartus tool allows the RTL designer to select different options which can affect the table values. The table-driven, user-space driver allows for supporting future, modified implementations and provides users the ability to modify the IOPLL implementation. [1] https://github.com/OPAE/dfl-feature-id [2] https://github.com/OPAE/opae-sdk/blob/a494f54a9f0356d0425edbff228f0254a4c70303/libraries/plugins/xfpga/usrclk/fpga_user_clk.c [3] https://github.com/OPAE/opae-sdk/blob/a494f54a9f0356d0425edbff228f0254a4c70303/libraries/plugins/xfpga/usrclk/fpga_user_clk_freq.h Acked-by: Xu Yilun Signed-off-by: Russ Weight Signed-off-by: Peter Colberg Link: https://lore.kernel.org/r/20220831204851.4683-1-peter.colberg@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_dfl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/uio/uio_dfl.c b/drivers/uio/uio_dfl.c index 8f39cc8bb034..69e93f3e7faf 100644 --- a/drivers/uio/uio_dfl.c +++ b/drivers/uio/uio_dfl.c @@ -46,10 +46,12 @@ static int uio_dfl_probe(struct dfl_device *ddev) #define FME_FEATURE_ID_ETH_GROUP 0x10 #define FME_FEATURE_ID_HSSI_SUBSYS 0x15 +#define PORT_FEATURE_ID_IOPLL_USRCLK 0x14 static const struct dfl_device_id uio_dfl_ids[] = { { FME_ID, FME_FEATURE_ID_ETH_GROUP }, { FME_ID, FME_FEATURE_ID_HSSI_SUBSYS }, + { PORT_ID, PORT_FEATURE_ID_IOPLL_USRCLK }, { } }; MODULE_DEVICE_TABLE(dfl, uio_dfl_ids); -- cgit v1.2.3 From 22c053900095f4c71f7c6adc9b83d657d6529447 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 10 Aug 2022 12:18:08 +0300 Subject: get_maintainer: Add Alan to .get_maintainer.ignore Alan asked to be added to the .get_maintainer.ignore list. Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/YvN30KhO9aD5Sza9@kili Signed-off-by: Greg Kroah-Hartman --- .get_maintainer.ignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.get_maintainer.ignore b/.get_maintainer.ignore index a64d21913745..c298bab3d320 100644 --- a/.get_maintainer.ignore +++ b/.get_maintainer.ignore @@ -1,2 +1,4 @@ +Alan Cox +Alan Cox Christoph Hellwig Marc Gonzalez -- cgit v1.2.3 From 6e08c43dffd46f0571686917eeda4284ed220079 Mon Sep 17 00:00:00 2001 From: Alexander Baehr Date: Sat, 6 Aug 2022 13:31:25 +0200 Subject: parport: add support for Netmos device 9900 The Netmos parallel port 9901 was already supported but the device 9900 was not. This patch adds the required settings for it and was successfully tested with the Netmos device 9900. Signed-off-by: Alexander Baehr Link: https://lore.kernel.org/r/20220806113334.264598686@osadl.org Signed-off-by: Greg Kroah-Hartman --- drivers/parport/parport_pc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index eda4ded4d5e5..7c45927e2131 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -2604,6 +2604,7 @@ enum parport_pc_pci_cards { oxsemi_pcie_pport, aks_0100, mobility_pp, + netmos_9900, netmos_9705, netmos_9715, netmos_9755, @@ -2665,6 +2666,7 @@ static struct parport_pc_pci { /* oxsemi_pcie_pport */ { 1, { { 0, 1 }, } }, /* aks_0100 */ { 1, { { 0, -1 }, } }, /* mobility_pp */ { 1, { { 0, 1 }, } }, + /* netmos_9900 */ { 1, { { 0, -1 }, } }, /* The netmos entries below are untested */ /* netmos_9705 */ { 1, { { 0, -1 }, } }, @@ -2746,6 +2748,8 @@ static const struct pci_device_id parport_pc_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, aks_0100 }, { 0x14f2, 0x0121, PCI_ANY_ID, PCI_ANY_ID, 0, 0, mobility_pp }, /* NetMos communication controllers */ + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, + 0xA000, 0x2000, 0, 0, netmos_9900 }, { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9705, PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9705 }, { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9715, -- cgit v1.2.3 From 9c6d194f40a97ecab85af8c4af33ec934ea75716 Mon Sep 17 00:00:00 2001 From: Vishnu Dasa Date: Mon, 25 Jul 2022 09:32:46 -0700 Subject: VMCI: Update maintainers for VMCI Remove Rajesh as a maintainer for the VMCI driver. Acked-by: Bryan Tan Acked-by: Rajesh Jalisatgi Signed-off-by: Vishnu Dasa Link: https://lore.kernel.org/r/20220725163246.38486-1-vdasa@vmware.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 8a5012ba6ff9..1b2ab4b04d69 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21716,7 +21716,6 @@ F: drivers/ptp/ptp_vmw.c VMWARE VMCI DRIVER M: Bryan Tan -M: Rajesh Jalisatgi M: Vishnu Dasa R: VMware PV-Drivers Reviewers L: linux-kernel@vger.kernel.org -- cgit v1.2.3 From a68108c8370a7210626413e5aaf2327de3aeab94 Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Thu, 25 Aug 2022 17:20:29 +0200 Subject: MAINTAINERS: add spdxexclude to LICENSES and SPDX stuff Commit 0509b270a358 ("scripts/spdxcheck: Put excluded files and directories into a separate file") moved excluded files to the new file scripts/spdxexclude, but did not adjust the MAINTAINERS section. The file scripts/spdxexclude clearly belongs to LICENSES and SPDX stuff. Add a corresponding file entry. Signed-off-by: Lukas Bulwahn Link: https://lore.kernel.org/r/20220825152029.12660-1-lukas.bulwahn@gmail.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 1b2ab4b04d69..61547e61b7cb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11625,6 +11625,7 @@ F: Documentation/process/license-rules.rst F: LICENSES/ F: scripts/spdxcheck-test.sh F: scripts/spdxcheck.py +F: scripts/spdxexclude LINEAR RANGES HELPERS M: Mark Brown -- cgit v1.2.3 From 393fc2f5948fd340d016a9557eea6e1ac2f6c60c Mon Sep 17 00:00:00 2001 From: Kumaravel Thiagarajan Date: Thu, 25 Aug 2022 01:30:43 +0530 Subject: misc: microchip: pci1xxxx: load auxiliary bus driver for the PIO function in the multi-function endpoint of pci1xxxx device. pci1xxxx is a PCIe switch with a multi-function endpoint on one of its downstream ports. PIO function is one of the functions in the multi-function endpoint. PIO function combines a GPIO controller and also an interface to program pci1xxxx's OTP & EEPROM. This auxiliary bus driver is loaded for the PIO function and separate child devices are enumerated for GPIO controller and OTP/EEPROM interface. Signed-off-by: Kumaravel Thiagarajan Link: https://lore.kernel.org/r/20220824200047.150308-2-kumaravel.thiagarajan@microchip.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 7 ++ drivers/misc/Kconfig | 1 + drivers/misc/Makefile | 3 +- drivers/misc/mchp_pci1xxxx/Kconfig | 11 ++ drivers/misc/mchp_pci1xxxx/Makefile | 1 + drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c | 166 ++++++++++++++++++++++++++ drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.h | 28 +++++ 7 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 drivers/misc/mchp_pci1xxxx/Kconfig create mode 100644 drivers/misc/mchp_pci1xxxx/Makefile create mode 100644 drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c create mode 100644 drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.h diff --git a/MAINTAINERS b/MAINTAINERS index 61547e61b7cb..992e6f80c4ee 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13422,6 +13422,13 @@ S: Supported F: Documentation/devicetree/bindings/mtd/atmel-nand.txt F: drivers/mtd/nand/raw/atmel/* +MICROCHIP PCI1XXXX GP DRIVER +M: Kumaravel Thiagarajan +L: linux-gpio@vger.kernel.org +S: Supported +F: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c +F: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.h + MICROCHIP OTPC DRIVER M: Claudiu Beznea L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 94e9fb4cdd76..358ad56f6524 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -513,4 +513,5 @@ source "drivers/misc/cardreader/Kconfig" source "drivers/misc/habanalabs/Kconfig" source "drivers/misc/uacce/Kconfig" source "drivers/misc/pvpanic/Kconfig" +source "drivers/misc/mchp_pci1xxxx/Kconfig" endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 2be8542616dd..ac9b3e757ba1 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -60,4 +60,5 @@ obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o obj-$(CONFIG_OPEN_DICE) += open-dice.o -obj-$(CONFIG_VCPU_STALL_DETECTOR) += vcpu_stall_detector.o \ No newline at end of file +obj-$(CONFIG_GP_PCI1XXXX) += mchp_pci1xxxx/ +obj-$(CONFIG_VCPU_STALL_DETECTOR) += vcpu_stall_detector.o diff --git a/drivers/misc/mchp_pci1xxxx/Kconfig b/drivers/misc/mchp_pci1xxxx/Kconfig new file mode 100644 index 000000000000..f6a6970d2a59 --- /dev/null +++ b/drivers/misc/mchp_pci1xxxx/Kconfig @@ -0,0 +1,11 @@ +config GP_PCI1XXXX + tristate "Microchip PCI1XXXX PCIe to GPIO Expander + OTP/EEPROM manager" + depends on PCI + select GPIOLIB_IRQCHIP + help + PCI1XXXX is a PCIe GEN 3 switch with one of the endpoints having + multiple functions and one of the functions is a GPIO controller + which also has registers to interface with the OTP and EEPROM. + Select yes, no or module here to include or exclude the driver + for the GPIO function. + diff --git a/drivers/misc/mchp_pci1xxxx/Makefile b/drivers/misc/mchp_pci1xxxx/Makefile new file mode 100644 index 000000000000..23927ab251c4 --- /dev/null +++ b/drivers/misc/mchp_pci1xxxx/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_GP_PCI1XXXX) := mchp_pci1xxxx_gp.o diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c new file mode 100644 index 000000000000..bfc03028b34d --- /dev/null +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2022 Microchip Technology Inc. + +#include +#include +#include +#include +#include +#include +#include +#include +#include "mchp_pci1xxxx_gp.h" + +struct aux_bus_device { + struct auxiliary_device_wrapper *aux_device_wrapper[2]; +}; + +static DEFINE_IDA(gp_client_ida); +static const char aux_dev_otp_e2p_name[15] = "gp_otp_e2p"; +static const char aux_dev_gpio_name[15] = "gp_gpio"; + +static void gp_auxiliary_device_release(struct device *dev) +{ + struct auxiliary_device_wrapper *aux_device_wrapper = + (struct auxiliary_device_wrapper *)container_of(dev, + struct auxiliary_device_wrapper, aux_dev.dev); + + ida_free(&gp_client_ida, aux_device_wrapper->aux_dev.id); + kfree(aux_device_wrapper); +} + +static int gp_aux_bus_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct aux_bus_device *aux_bus; + int retval; + + retval = pcim_enable_device(pdev); + if (retval) + return retval; + + aux_bus = kzalloc(sizeof(*aux_bus), GFP_KERNEL); + if (!aux_bus) + return -ENOMEM; + + aux_bus->aux_device_wrapper[0] = kzalloc(sizeof(*aux_bus->aux_device_wrapper[0]), + GFP_KERNEL); + if (!aux_bus->aux_device_wrapper[0]) + return -ENOMEM; + + retval = ida_alloc(&gp_client_ida, GFP_KERNEL); + if (retval < 0) + goto err_ida_alloc_0; + + aux_bus->aux_device_wrapper[0]->aux_dev.name = aux_dev_otp_e2p_name; + aux_bus->aux_device_wrapper[0]->aux_dev.dev.parent = &pdev->dev; + aux_bus->aux_device_wrapper[0]->aux_dev.dev.release = gp_auxiliary_device_release; + aux_bus->aux_device_wrapper[0]->aux_dev.id = retval; + + aux_bus->aux_device_wrapper[0]->gp_aux_data.region_start = pci_resource_start(pdev, 0); + aux_bus->aux_device_wrapper[0]->gp_aux_data.region_length = pci_resource_end(pdev, 0); + + retval = auxiliary_device_init(&aux_bus->aux_device_wrapper[0]->aux_dev); + if (retval < 0) + goto err_aux_dev_init_0; + + retval = auxiliary_device_add(&aux_bus->aux_device_wrapper[0]->aux_dev); + if (retval) + goto err_aux_dev_add_0; + + aux_bus->aux_device_wrapper[1] = kzalloc(sizeof(*aux_bus->aux_device_wrapper[1]), + GFP_KERNEL); + if (!aux_bus->aux_device_wrapper[1]) + return -ENOMEM; + + retval = ida_alloc(&gp_client_ida, GFP_KERNEL); + if (retval < 0) + goto err_ida_alloc_1; + + aux_bus->aux_device_wrapper[1]->aux_dev.name = aux_dev_gpio_name; + aux_bus->aux_device_wrapper[1]->aux_dev.dev.parent = &pdev->dev; + aux_bus->aux_device_wrapper[1]->aux_dev.dev.release = gp_auxiliary_device_release; + aux_bus->aux_device_wrapper[1]->aux_dev.id = retval; + + aux_bus->aux_device_wrapper[1]->gp_aux_data.region_start = pci_resource_start(pdev, 0); + aux_bus->aux_device_wrapper[1]->gp_aux_data.region_length = pci_resource_end(pdev, 0); + + retval = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); + + if (retval < 0) + return retval; + + pdev->irq = pci_irq_vector(pdev, 0); + if (pdev->irq < 0) + return retval; + + aux_bus->aux_device_wrapper[1]->gp_aux_data.irq_num = pdev->irq; + + retval = auxiliary_device_init(&aux_bus->aux_device_wrapper[1]->aux_dev); + if (retval < 0) + goto err_aux_dev_init_1; + + retval = auxiliary_device_add(&aux_bus->aux_device_wrapper[1]->aux_dev); + if (retval) + goto err_aux_dev_add_1; + + pci_set_drvdata(pdev, aux_bus); + pci_set_master(pdev); + + return 0; + +err_aux_dev_add_1: + auxiliary_device_uninit(&aux_bus->aux_device_wrapper[1]->aux_dev); + +err_aux_dev_init_1: + ida_free(&gp_client_ida, aux_bus->aux_device_wrapper[1]->aux_dev.id); + +err_ida_alloc_1: + kfree(aux_bus->aux_device_wrapper[1]); + +err_aux_dev_add_0: + auxiliary_device_uninit(&aux_bus->aux_device_wrapper[0]->aux_dev); + +err_aux_dev_init_0: + ida_free(&gp_client_ida, aux_bus->aux_device_wrapper[0]->aux_dev.id); + +err_ida_alloc_0: + kfree(aux_bus->aux_device_wrapper[0]); + + return retval; +} + +static void gp_aux_bus_remove(struct pci_dev *pdev) +{ + struct aux_bus_device *aux_bus = pci_get_drvdata(pdev); + + auxiliary_device_delete(&aux_bus->aux_device_wrapper[0]->aux_dev); + auxiliary_device_uninit(&aux_bus->aux_device_wrapper[0]->aux_dev); + auxiliary_device_delete(&aux_bus->aux_device_wrapper[1]->aux_dev); + auxiliary_device_uninit(&aux_bus->aux_device_wrapper[1]->aux_dev); + kfree(aux_bus); + pci_disable_device(pdev); +} + +static const struct pci_device_id pci1xxxx_tbl[] = { + { PCI_DEVICE(0x1055, 0xA005) }, + { PCI_DEVICE(0x1055, 0xA015) }, + { PCI_DEVICE(0x1055, 0xA025) }, + { PCI_DEVICE(0x1055, 0xA035) }, + { PCI_DEVICE(0x1055, 0xA045) }, + { PCI_DEVICE(0x1055, 0xA055) }, + {0,} +}; +MODULE_DEVICE_TABLE(pci, pci1xxxx_tbl); + +static struct pci_driver pci1xxxx_gp_driver = { + .name = "PCI1xxxxGP", + .id_table = pci1xxxx_tbl, + .probe = gp_aux_bus_probe, + .remove = gp_aux_bus_remove, +}; + +module_pci_driver(pci1xxxx_gp_driver); + +MODULE_DESCRIPTION("Microchip Technology Inc. PCI1xxxx GP expander"); +MODULE_AUTHOR("Kumaravel Thiagarajan "); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.h b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.h new file mode 100644 index 000000000000..37eec73b20d7 --- /dev/null +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2022 Microchip Technology Inc. */ + +#ifndef _GPIO_PCI1XXXX_H +#define _GPIO_PCI1XXXX_H + +#include +#include +#include +#include +#include + +/* Perform operations like variable length write, read and write with read back for OTP / EEPROM + * Perform bit mode write in OTP + */ + +struct gp_aux_data_type { + int irq_num; + resource_size_t region_start; + resource_size_t region_length; +}; + +struct auxiliary_device_wrapper { + struct auxiliary_device aux_dev; + struct gp_aux_data_type gp_aux_data; +}; + +#endif -- cgit v1.2.3 From 7d3e4d807df2a216b9aa2944372f2b3f6ef3f205 Mon Sep 17 00:00:00 2001 From: Kumaravel Thiagarajan Date: Thu, 25 Aug 2022 01:30:44 +0530 Subject: misc: microchip: pci1xxxx: load gpio driver for the gpio controller auxiliary device enumerated by the auxiliary bus driver. PIO function's auxiliary bus driver enumerates separate child devices for GPIO controller and OTP/EEPROM interface. This gpio driver implemented based on the gpio framework is loaded for the gpio auxiliary device. Signed-off-by: Kumaravel Thiagarajan Link: https://lore.kernel.org/r/20220824200047.150308-3-kumaravel.thiagarajan@microchip.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + drivers/misc/mchp_pci1xxxx/Makefile | 2 +- drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c | 163 ++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c diff --git a/MAINTAINERS b/MAINTAINERS index 992e6f80c4ee..45716318e342 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13428,6 +13428,7 @@ L: linux-gpio@vger.kernel.org S: Supported F: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c F: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.h +F: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c MICROCHIP OTPC DRIVER M: Claudiu Beznea diff --git a/drivers/misc/mchp_pci1xxxx/Makefile b/drivers/misc/mchp_pci1xxxx/Makefile index 23927ab251c4..fc4615cfe28b 100644 --- a/drivers/misc/mchp_pci1xxxx/Makefile +++ b/drivers/misc/mchp_pci1xxxx/Makefile @@ -1 +1 @@ -obj-$(CONFIG_GP_PCI1XXXX) := mchp_pci1xxxx_gp.o +obj-$(CONFIG_GP_PCI1XXXX) := mchp_pci1xxxx_gp.o mchp_pci1xxxx_gpio.o diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c new file mode 100644 index 000000000000..97ed7ae1baaa --- /dev/null +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2022 Microchip Technology Inc. +// pci1xxxx gpio driver + +#include +#include +#include +#include +#include +#include +#include + +#include "mchp_pci1xxxx_gp.h" + +#define PCI1XXXX_NR_PINS 93 +#define PULLUP_OFFSET(x) ((((x) / 32) * 4) + 0x400 + 0x40) +#define PULLDOWN_OFFSET(x) ((((x) / 32) * 4) + 0x400 + 0x50) +#define OPENDRAIN_OFFSET(x) ((((x) / 32) * 4) + 0x400 + 0x60) +#define DEBOUNCE_OFFSET(x) ((((x) / 32) * 4) + 0x400 + 0xE0) +#define PIO_GLOBAL_CONFIG_OFFSET (0x400 + 0xF0) +#define PIO_PCI_CTRL_REG_OFFSET (0x400 + 0xF4) +#define INTR_MASK_OFFSET(x) ((((x) / 32) * 4) + 0x400 + 0x100) +#define INTR_STATUS_OFFSET(x) (((x) * 4) + 0x400 + 0xD0) + +struct pci1xxxx_gpio { + struct auxiliary_device *aux_dev; + void __iomem *reg_base; + struct gpio_chip gpio; + spinlock_t lock; + int irq_base; +}; + +static inline void pci1xxx_assign_bit(void __iomem *base_addr, unsigned int reg_offset, + unsigned int bitpos, bool set) +{ + u32 data; + + data = readl(base_addr + reg_offset); + if (set) + data |= BIT(bitpos); + else + data &= ~BIT(bitpos); + writel(data, base_addr + reg_offset); +} + +static int pci1xxxx_gpio_set_config(struct gpio_chip *gpio, unsigned int offset, + unsigned long config) +{ + struct pci1xxxx_gpio *priv = gpiochip_get_data(gpio); + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&priv->lock, flags); + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_BIAS_PULL_UP: + pci1xxx_assign_bit(priv->reg_base, PULLUP_OFFSET(offset), (offset % 32), true); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + pci1xxx_assign_bit(priv->reg_base, PULLDOWN_OFFSET(offset), (offset % 32), true); + break; + case PIN_CONFIG_BIAS_DISABLE: + pci1xxx_assign_bit(priv->reg_base, PULLUP_OFFSET(offset), (offset % 32), false); + pci1xxx_assign_bit(priv->reg_base, PULLDOWN_OFFSET(offset), (offset % 32), false); + break; + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + pci1xxx_assign_bit(priv->reg_base, OPENDRAIN_OFFSET(offset), (offset % 32), true); + break; + default: + ret = -EOPNOTSUPP; + break; + } + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; +} + +static int pci1xxxx_gpio_setup(struct pci1xxxx_gpio *priv, int irq) +{ + struct gpio_chip *gchip = &priv->gpio; + + gchip->label = dev_name(&priv->aux_dev->dev); + gchip->parent = &priv->aux_dev->dev; + gchip->owner = THIS_MODULE; + gchip->set_config = pci1xxxx_gpio_set_config; + gchip->dbg_show = NULL; + gchip->base = -1; + gchip->ngpio = PCI1XXXX_NR_PINS; + gchip->can_sleep = false; + + return 0; +} + +static int pci1xxxx_gpio_probe(struct auxiliary_device *aux_dev, + const struct auxiliary_device_id *id) + +{ + struct auxiliary_device_wrapper *aux_dev_wrapper; + struct gp_aux_data_type *pdata; + struct pci1xxxx_gpio *priv; + int retval; + + aux_dev_wrapper = (struct auxiliary_device_wrapper *) + container_of(aux_dev, struct auxiliary_device_wrapper, aux_dev); + + pdata = &aux_dev_wrapper->gp_aux_data; + + if (!pdata) + return -EINVAL; + + priv = devm_kzalloc(&aux_dev->dev, sizeof(struct pci1xxxx_gpio), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->aux_dev = aux_dev; + + if (!devm_request_mem_region(&aux_dev->dev, pdata->region_start, 0x800, aux_dev->name)) + return -EBUSY; + + priv->reg_base = devm_ioremap(&aux_dev->dev, pdata->region_start, 0x800); + if (!priv->reg_base) + return -ENOMEM; + + writel(0x0264, (priv->reg_base + 0x400 + 0xF0)); + + retval = pci1xxxx_gpio_setup(priv, pdata->irq_num); + + if (retval < 0) + return retval; + + dev_set_drvdata(&aux_dev->dev, priv); + + return devm_gpiochip_add_data(&aux_dev->dev, &priv->gpio, priv); +} + +const struct auxiliary_device_id pci1xxxx_gpio_auxiliary_id_table[] = { + {.name = "mchp_pci1xxxx_gp.gp_gpio"}, + {} +}; + +static struct auxiliary_driver pci1xxxx_gpio_driver = { + .driver = { + .name = "PCI1xxxxGPIO", + }, + .probe = pci1xxxx_gpio_probe, + .id_table = pci1xxxx_gpio_auxiliary_id_table +}; + +static int __init pci1xxxx_gpio_driver_init(void) +{ + return auxiliary_driver_register(&pci1xxxx_gpio_driver); +} + +static void __exit pci1xxxx_gpio_driver_exit(void) +{ + auxiliary_driver_unregister(&pci1xxxx_gpio_driver); +} + +module_init(pci1xxxx_gpio_driver_init); +module_exit(pci1xxxx_gpio_driver_exit); + +MODULE_DESCRIPTION("Microchip Technology Inc. PCI1xxxx GPIO controller"); +MODULE_AUTHOR("Kumaravel Thiagarajan "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 9b91a368a4de0559c8fde4e69cdab797cb3d9876 Mon Sep 17 00:00:00 2001 From: Kumaravel Thiagarajan Date: Thu, 25 Aug 2022 01:30:45 +0530 Subject: misc: microchip: pci1xxxx: Add functions to configure gpio pins as input / output, get status, handle I/O for gpio pins. direction_input and direction_output functions configures a gpio pin as input and output respectively. get_direction function returns if a gpio pin is output or input. get function returns the value of a gpio pin whereas set function assigns output value for a gpio pin. Signed-off-by: Kumaravel Thiagarajan Link: https://lore.kernel.org/r/20220824200047.150308-4-kumaravel.thiagarajan@microchip.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c | 79 +++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c index 97ed7ae1baaa..45e197fe3a3e 100644 --- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c @@ -13,6 +13,10 @@ #include "mchp_pci1xxxx_gp.h" #define PCI1XXXX_NR_PINS 93 +#define OUT_EN_OFFSET(x) ((((x) / 32) * 4) + 0x400) +#define INP_EN_OFFSET(x) ((((x) / 32) * 4) + 0x400 + 0x10) +#define OUT_OFFSET(x) ((((x) / 32) * 4) + 0x400 + 0x20) +#define INP_OFFSET(x) ((((x) / 32) * 4) + 0x400 + 0x30) #define PULLUP_OFFSET(x) ((((x) / 32) * 4) + 0x400 + 0x40) #define PULLDOWN_OFFSET(x) ((((x) / 32) * 4) + 0x400 + 0x50) #define OPENDRAIN_OFFSET(x) ((((x) / 32) * 4) + 0x400 + 0x60) @@ -30,6 +34,24 @@ struct pci1xxxx_gpio { int irq_base; }; +static int pci1xxxx_gpio_get_direction(struct gpio_chip *gpio, unsigned int nr) +{ + struct pci1xxxx_gpio *priv = gpiochip_get_data(gpio); + u32 data; + int ret = -EINVAL; + + data = readl(priv->reg_base + INP_EN_OFFSET(nr)); + if (data & BIT(nr % 32)) { + ret = 1; + } else { + data = readl(priv->reg_base + OUT_EN_OFFSET(nr)); + if (data & BIT(nr % 32)) + ret = 0; + } + + return ret; +} + static inline void pci1xxx_assign_bit(void __iomem *base_addr, unsigned int reg_offset, unsigned int bitpos, bool set) { @@ -43,6 +65,58 @@ static inline void pci1xxx_assign_bit(void __iomem *base_addr, unsigned int reg_ writel(data, base_addr + reg_offset); } +static int pci1xxxx_gpio_direction_input(struct gpio_chip *gpio, unsigned int nr) +{ + struct pci1xxxx_gpio *priv = gpiochip_get_data(gpio); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + pci1xxx_assign_bit(priv->reg_base, INP_EN_OFFSET(nr), (nr % 32), true); + pci1xxx_assign_bit(priv->reg_base, OUT_EN_OFFSET(nr), (nr % 32), false); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int pci1xxxx_gpio_get(struct gpio_chip *gpio, unsigned int nr) +{ + struct pci1xxxx_gpio *priv = gpiochip_get_data(gpio); + + return (readl(priv->reg_base + INP_OFFSET(nr)) >> (nr % 32)) & 1; +} + +static int pci1xxxx_gpio_direction_output(struct gpio_chip *gpio, + unsigned int nr, int val) +{ + struct pci1xxxx_gpio *priv = gpiochip_get_data(gpio); + unsigned long flags; + u32 data; + + spin_lock_irqsave(&priv->lock, flags); + pci1xxx_assign_bit(priv->reg_base, INP_EN_OFFSET(nr), (nr % 32), false); + pci1xxx_assign_bit(priv->reg_base, OUT_EN_OFFSET(nr), (nr % 32), true); + data = readl(priv->reg_base + OUT_OFFSET(nr)); + if (val) + data |= (1 << (nr % 32)); + else + data &= ~(1 << (nr % 32)); + writel(data, priv->reg_base + OUT_OFFSET(nr)); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static void pci1xxxx_gpio_set(struct gpio_chip *gpio, + unsigned int nr, int val) +{ + struct pci1xxxx_gpio *priv = gpiochip_get_data(gpio); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + pci1xxx_assign_bit(priv->reg_base, OUT_OFFSET(nr), (nr % 32), val); + spin_unlock_irqrestore(&priv->lock, flags); +} + static int pci1xxxx_gpio_set_config(struct gpio_chip *gpio, unsigned int offset, unsigned long config) { @@ -81,6 +155,11 @@ static int pci1xxxx_gpio_setup(struct pci1xxxx_gpio *priv, int irq) gchip->label = dev_name(&priv->aux_dev->dev); gchip->parent = &priv->aux_dev->dev; gchip->owner = THIS_MODULE; + gchip->direction_input = pci1xxxx_gpio_direction_input; + gchip->direction_output = pci1xxxx_gpio_direction_output; + gchip->get_direction = pci1xxxx_gpio_get_direction; + gchip->get = pci1xxxx_gpio_get; + gchip->set = pci1xxxx_gpio_set; gchip->set_config = pci1xxxx_gpio_set_config; gchip->dbg_show = NULL; gchip->base = -1; -- cgit v1.2.3 From 1f4d8ae231f47c7d890198cd847055a96482a443 Mon Sep 17 00:00:00 2001 From: Kumaravel Thiagarajan Date: Thu, 25 Aug 2022 01:30:46 +0530 Subject: misc: microchip: pci1xxxx: Add gpio irq handler and irq helper functions irq_ack, irq_mask, irq_unmask and irq_set_type of irq_chip. The helper functions irq_set_type, irq_mask, irq_unmask and irq_ack configure the interrupt type, mask, unmask and acknowledge the interrupts. Signed-off-by: Kumaravel Thiagarajan Link: https://lore.kernel.org/r/20220824200047.150308-5-kumaravel.thiagarajan@microchip.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c | 159 ++++++++++++++++++++++++ 1 file changed, 159 insertions(+) diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c index 45e197fe3a3e..eb5ad75c9c88 100644 --- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "mchp_pci1xxxx_gp.h" @@ -20,6 +21,13 @@ #define PULLUP_OFFSET(x) ((((x) / 32) * 4) + 0x400 + 0x40) #define PULLDOWN_OFFSET(x) ((((x) / 32) * 4) + 0x400 + 0x50) #define OPENDRAIN_OFFSET(x) ((((x) / 32) * 4) + 0x400 + 0x60) +#define WAKEMASK_OFFSET(x) ((((x) / 32) * 4) + 0x400 + 0x70) +#define MODE_OFFSET(x) ((((x) / 32) * 4) + 0x400 + 0x80) +#define INTR_LO_TO_HI_EDGE_CONFIG(x) ((((x) / 32) * 4) + 0x400 + 0x90) +#define INTR_HI_TO_LO_EDGE_CONFIG(x) ((((x) / 32) * 4) + 0x400 + 0xA0) +#define INTR_LEVEL_CONFIG_OFFSET(x) ((((x) / 32) * 4) + 0x400 + 0xB0) +#define INTR_LEVEL_MASK_OFFSET(x) ((((x) / 32) * 4) + 0x400 + 0xC0) +#define INTR_STAT_OFFSET(x) ((((x) / 32) * 4) + 0x400 + 0xD0) #define DEBOUNCE_OFFSET(x) ((((x) / 32) * 4) + 0x400 + 0xE0) #define PIO_GLOBAL_CONFIG_OFFSET (0x400 + 0xF0) #define PIO_PCI_CTRL_REG_OFFSET (0x400 + 0xF4) @@ -148,9 +156,146 @@ static int pci1xxxx_gpio_set_config(struct gpio_chip *gpio, unsigned int offset, return ret; } +static void pci1xxxx_gpio_irq_ack(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct pci1xxxx_gpio *priv = gpiochip_get_data(chip); + unsigned int gpio = irqd_to_hwirq(data); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + pci1xxx_assign_bit(priv->reg_base, INTR_STAT_OFFSET(gpio), (gpio % 32), true); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void pci1xxxx_gpio_irq_set_mask(struct irq_data *data, bool set) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct pci1xxxx_gpio *priv = gpiochip_get_data(chip); + unsigned int gpio = irqd_to_hwirq(data); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + pci1xxx_assign_bit(priv->reg_base, INTR_MASK_OFFSET(gpio), (gpio % 32), set); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void pci1xxxx_gpio_irq_mask(struct irq_data *data) +{ + pci1xxxx_gpio_irq_set_mask(data, true); +} + +static void pci1xxxx_gpio_irq_unmask(struct irq_data *data) +{ + pci1xxxx_gpio_irq_set_mask(data, false); +} + +static int pci1xxxx_gpio_set_type(struct irq_data *data, unsigned int trigger_type) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct pci1xxxx_gpio *priv = gpiochip_get_data(chip); + unsigned int gpio = irqd_to_hwirq(data); + unsigned int bitpos = gpio % 32; + + if (trigger_type & IRQ_TYPE_EDGE_FALLING) { + pci1xxx_assign_bit(priv->reg_base, INTR_HI_TO_LO_EDGE_CONFIG(gpio), + bitpos, false); + pci1xxx_assign_bit(priv->reg_base, MODE_OFFSET(gpio), + bitpos, false); + irq_set_handler_locked(data, handle_edge_irq); + } else { + pci1xxx_assign_bit(priv->reg_base, INTR_HI_TO_LO_EDGE_CONFIG(gpio), + bitpos, true); + } + + if (trigger_type & IRQ_TYPE_EDGE_RISING) { + pci1xxx_assign_bit(priv->reg_base, INTR_LO_TO_HI_EDGE_CONFIG(gpio), + bitpos, false); + pci1xxx_assign_bit(priv->reg_base, MODE_OFFSET(gpio), bitpos, + false); + irq_set_handler_locked(data, handle_edge_irq); + } else { + pci1xxx_assign_bit(priv->reg_base, INTR_LO_TO_HI_EDGE_CONFIG(gpio), + bitpos, true); + } + + if (trigger_type & IRQ_TYPE_LEVEL_LOW) { + pci1xxx_assign_bit(priv->reg_base, INTR_LEVEL_CONFIG_OFFSET(gpio), + bitpos, true); + pci1xxx_assign_bit(priv->reg_base, INTR_LEVEL_MASK_OFFSET(gpio), + bitpos, false); + pci1xxx_assign_bit(priv->reg_base, MODE_OFFSET(gpio), bitpos, + true); + irq_set_handler_locked(data, handle_edge_irq); + } + + if (trigger_type & IRQ_TYPE_LEVEL_HIGH) { + pci1xxx_assign_bit(priv->reg_base, INTR_LEVEL_CONFIG_OFFSET(gpio), + bitpos, false); + pci1xxx_assign_bit(priv->reg_base, INTR_LEVEL_MASK_OFFSET(gpio), + bitpos, false); + pci1xxx_assign_bit(priv->reg_base, MODE_OFFSET(gpio), bitpos, + true); + irq_set_handler_locked(data, handle_edge_irq); + } + + if ((!(trigger_type & IRQ_TYPE_LEVEL_LOW)) && (!(trigger_type & IRQ_TYPE_LEVEL_HIGH))) + pci1xxx_assign_bit(priv->reg_base, INTR_LEVEL_MASK_OFFSET(gpio), bitpos, true); + + return true; +} + +static irqreturn_t pci1xxxx_gpio_irq_handler(int irq, void *dev_id) +{ + struct pci1xxxx_gpio *priv = dev_id; + struct gpio_chip *gc = &priv->gpio; + unsigned long int_status = 0; + unsigned long flags; + u8 pincount; + int bit; + u8 gpiobank; + + spin_lock_irqsave(&priv->lock, flags); + pci1xxx_assign_bit(priv->reg_base, PIO_GLOBAL_CONFIG_OFFSET, 16, true); + spin_unlock_irqrestore(&priv->lock, flags); + for (gpiobank = 0; gpiobank < 3; gpiobank++) { + spin_lock_irqsave(&priv->lock, flags); + int_status = readl(priv->reg_base + INTR_STATUS_OFFSET(gpiobank)); + spin_unlock_irqrestore(&priv->lock, flags); + if (gpiobank == 2) + pincount = 29; + else + pincount = 32; + for_each_set_bit(bit, &int_status, pincount) { + unsigned int irq; + + spin_lock_irqsave(&priv->lock, flags); + writel(BIT(bit), priv->reg_base + INTR_STATUS_OFFSET(gpiobank)); + spin_unlock_irqrestore(&priv->lock, flags); + irq = irq_find_mapping(gc->irq.domain, (bit + (gpiobank * 32))); + generic_handle_irq(irq); + } + } + spin_lock_irqsave(&priv->lock, flags); + pci1xxx_assign_bit(priv->reg_base, PIO_GLOBAL_CONFIG_OFFSET, 16, false); + spin_unlock_irqrestore(&priv->lock, flags); + + return IRQ_HANDLED; +} + +static struct irq_chip pci1xxxx_gpio_irqchip = { + .name = "pci1xxxx_gpio", + .irq_ack = pci1xxxx_gpio_irq_ack, + .irq_mask = pci1xxxx_gpio_irq_mask, + .irq_unmask = pci1xxxx_gpio_irq_unmask, + .irq_set_type = pci1xxxx_gpio_set_type, +}; + static int pci1xxxx_gpio_setup(struct pci1xxxx_gpio *priv, int irq) { struct gpio_chip *gchip = &priv->gpio; + struct gpio_irq_chip *girq; + int retval; gchip->label = dev_name(&priv->aux_dev->dev); gchip->parent = &priv->aux_dev->dev; @@ -166,6 +311,20 @@ static int pci1xxxx_gpio_setup(struct pci1xxxx_gpio *priv, int irq) gchip->ngpio = PCI1XXXX_NR_PINS; gchip->can_sleep = false; + retval = devm_request_threaded_irq(&priv->aux_dev->dev, irq, + NULL, pci1xxxx_gpio_irq_handler, + IRQF_ONESHOT, "PCI1xxxxGPIO", priv); + + if (retval) + return retval; + + girq = &priv->gpio.irq; + girq->chip = &pci1xxxx_gpio_irqchip; + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_bad_irq; return 0; } -- cgit v1.2.3 From 4ec7ac90ff399b7d9af81cc8afd430a22786c61b Mon Sep 17 00:00:00 2001 From: Kumaravel Thiagarajan Date: Thu, 25 Aug 2022 01:30:47 +0530 Subject: misc: microchip: pci1xxxx: Add power management functions - suspend & resume handlers. Power event handlers suspend and resume are invoked by the operating system to notify the driver about the power events. Wakeup is enabled before entering suspend and disabled after resuming. Signed-off-by: Kumaravel Thiagarajan Link: https://lore.kernel.org/r/20220824200047.150308-6-kumaravel.thiagarajan@microchip.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c | 37 +++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c index eb5ad75c9c88..230503cca2ff 100644 --- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c @@ -14,6 +14,7 @@ #include "mchp_pci1xxxx_gp.h" #define PCI1XXXX_NR_PINS 93 +#define PERI_GEN_RESET 0 #define OUT_EN_OFFSET(x) ((((x) / 32) * 4) + 0x400) #define INP_EN_OFFSET(x) ((((x) / 32) * 4) + 0x400 + 0x10) #define OUT_OFFSET(x) ((((x) / 32) * 4) + 0x400 + 0x20) @@ -291,6 +292,38 @@ static struct irq_chip pci1xxxx_gpio_irqchip = { .irq_set_type = pci1xxxx_gpio_set_type, }; +static int pci1xxxx_gpio_suspend(struct device *dev) +{ + struct pci1xxxx_gpio *priv = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + pci1xxx_assign_bit(priv->reg_base, PIO_GLOBAL_CONFIG_OFFSET, + 16, true); + pci1xxx_assign_bit(priv->reg_base, PIO_GLOBAL_CONFIG_OFFSET, + 17, false); + pci1xxx_assign_bit(priv->reg_base, PERI_GEN_RESET, 16, true); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int pci1xxxx_gpio_resume(struct device *dev) +{ + struct pci1xxxx_gpio *priv = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + pci1xxx_assign_bit(priv->reg_base, PIO_GLOBAL_CONFIG_OFFSET, + 17, true); + pci1xxx_assign_bit(priv->reg_base, PIO_GLOBAL_CONFIG_OFFSET, + 16, false); + pci1xxx_assign_bit(priv->reg_base, PERI_GEN_RESET, 16, false); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + static int pci1xxxx_gpio_setup(struct pci1xxxx_gpio *priv, int irq) { struct gpio_chip *gchip = &priv->gpio; @@ -325,6 +358,7 @@ static int pci1xxxx_gpio_setup(struct pci1xxxx_gpio *priv, int irq) girq->parents = NULL; girq->default_type = IRQ_TYPE_NONE; girq->handler = handle_bad_irq; + return 0; } @@ -370,6 +404,8 @@ static int pci1xxxx_gpio_probe(struct auxiliary_device *aux_dev, return devm_gpiochip_add_data(&aux_dev->dev, &priv->gpio, priv); } +static SIMPLE_DEV_PM_OPS(pci1xxxx_gpio_pm_ops, pci1xxxx_gpio_suspend, pci1xxxx_gpio_resume); + const struct auxiliary_device_id pci1xxxx_gpio_auxiliary_id_table[] = { {.name = "mchp_pci1xxxx_gp.gp_gpio"}, {} @@ -378,6 +414,7 @@ const struct auxiliary_device_id pci1xxxx_gpio_auxiliary_id_table[] = { static struct auxiliary_driver pci1xxxx_gpio_driver = { .driver = { .name = "PCI1xxxxGPIO", + .pm = &pci1xxxx_gpio_pm_ops, }, .probe = pci1xxxx_gpio_probe, .id_table = pci1xxxx_gpio_auxiliary_id_table -- cgit v1.2.3 From 939bc5453b8cbdde9f1e5110ce8309aedb1b501a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 1 Sep 2022 08:18:45 +0300 Subject: fpga: prevent integer overflow in dfl_feature_ioctl_set_irq() The "hdr.count * sizeof(s32)" multiplication can overflow on 32 bit systems leading to memory corruption. Use array_size() to fix that. Fixes: 322b598be4d9 ("fpga: dfl: introduce interrupt trigger setting API") Signed-off-by: Dan Carpenter Acked-by: Xu Yilun Link: https://lore.kernel.org/r/YxBAtYCM38dM7yzI@kili Signed-off-by: Xu Yilun --- drivers/fpga/dfl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c index 5498bc337f8b..b9aae85ba930 100644 --- a/drivers/fpga/dfl.c +++ b/drivers/fpga/dfl.c @@ -1866,7 +1866,7 @@ long dfl_feature_ioctl_set_irq(struct platform_device *pdev, return -EINVAL; fds = memdup_user((void __user *)(arg + sizeof(hdr)), - hdr.count * sizeof(s32)); + array_size(hdr.count, sizeof(s32))); if (IS_ERR(fds)) return PTR_ERR(fds); -- cgit v1.2.3 From 6cda6aa83c0b548471bc30cc2083fe5fd255591b Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Sun, 4 Sep 2022 12:02:01 +0200 Subject: dt-bindings: iio: adc: ti,tsc2046: add vref-supply property Add property for the voltage reference supply. Signed-off-by: Oleksij Rempel Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220904100203.3614502-1-o.rempel@pengutronix.de Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/ti,tsc2046.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/ti,tsc2046.yaml b/Documentation/devicetree/bindings/iio/adc/ti,tsc2046.yaml index 0b48814c0dc2..bdf3bba2d750 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,tsc2046.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,tsc2046.yaml @@ -23,6 +23,9 @@ properties: interrupts: maxItems: 1 + vref-supply: + description: Optional supply of the reference voltage + "#io-channel-cells": const: 1 -- cgit v1.2.3 From a616a6a1ff164d1502b28c1b1f5d481bfb26d879 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Sun, 4 Sep 2022 12:02:02 +0200 Subject: iio: adc: tsc2046: add vref support If VREF pin is attached, we should use external VREF source instead of the internal. Otherwise we will get wrong measurements on some of the channel types. Signed-off-by: Oleksij Rempel Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220904100203.3614502-2-o.rempel@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-tsc2046.c | 57 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/ti-tsc2046.c b/drivers/iio/adc/ti-tsc2046.c index 0d9436a69cbf..8d6559303172 100644 --- a/drivers/iio/adc/ti-tsc2046.c +++ b/drivers/iio/adc/ti-tsc2046.c @@ -8,7 +8,9 @@ #include #include #include +#include #include +#include #include @@ -139,6 +141,7 @@ enum tsc2046_state { struct tsc2046_adc_priv { struct spi_device *spi; const struct tsc2046_adc_dcfg *dcfg; + struct regulator *vref_reg; struct iio_trigger *trig; struct hrtimer trig_timer; @@ -173,6 +176,7 @@ struct tsc2046_adc_priv { u32 scan_interval_us; u32 time_per_scan_us; u32 time_per_bit_ns; + unsigned int vref_mv; struct tsc2046_adc_ch_cfg ch_cfg[TI_TSC2046_MAX_CHAN]; }; @@ -252,7 +256,9 @@ static u8 tsc2046_adc_get_cmd(struct tsc2046_adc_priv *priv, int ch_idx, case TI_TSC2046_ADDR_AUX: case TI_TSC2046_ADDR_VBAT: case TI_TSC2046_ADDR_TEMP0: - pd |= TI_TSC2046_SER | TI_TSC2046_PD1_VREF_ON; + pd |= TI_TSC2046_SER; + if (!priv->vref_reg) + pd |= TI_TSC2046_PD1_VREF_ON; } return TI_TSC2046_START | FIELD_PREP(TI_TSC2046_ADDR, ch_idx) | pd; @@ -468,7 +474,7 @@ static int tsc2046_adc_read_raw(struct iio_dev *indio_dev, * So, it is better to use external voltage-divider driver * instead, which is calculating complete chain. */ - *val = TI_TSC2046_INT_VREF; + *val = priv->vref_mv; *val2 = chan->scan_type.realbits; return IIO_VAL_FRACTIONAL_LOG2; } @@ -740,6 +746,49 @@ static void tsc2046_adc_parse_fwnode(struct tsc2046_adc_priv *priv) } } +static void tsc2046_adc_regulator_disable(void *data) +{ + struct tsc2046_adc_priv *priv = data; + + regulator_disable(priv->vref_reg); +} + +static int tsc2046_adc_configure_regulator(struct tsc2046_adc_priv *priv) +{ + struct device *dev = &priv->spi->dev; + int ret; + + priv->vref_reg = devm_regulator_get_optional(dev, "vref"); + if (IS_ERR(priv->vref_reg)) { + /* If regulator exists but can't be get, return an error */ + if (PTR_ERR(priv->vref_reg) != -ENODEV) + return PTR_ERR(priv->vref_reg); + priv->vref_reg = NULL; + } + if (!priv->vref_reg) { + /* Use internal reference */ + priv->vref_mv = TI_TSC2046_INT_VREF; + return 0; + } + + ret = regulator_enable(priv->vref_reg); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, tsc2046_adc_regulator_disable, + priv); + if (ret) + return ret; + + ret = regulator_get_voltage(priv->vref_reg); + if (ret < 0) + return ret; + + priv->vref_mv = ret / MILLI; + + return 0; +} + static int tsc2046_adc_probe(struct spi_device *spi) { const struct tsc2046_adc_dcfg *dcfg; @@ -781,6 +830,10 @@ static int tsc2046_adc_probe(struct spi_device *spi) indio_dev->num_channels = dcfg->num_channels; indio_dev->info = &tsc2046_adc_info; + ret = tsc2046_adc_configure_regulator(priv); + if (ret) + return ret; + tsc2046_adc_parse_fwnode(priv); ret = tsc2046_adc_setup_spi_msg(priv); -- cgit v1.2.3 From 3f8dd0a7dc16514d365253568bd32b8fe86c2e94 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Sun, 4 Sep 2022 12:02:03 +0200 Subject: iio: adc: tsc2046: silent spi_device_id warning Add spi_device_id to silent following kernel runtime warning: "SPI driver tsc2046 has no spi_device_id for ti,tsc2046e-adc". Signed-off-by: Oleksij Rempel Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220904100203.3614502-3-o.rempel@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-tsc2046.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/iio/adc/ti-tsc2046.c b/drivers/iio/adc/ti-tsc2046.c index 8d6559303172..1bbb51a6683c 100644 --- a/drivers/iio/adc/ti-tsc2046.c +++ b/drivers/iio/adc/ti-tsc2046.c @@ -805,6 +805,11 @@ static int tsc2046_adc_probe(struct spi_device *spi) } dcfg = device_get_match_data(dev); + if (!dcfg) { + const struct spi_device_id *id = spi_get_device_id(spi); + + dcfg = (const struct tsc2046_adc_dcfg *)id->driver_data; + } if (!dcfg) return -EINVAL; @@ -886,11 +891,18 @@ static const struct of_device_id ads7950_of_table[] = { }; MODULE_DEVICE_TABLE(of, ads7950_of_table); +static const struct spi_device_id tsc2046_adc_spi_ids[] = { + { "tsc2046e-adc", (unsigned long)&tsc2046_adc_dcfg_tsc2046e }, + { } +}; +MODULE_DEVICE_TABLE(spi, tsc2046_adc_spi_ids); + static struct spi_driver tsc2046_adc_driver = { .driver = { .name = "tsc2046", .of_match_table = ads7950_of_table, }, + .id_table = tsc2046_adc_spi_ids, .probe = tsc2046_adc_probe, }; module_spi_driver(tsc2046_adc_driver); -- cgit v1.2.3 From 741d1e3783d9486d76534f2f08442e1f0eb108a1 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 31 Aug 2022 17:15:28 +0300 Subject: iio: magnetometer: yamaha-yas530: Use pointers as driver data Unify ID tables to use pointers for driver data. It will allow to simplify the driver later on. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220831141530.80572-1-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/yamaha-yas530.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index 65bb34c24810..533fd8c73024 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -1437,8 +1438,10 @@ static int yas5xx_probe(struct i2c_client *i2c, goto assert_reset; } - yas5xx->chip_info = &yas5xx_chip_info_tbl[id->driver_data]; - ci = yas5xx->chip_info; + ci = device_get_match_data(dev); + if (!ci) + ci = (const struct yas5xx_chip_info *)id->driver_data; + yas5xx->chip_info = ci; ret = regmap_read(yas5xx->map, YAS5XX_DEVICE_ID, &id_check); if (ret) @@ -1585,19 +1588,19 @@ static DEFINE_RUNTIME_DEV_PM_OPS(yas5xx_dev_pm_ops, yas5xx_runtime_suspend, yas5xx_runtime_resume, NULL); static const struct i2c_device_id yas5xx_id[] = { - {"yas530", yas530 }, - {"yas532", yas532 }, - {"yas533", yas533 }, - {"yas537", yas537 }, + {"yas530", (kernel_ulong_t)&yas5xx_chip_info_tbl[yas530] }, + {"yas532", (kernel_ulong_t)&yas5xx_chip_info_tbl[yas532] }, + {"yas533", (kernel_ulong_t)&yas5xx_chip_info_tbl[yas533] }, + {"yas537", (kernel_ulong_t)&yas5xx_chip_info_tbl[yas537] }, {} }; MODULE_DEVICE_TABLE(i2c, yas5xx_id); static const struct of_device_id yas5xx_of_match[] = { - { .compatible = "yamaha,yas530", }, - { .compatible = "yamaha,yas532", }, - { .compatible = "yamaha,yas533", }, - { .compatible = "yamaha,yas537", }, + { .compatible = "yamaha,yas530", &yas5xx_chip_info_tbl[yas530] }, + { .compatible = "yamaha,yas532", &yas5xx_chip_info_tbl[yas532] }, + { .compatible = "yamaha,yas533", &yas5xx_chip_info_tbl[yas533] }, + { .compatible = "yamaha,yas537", &yas5xx_chip_info_tbl[yas537] }, {} }; MODULE_DEVICE_TABLE(of, yas5xx_of_match); -- cgit v1.2.3 From ff1c17e9a7623f73aa44c605a5e6a4396df46a5e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 31 Aug 2022 17:15:29 +0300 Subject: iio: magnetometer: yamaha-yas530: Make strings const in chip info For better compiler coverage mark strings consts in the chip info. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220831141530.80572-2-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/yamaha-yas530.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index 533fd8c73024..c56c7c9efeb8 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -189,8 +189,8 @@ struct yas5xx; */ struct yas5xx_chip_info { unsigned int devid; - char *product_name; - char *version_names[2]; + const char *product_name; + const char *version_names[2]; const int *volatile_reg; int volatile_reg_qty; u32 scaling_val2; -- cgit v1.2.3 From d05d73779145f6d1addc10b516f589f803fcdb56 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 31 Aug 2022 17:15:30 +0300 Subject: iio: magnetometer: yamaha-yas530: Use dev_err_probe() Unify error message format by using dev_err_probe(). Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220831141530.80572-3-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/yamaha-yas530.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index c56c7c9efeb8..fdb92a6420e1 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -1415,10 +1415,8 @@ static int yas5xx_probe(struct i2c_client *i2c, return dev_err_probe(dev, ret, "cannot get regulators\n"); ret = regulator_bulk_enable(ARRAY_SIZE(yas5xx->regs), yas5xx->regs); - if (ret) { - dev_err(dev, "cannot enable regulators\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "cannot enable regulators\n"); /* See comment in runtime resume callback */ usleep_range(31000, 40000); @@ -1426,15 +1424,13 @@ static int yas5xx_probe(struct i2c_client *i2c, /* This will take the device out of reset if need be */ yas5xx->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(yas5xx->reset)) { - ret = dev_err_probe(dev, PTR_ERR(yas5xx->reset), - "failed to get reset line\n"); + ret = dev_err_probe(dev, PTR_ERR(yas5xx->reset), "failed to get reset line\n"); goto reg_off; } yas5xx->map = devm_regmap_init_i2c(i2c, &yas5xx_regmap_config); if (IS_ERR(yas5xx->map)) { - dev_err(dev, "failed to allocate register map\n"); - ret = PTR_ERR(yas5xx->map); + ret = dev_err_probe(dev, PTR_ERR(yas5xx->map), "failed to allocate register map\n"); goto assert_reset; } @@ -1484,13 +1480,13 @@ static int yas5xx_probe(struct i2c_client *i2c, yas5xx_handle_trigger, NULL); if (ret) { - dev_err(dev, "triggered buffer setup failed\n"); + dev_err_probe(dev, ret, "triggered buffer setup failed\n"); goto assert_reset; } ret = iio_device_register(indio_dev); if (ret) { - dev_err(dev, "device register failed\n"); + dev_err_probe(dev, ret, "device register failed\n"); goto cleanup_buffer; } -- cgit v1.2.3 From 5a80c2572f5e40fc106fe9f372964935798dc637 Mon Sep 17 00:00:00 2001 From: Ramona Bolboaca Date: Wed, 31 Aug 2022 16:30:20 +0300 Subject: dt-bindings: iio: adc: Add max11205 documentation file Add bindings documentation file and MAINTAINERS entry for MAX11205. Signed-off-by: Ramona Bolboaca Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220831133021.215625-1-ramona.bolboaca@analog.com Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/maxim,max11205.yaml | 69 ++++++++++++++++++++++ MAINTAINERS | 8 +++ 2 files changed, 77 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/maxim,max11205.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/maxim,max11205.yaml b/Documentation/devicetree/bindings/iio/adc/maxim,max11205.yaml new file mode 100644 index 000000000000..5f9e043cf5cd --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/maxim,max11205.yaml @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/maxim,max11205.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Maxim MAX11205 ADC + +maintainers: + - Ramona Bolboaca + +description: | + The MAX11205 is an ultra-low-power (< 300FA max active current), + high-resolution, serial-output ADC. + + https://datasheets.maximintegrated.com/en/ds/MAX11205.pdf + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + enum: + - maxim,max11205a + - maxim,max11205b + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + spi-max-frequency: + maximum: 5000000 + + spi-cpha: true + + vref-supply: + description: + The regulator supply for the ADC reference voltage. This is a differential + reference. It is equal to the V_REFP - V_REFN. The maximum value is 3.6V. + +required: + - compatible + - reg + - interrupts + - spi-max-frequency + - spi-cpha + - vref-supply + +unevaluatedProperties: false + +examples: + - | + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + adc@0 { + compatible = "maxim,max11205a"; + reg = <0>; + spi-max-frequency = <5000000>; + spi-cpha; + interrupt-parent = <&gpio>; + interrupts = <19 IRQ_TYPE_EDGE_FALLING>; + vref-supply = <&max11205_vref>; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index d9c8596ee28b..17bdf4398391 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12332,6 +12332,14 @@ S: Maintained F: Documentation/devicetree/bindings/iio/proximity/maxbotix,mb1232.yaml F: drivers/iio/proximity/mb1232.c +MAXIM MAX11205 DRIVER +M: Ramona Bolboaca +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/adc/maxim,max11205.yaml +F: drivers/iio/adc/max11205.c + MAXIM MAX17040 FAMILY FUEL GAUGE DRIVERS R: Iskren Chernev R: Krzysztof Kozlowski -- cgit v1.2.3 From 0fea1007f0e68764aa18822bb6fa83d3809739e8 Mon Sep 17 00:00:00 2001 From: Ramona Bolboaca Date: Wed, 31 Aug 2022 16:30:21 +0300 Subject: iio: adc: add max11205 adc driver Adding support for max11205 16-bit single-channel ultra-low power delta-sigma adc. The MAX11205 is compatible with the 2-wire interface and uses SCLK and RDY/DOUT for serial communications. In this mode, all controls are implemented by timing the high or low phase of the SCLK. The 2-wire serial interface only allows for data to be read out through the RDY/DOUT output. Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX11205.pdf Signed-off-by: Ramona Bolboaca Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220831133021.215625-2-ramona.bolboaca@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 14 ++++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/max11205.c | 183 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 drivers/iio/adc/max11205.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index e3c2881ed23a..56e3ca3b4a5d 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -653,6 +653,20 @@ config MAX1118 To compile this driver as a module, choose M here: the module will be called max1118. +config MAX11205 + tristate "Maxim max11205 ADC driver" + depends on SPI + select AD_SIGMA_DELTA + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + + help + Say yes here to build support for Maxim max11205 16-bit, single-channel + ultra-low power delta-sigma ADC. + + To compile this driver as a module, choose M here: the module will be + called max11205. + config MAX1241 tristate "Maxim max1241 ADC driver" depends on SPI_MASTER diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index ab084094263b..46caba7a010c 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_LTC2497) += ltc2497.o ltc2497-core.o obj-$(CONFIG_MAX1027) += max1027.o obj-$(CONFIG_MAX11100) += max11100.o obj-$(CONFIG_MAX1118) += max1118.o +obj-$(CONFIG_MAX11205) += max11205.o obj-$(CONFIG_MAX1241) += max1241.o obj-$(CONFIG_MAX1363) += max1363.o obj-$(CONFIG_MAX9611) += max9611.o diff --git a/drivers/iio/adc/max11205.c b/drivers/iio/adc/max11205.c new file mode 100644 index 000000000000..65fc32971ba5 --- /dev/null +++ b/drivers/iio/adc/max11205.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim MAX11205 16-Bit Delta-Sigma ADC + * + * Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX1240-max11205.pdf + * Copyright (C) 2022 Analog Devices, Inc. + * Author: Ramona Bolboaca + */ + +#include +#include +#include +#include + +#include +#include + +#define MAX11205_BIT_SCALE 15 +#define MAX11205A_OUT_DATA_RATE 116 +#define MAX11205B_OUT_DATA_RATE 13 + +enum max11205_chip_type { + TYPE_MAX11205A, + TYPE_MAX11205B, +}; + +struct max11205_chip_info { + unsigned int out_data_rate; + const char *name; +}; + +struct max11205_state { + const struct max11205_chip_info *chip_info; + struct regulator *vref; + struct ad_sigma_delta sd; +}; + +static const struct ad_sigma_delta_info max11205_sigma_delta_info = { + .has_registers = false, +}; + +static int max11205_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct max11205_state *st = iio_priv(indio_dev); + int reg_mv; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return ad_sigma_delta_single_conversion(indio_dev, chan, val); + case IIO_CHAN_INFO_SCALE: + reg_mv = regulator_get_voltage(st->vref); + if (reg_mv < 0) + return reg_mv; + reg_mv /= 1000; + *val = reg_mv; + *val2 = MAX11205_BIT_SCALE; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->chip_info->out_data_rate; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static const struct iio_info max11205_iio_info = { + .read_raw = max11205_read_raw, + .validate_trigger = ad_sd_validate_trigger, +}; + +static const struct iio_chan_spec max11205_channels[] = { + { + .type = IIO_VOLTAGE, + .indexed = 1, + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_BE, + }, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static const struct max11205_chip_info max11205_chip_info[] = { + [TYPE_MAX11205A] = { + .out_data_rate = MAX11205A_OUT_DATA_RATE, + .name = "max11205a", + }, + [TYPE_MAX11205B] = { + .out_data_rate = MAX11205B_OUT_DATA_RATE, + .name = "max11205b", + }, +}; + +static void max11205_reg_disable(void *reg) +{ + regulator_disable(reg); +} + +static int max11205_probe(struct spi_device *spi) +{ + struct max11205_state *st; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + ad_sd_init(&st->sd, indio_dev, spi, &max11205_sigma_delta_info); + + st->chip_info = device_get_match_data(&spi->dev); + if (!st->chip_info) + st->chip_info = + (const struct max11205_chip_info *)spi_get_device_id(spi)->driver_data; + + indio_dev->name = st->chip_info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = max11205_channels; + indio_dev->num_channels = 1; + indio_dev->info = &max11205_iio_info; + + st->vref = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(st->vref)) + return dev_err_probe(&spi->dev, PTR_ERR(st->vref), + "Failed to get vref regulator\n"); + + ret = regulator_enable(st->vref); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, max11205_reg_disable, st->vref); + if (ret) + return ret; + + ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id max11205_spi_ids[] = { + { "max11205a", (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205A] }, + { "max11205b", (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205B] }, + { } +}; +MODULE_DEVICE_TABLE(spi, max11205_spi_ids); + +static const struct of_device_id max11205_dt_ids[] = { + { + .compatible = "maxim,max11205a", + .data = &max11205_chip_info[TYPE_MAX11205A], + }, + { + .compatible = "maxim,max11205b", + .data = &max11205_chip_info[TYPE_MAX11205B], + }, + { } +}; +MODULE_DEVICE_TABLE(of, max11205_dt_ids); + +static struct spi_driver max11205_spi_driver = { + .driver = { + .name = "max11205", + .of_match_table = max11205_dt_ids, + }, + .probe = max11205_probe, + .id_table = max11205_spi_ids, +}; +module_spi_driver(max11205_spi_driver); + +MODULE_AUTHOR("Ramona Bolboaca "); +MODULE_DESCRIPTION("MAX11205 ADC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA); -- cgit v1.2.3 From 2bc9cd66eb25d0fefbb081421d6586495e25840e Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Mon, 29 Aug 2022 11:18:40 +0200 Subject: iio: Use per-device lockdep class for mlock If an IIO driver uses callbacks from another IIO driver and calls iio_channel_start_all_cb() from one of its buffer setup ops, then lockdep complains due to the lock nesting, as in the below example with lmp91000. Since the locks are being taken on different IIO devices, there is no actual deadlock. Fix the warning by telling lockdep to use a different class for each iio_device. ============================================ WARNING: possible recursive locking detected -------------------------------------------- python3/23 is trying to acquire lock: (&indio_dev->mlock){+.+.}-{3:3}, at: iio_update_buffers but task is already holding lock: (&indio_dev->mlock){+.+.}-{3:3}, at: enable_store other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(&indio_dev->mlock); lock(&indio_dev->mlock); *** DEADLOCK *** May be due to missing lock nesting notation 5 locks held by python3/23: #0: (sb_writers#5){.+.+}-{0:0}, at: ksys_write #1: (&of->mutex){+.+.}-{3:3}, at: kernfs_fop_write_iter #2: (kn->active#14){.+.+}-{0:0}, at: kernfs_fop_write_iter #3: (&indio_dev->mlock){+.+.}-{3:3}, at: enable_store #4: (&iio_dev_opaque->info_exist_lock){+.+.}-{3:3}, at: iio_update_buffers Call Trace: __mutex_lock iio_update_buffers iio_channel_start_all_cb lmp91000_buffer_postenable __iio_update_buffers enable_store Fixes: 67e17300dc1d76 ("iio: potentiostat: add LMP91000 support") Signed-off-by: Vincent Whitchurch Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220829091840.2791846-1-vincent.whitchurch@axis.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 5 +++++ include/linux/iio/iio-opaque.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index d38623c046cc..583e0e5205a0 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1626,6 +1626,8 @@ static void iio_dev_release(struct device *device) iio_device_detach_buffers(indio_dev); + lockdep_unregister_key(&iio_dev_opaque->mlock_key); + ida_free(&iio_ida, iio_dev_opaque->id); kfree(iio_dev_opaque); } @@ -1685,6 +1687,9 @@ struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv) INIT_LIST_HEAD(&iio_dev_opaque->buffer_list); INIT_LIST_HEAD(&iio_dev_opaque->ioctl_handlers); + lockdep_register_key(&iio_dev_opaque->mlock_key); + lockdep_set_class(&indio_dev->mlock, &iio_dev_opaque->mlock_key); + return indio_dev; } EXPORT_SYMBOL(iio_device_alloc); diff --git a/include/linux/iio/iio-opaque.h b/include/linux/iio/iio-opaque.h index 6b3586b3f952..d1f8b30a7c8b 100644 --- a/include/linux/iio/iio-opaque.h +++ b/include/linux/iio/iio-opaque.h @@ -11,6 +11,7 @@ * checked by device drivers but should be considered * read-only as this is a core internal bit * @driver_module: used to make it harder to undercut users + * @mlock_key: lockdep class for iio_dev lock * @info_exist_lock: lock to prevent use during removal * @trig_readonly: mark the current trigger immutable * @event_interface: event chrdevs associated with interrupt lines @@ -42,6 +43,7 @@ struct iio_dev_opaque { int currentmode; int id; struct module *driver_module; + struct lock_class_key mlock_key; struct mutex info_exist_lock; bool trig_readonly; struct iio_event_interface *event_interface; -- cgit v1.2.3 From 835e699ef82adfc85ac4cc3f1f237c1adfdefd20 Mon Sep 17 00:00:00 2001 From: Jagath Jog J Date: Wed, 31 Aug 2022 12:01:16 +0530 Subject: iio: Add new event type gesture and use direction for single and double tap Add new event type for tap called gesture and the direction can be used to differentiate single and double tap. This may be used by accelerometer sensors to express single and double tap events. For directional tap, modifiers like IIO_MOD_(X/Y/Z) can be used along with singletap and doubletap direction. Signed-off-by: Jagath Jog J Link: https://lore.kernel.org/r/20220831063117.4141-2-jagathjog1996@gmail.com Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 69 +++++++++++++++++++++++++++++++++ drivers/iio/industrialio-event.c | 7 +++- include/linux/iio/types.h | 2 + include/uapi/linux/iio/types.h | 3 ++ tools/iio/iio_event_monitor.c | 8 +++- 5 files changed, 87 insertions(+), 2 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 80e8a38d1ee2..66e81c48ee21 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -2068,3 +2068,72 @@ Description: individual channels. If multiple channels are enabled in a scan, then the sampling_frequency of the scan may be computed from the per channel sampling frequencies. + +What: /sys/.../events/in_accel_gesture_singletap_en +What: /sys/.../events/in_accel_gesture_doubletap_en +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Device generates an event on a single or double tap. + +What: /sys/.../events/in_accel_gesture_singletap_value +What: /sys/.../events/in_accel_gesture_doubletap_value +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Specifies the threshold value that the device is comparing + against to generate the tap gesture event. The lower + threshold value increases the sensitivity of tap detection. + Units and the exact meaning of value are device-specific. + +What: /sys/.../events/in_accel_gesture_tap_value_available +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Lists all available threshold values which can be used to + modify the sensitivity of the tap detection. + +What: /sys/.../events/in_accel_gesture_singletap_reset_timeout +What: /sys/.../events/in_accel_gesture_doubletap_reset_timeout +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Specifies the timeout value in seconds for the tap detector + to not to look for another tap event after the event as + occurred. Basically the minimum quiet time between the two + single-tap's or two double-tap's. + +What: /sys/.../events/in_accel_gesture_tap_reset_timeout_available +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Lists all available tap reset timeout values. Units in seconds. + +What: /sys/.../events/in_accel_gesture_doubletap_tap2_min_delay +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Specifies the minimum quiet time in seconds between the two + taps of a double tap. + +What: /sys/.../events/in_accel_gesture_doubletap_tap2_min_delay_available +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Lists all available delay values between two taps in the double + tap. Units in seconds. + +What: /sys/.../events/in_accel_gesture_tap_maxtomin_time +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Specifies the maximum time difference allowed between upper + and lower peak of tap to consider it as the valid tap event. + Units in seconds. + +What: /sys/.../events/in_accel_gesture_tap_maxtomin_time_available +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Lists all available time values between upper peak to lower + peak. Units in seconds. diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index 0e2056894965..3d78da2531a9 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -231,12 +231,15 @@ static const char * const iio_ev_type_text[] = { [IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive", [IIO_EV_TYPE_CHANGE] = "change", [IIO_EV_TYPE_MAG_REFERENCED] = "mag_referenced", + [IIO_EV_TYPE_GESTURE] = "gesture", }; static const char * const iio_ev_dir_text[] = { [IIO_EV_DIR_EITHER] = "either", [IIO_EV_DIR_RISING] = "rising", - [IIO_EV_DIR_FALLING] = "falling" + [IIO_EV_DIR_FALLING] = "falling", + [IIO_EV_DIR_SINGLETAP] = "singletap", + [IIO_EV_DIR_DOUBLETAP] = "doubletap", }; static const char * const iio_ev_info_text[] = { @@ -247,6 +250,8 @@ static const char * const iio_ev_info_text[] = { [IIO_EV_INFO_HIGH_PASS_FILTER_3DB] = "high_pass_filter_3db", [IIO_EV_INFO_LOW_PASS_FILTER_3DB] = "low_pass_filter_3db", [IIO_EV_INFO_TIMEOUT] = "timeout", + [IIO_EV_INFO_RESET_TIMEOUT] = "reset_timeout", + [IIO_EV_INFO_TAP2_MIN_DELAY] = "tap2_min_delay", }; static enum iio_event_direction iio_ev_attr_dir(struct iio_dev_attr *attr) diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index 27143b03909d..82faa98c719a 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -17,6 +17,8 @@ enum iio_event_info { IIO_EV_INFO_HIGH_PASS_FILTER_3DB, IIO_EV_INFO_LOW_PASS_FILTER_3DB, IIO_EV_INFO_TIMEOUT, + IIO_EV_INFO_RESET_TIMEOUT, + IIO_EV_INFO_TAP2_MIN_DELAY, }; #define IIO_VAL_INT 1 diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h index 472cead10d8d..913864221ac4 100644 --- a/include/uapi/linux/iio/types.h +++ b/include/uapi/linux/iio/types.h @@ -105,6 +105,7 @@ enum iio_event_type { IIO_EV_TYPE_MAG_ADAPTIVE, IIO_EV_TYPE_CHANGE, IIO_EV_TYPE_MAG_REFERENCED, + IIO_EV_TYPE_GESTURE, }; enum iio_event_direction { @@ -112,6 +113,8 @@ enum iio_event_direction { IIO_EV_DIR_RISING, IIO_EV_DIR_FALLING, IIO_EV_DIR_NONE, + IIO_EV_DIR_SINGLETAP, + IIO_EV_DIR_DOUBLETAP, }; #endif /* _UAPI_IIO_TYPES_H_ */ diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index 2f4581658859..b3b3ea399f67 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -69,12 +69,15 @@ static const char * const iio_ev_type_text[] = { [IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive", [IIO_EV_TYPE_CHANGE] = "change", [IIO_EV_TYPE_MAG_REFERENCED] = "mag_referenced", + [IIO_EV_TYPE_GESTURE] = "gesture", }; static const char * const iio_ev_dir_text[] = { [IIO_EV_DIR_EITHER] = "either", [IIO_EV_DIR_RISING] = "rising", - [IIO_EV_DIR_FALLING] = "falling" + [IIO_EV_DIR_FALLING] = "falling", + [IIO_EV_DIR_SINGLETAP] = "singletap", + [IIO_EV_DIR_DOUBLETAP] = "doubletap", }; static const char * const iio_modifier_names[] = { @@ -227,6 +230,7 @@ static bool event_is_known(struct iio_event_data *event) case IIO_EV_TYPE_THRESH_ADAPTIVE: case IIO_EV_TYPE_MAG_ADAPTIVE: case IIO_EV_TYPE_CHANGE: + case IIO_EV_TYPE_GESTURE: break; default: return false; @@ -236,6 +240,8 @@ static bool event_is_known(struct iio_event_data *event) case IIO_EV_DIR_EITHER: case IIO_EV_DIR_RISING: case IIO_EV_DIR_FALLING: + case IIO_EV_DIR_SINGLETAP: + case IIO_EV_DIR_DOUBLETAP: case IIO_EV_DIR_NONE: break; default: -- cgit v1.2.3 From 961db2da159d5191d2a1d9a7cf5ddf1672621a2b Mon Sep 17 00:00:00 2001 From: Jagath Jog J Date: Wed, 31 Aug 2022 12:01:17 +0530 Subject: iio: accel: bma400: Add support for single and double tap events Add support for single and double tap events based on the tap threshold value, minimum quiet time before and after the tap and minimum time between the taps in the double tap. The INT1 pin is used to interrupt and the event is pushed to userspace. Signed-off-by: Jagath Jog J Link: https://lore.kernel.org/r/20220831063117.4141-3-jagathjog1996@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bma400.h | 14 ++ drivers/iio/accel/bma400_core.c | 343 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 347 insertions(+), 10 deletions(-) diff --git a/drivers/iio/accel/bma400.h b/drivers/iio/accel/bma400.h index e8f802a82300..36edbaff4f7f 100644 --- a/drivers/iio/accel/bma400.h +++ b/drivers/iio/accel/bma400.h @@ -40,6 +40,7 @@ #define BMA400_INT_STAT1_REG 0x0f #define BMA400_INT_STAT2_REG 0x10 #define BMA400_INT12_MAP_REG 0x23 +#define BMA400_INT_ENG_OVRUN_MSK BIT(4) /* Temperature register */ #define BMA400_TEMP_DATA_REG 0x11 @@ -105,6 +106,19 @@ #define BMA400_INT_GEN2_MSK BIT(3) #define BMA400_GEN_HYST_MSK GENMASK(1, 0) +/* TAP config registers */ +#define BMA400_TAP_CONFIG 0x57 +#define BMA400_TAP_CONFIG1 0x58 +#define BMA400_S_TAP_MSK BIT(2) +#define BMA400_D_TAP_MSK BIT(3) +#define BMA400_INT_S_TAP_MSK BIT(10) +#define BMA400_INT_D_TAP_MSK BIT(11) +#define BMA400_TAP_SEN_MSK GENMASK(2, 0) +#define BMA400_TAP_TICSTH_MSK GENMASK(1, 0) +#define BMA400_TAP_QUIET_MSK GENMASK(3, 2) +#define BMA400_TAP_QUIETDT_MSK GENMASK(5, 4) +#define BMA400_TAP_TIM_LIST_LEN 4 + /* * BMA400_SCALE_MIN macro value represents m/s^2 for 1 LSB before * converting to micro values for +-2g range. diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c index c31bdd9b168e..eceb1f8d338d 100644 --- a/drivers/iio/accel/bma400_core.c +++ b/drivers/iio/accel/bma400_core.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,27 @@ static int bma400_sample_freqs[14]; static const int bma400_osr_range[] = { 0, 1, 3 }; +static int tap_reset_timeout[BMA400_TAP_TIM_LIST_LEN] = { + 300000, + 400000, + 500000, + 600000 +}; + +static int tap_max2min_time[BMA400_TAP_TIM_LIST_LEN] = { + 30000, + 45000, + 60000, + 90000 +}; + +static int double_tap2_min_delay[BMA400_TAP_TIM_LIST_LEN] = { + 20000, + 40000, + 60000, + 80000 +}; + /* See the ACC_CONFIG0 section of the datasheet */ enum bma400_power_mode { POWER_MODE_SLEEP = 0x00, @@ -88,6 +110,7 @@ struct bma400_data { bool step_event_en; bool activity_event_en; unsigned int generic_event_en; + unsigned int tap_event_en_bitmask; /* Correct time stamp alignment */ struct { __le16 buff[3]; @@ -216,6 +239,115 @@ static const struct iio_event_spec bma400_accel_event[] = { BIT(IIO_EV_INFO_HYSTERESIS) | BIT(IIO_EV_INFO_ENABLE), }, + { + .type = IIO_EV_TYPE_GESTURE, + .dir = IIO_EV_DIR_SINGLETAP, + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_RESET_TIMEOUT), + }, + { + .type = IIO_EV_TYPE_GESTURE, + .dir = IIO_EV_DIR_DOUBLETAP, + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_RESET_TIMEOUT) | + BIT(IIO_EV_INFO_TAP2_MIN_DELAY), + }, +}; + +static int usec_to_tapreg_raw(int usec, const int *time_list) +{ + int index; + + for (index = 0; index < BMA400_TAP_TIM_LIST_LEN; index++) { + if (usec == time_list[index]) + return index; + } + return -EINVAL; +} + +static ssize_t in_accel_gesture_tap_maxtomin_time_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct bma400_data *data = iio_priv(indio_dev); + int ret, reg_val, raw, vals[2]; + + ret = regmap_read(data->regmap, BMA400_TAP_CONFIG1, ®_val); + if (ret) + return ret; + + raw = FIELD_GET(BMA400_TAP_TICSTH_MSK, reg_val); + vals[0] = 0; + vals[1] = tap_max2min_time[raw]; + + return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, vals); +} + +static ssize_t in_accel_gesture_tap_maxtomin_time_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 bma400_data *data = iio_priv(indio_dev); + int ret, val_int, val_fract, raw; + + ret = iio_str_to_fixpoint(buf, 100000, &val_int, &val_fract); + if (ret) + return ret; + + raw = usec_to_tapreg_raw(val_fract, tap_max2min_time); + if (raw < 0) + return -EINVAL; + + ret = regmap_update_bits(data->regmap, BMA400_TAP_CONFIG1, + BMA400_TAP_TICSTH_MSK, + FIELD_PREP(BMA400_TAP_TICSTH_MSK, raw)); + if (ret) + return ret; + + return len; +} + +static IIO_DEVICE_ATTR_RW(in_accel_gesture_tap_maxtomin_time, 0); + +/* + * Tap interrupts works with 200 Hz input data rate and the time based tap + * controls are in the terms of data samples so the below calculation is + * used to convert the configuration values into seconds. + * e.g.: + * 60 data samples * 0.005 ms = 0.3 seconds. + * 80 data samples * 0.005 ms = 0.4 seconds. + */ + +/* quiet configuration values in seconds */ +static IIO_CONST_ATTR(in_accel_gesture_tap_reset_timeout_available, + "0.3 0.4 0.5 0.6"); + +/* tics_th configuration values in seconds */ +static IIO_CONST_ATTR(in_accel_gesture_tap_maxtomin_time_available, + "0.03 0.045 0.06 0.09"); + +/* quiet_dt configuration values in seconds */ +static IIO_CONST_ATTR(in_accel_gesture_doubletap_tap2_min_delay_available, + "0.02 0.04 0.06 0.08"); + +/* List of sensitivity values available to configure tap interrupts */ +static IIO_CONST_ATTR(in_accel_gesture_tap_value_available, "0 1 2 3 4 5 6 7"); + +static struct attribute *bma400_event_attributes[] = { + &iio_const_attr_in_accel_gesture_tap_value_available.dev_attr.attr, + &iio_const_attr_in_accel_gesture_tap_reset_timeout_available.dev_attr.attr, + &iio_const_attr_in_accel_gesture_tap_maxtomin_time_available.dev_attr.attr, + &iio_const_attr_in_accel_gesture_doubletap_tap2_min_delay_available.dev_attr.attr, + &iio_dev_attr_in_accel_gesture_tap_maxtomin_time.dev_attr.attr, + NULL +}; + +static const struct attribute_group bma400_event_attribute_group = { + .attrs = bma400_event_attributes, }; #define BMA400_ACC_CHANNEL(_index, _axis) { \ @@ -1012,6 +1144,12 @@ static int bma400_read_event_config(struct iio_dev *indio_dev, case IIO_EV_DIR_FALLING: return FIELD_GET(BMA400_INT_GEN2_MSK, data->generic_event_en); + case IIO_EV_DIR_SINGLETAP: + return FIELD_GET(BMA400_S_TAP_MSK, + data->tap_event_en_bitmask); + case IIO_EV_DIR_DOUBLETAP: + return FIELD_GET(BMA400_D_TAP_MSK, + data->tap_event_en_bitmask); default: return -EINVAL; } @@ -1101,6 +1239,80 @@ static int bma400_activity_event_en(struct bma400_data *data, return 0; } +static int bma400_tap_event_en(struct bma400_data *data, + enum iio_event_direction dir, int state) +{ + unsigned int mask, field_value; + int ret; + + /* + * Tap interrupts can be configured only in normal mode. + * See table in section 4.3 "Power modes - performance modes" of + * datasheet v1.2. + */ + if (data->power_mode != POWER_MODE_NORMAL) + return -EINVAL; + + /* + * Tap interrupts are operating with a data rate of 200Hz. + * See section 4.7 "Tap sensing interrupt" in datasheet v1.2. + */ + if (data->sample_freq.hz != 200 && state) { + dev_err(data->dev, "Invalid data rate for tap interrupts.\n"); + return -EINVAL; + } + + ret = regmap_update_bits(data->regmap, BMA400_INT12_MAP_REG, + BMA400_S_TAP_MSK, + FIELD_PREP(BMA400_S_TAP_MSK, state)); + if (ret) + return ret; + + switch (dir) { + case IIO_EV_DIR_SINGLETAP: + mask = BMA400_S_TAP_MSK; + set_mask_bits(&field_value, BMA400_S_TAP_MSK, + FIELD_PREP(BMA400_S_TAP_MSK, state)); + break; + case IIO_EV_DIR_DOUBLETAP: + mask = BMA400_D_TAP_MSK; + set_mask_bits(&field_value, BMA400_D_TAP_MSK, + FIELD_PREP(BMA400_D_TAP_MSK, state)); + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(data->regmap, BMA400_INT_CONFIG1_REG, mask, + field_value); + if (ret) + return ret; + + set_mask_bits(&data->tap_event_en_bitmask, mask, field_value); + + return 0; +} + +static int bma400_disable_adv_interrupt(struct bma400_data *data) +{ + int ret; + + ret = regmap_write(data->regmap, BMA400_INT_CONFIG0_REG, 0); + if (ret) + return ret; + + ret = regmap_write(data->regmap, BMA400_INT_CONFIG1_REG, 0); + if (ret) + return ret; + + data->tap_event_en_bitmask = 0; + data->generic_event_en = 0; + data->step_event_en = false; + data->activity_event_en = false; + + return 0; +} + static int bma400_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, @@ -1111,10 +1323,20 @@ static int bma400_write_event_config(struct iio_dev *indio_dev, switch (chan->type) { case IIO_ACCEL: - mutex_lock(&data->mutex); - ret = bma400_activity_event_en(data, dir, state); - mutex_unlock(&data->mutex); - return ret; + switch (type) { + case IIO_EV_TYPE_MAG: + mutex_lock(&data->mutex); + ret = bma400_activity_event_en(data, dir, state); + mutex_unlock(&data->mutex); + return ret; + case IIO_EV_TYPE_GESTURE: + mutex_lock(&data->mutex); + ret = bma400_tap_event_en(data, dir, state); + mutex_unlock(&data->mutex); + return ret; + default: + return -EINVAL; + } case IIO_STEPS: mutex_lock(&data->mutex); ret = bma400_steps_event_enable(data, state); @@ -1157,10 +1379,13 @@ static int bma400_read_event_value(struct iio_dev *indio_dev, int *val, int *val2) { struct bma400_data *data = iio_priv(indio_dev); - int ret, reg; + int ret, reg, reg_val, raw; - switch (chan->type) { - case IIO_ACCEL: + if (chan->type != IIO_ACCEL) + return -EINVAL; + + switch (type) { + case IIO_EV_TYPE_MAG: reg = get_gen_config_reg(dir); if (reg < 0) return -EINVAL; @@ -1196,6 +1421,39 @@ static int bma400_read_event_value(struct iio_dev *indio_dev, default: return -EINVAL; } + case IIO_EV_TYPE_GESTURE: + switch (info) { + case IIO_EV_INFO_VALUE: + ret = regmap_read(data->regmap, BMA400_TAP_CONFIG, + ®_val); + if (ret) + return ret; + + *val = FIELD_GET(BMA400_TAP_SEN_MSK, reg_val); + return IIO_VAL_INT; + case IIO_EV_INFO_RESET_TIMEOUT: + ret = regmap_read(data->regmap, BMA400_TAP_CONFIG1, + ®_val); + if (ret) + return ret; + + raw = FIELD_GET(BMA400_TAP_QUIET_MSK, reg_val); + *val = 0; + *val2 = tap_reset_timeout[raw]; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_EV_INFO_TAP2_MIN_DELAY: + ret = regmap_read(data->regmap, BMA400_TAP_CONFIG1, + ®_val); + if (ret) + return ret; + + raw = FIELD_GET(BMA400_TAP_QUIETDT_MSK, reg_val); + *val = 0; + *val2 = double_tap2_min_delay[raw]; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } default: return -EINVAL; } @@ -1209,10 +1467,13 @@ static int bma400_write_event_value(struct iio_dev *indio_dev, int val, int val2) { struct bma400_data *data = iio_priv(indio_dev); - int reg, ret; + int reg, ret, raw; - switch (chan->type) { - case IIO_ACCEL: + if (chan->type != IIO_ACCEL) + return -EINVAL; + + switch (type) { + case IIO_EV_TYPE_MAG: reg = get_gen_config_reg(dir); if (reg < 0) return -EINVAL; @@ -1248,6 +1509,40 @@ static int bma400_write_event_value(struct iio_dev *indio_dev, default: return -EINVAL; } + case IIO_EV_TYPE_GESTURE: + switch (info) { + case IIO_EV_INFO_VALUE: + if (val < 0 || val > 7) + return -EINVAL; + + return regmap_update_bits(data->regmap, + BMA400_TAP_CONFIG, + BMA400_TAP_SEN_MSK, + FIELD_PREP(BMA400_TAP_SEN_MSK, + val)); + case IIO_EV_INFO_RESET_TIMEOUT: + raw = usec_to_tapreg_raw(val2, tap_reset_timeout); + if (raw < 0) + return -EINVAL; + + return regmap_update_bits(data->regmap, + BMA400_TAP_CONFIG1, + BMA400_TAP_QUIET_MSK, + FIELD_PREP(BMA400_TAP_QUIET_MSK, + raw)); + case IIO_EV_INFO_TAP2_MIN_DELAY: + raw = usec_to_tapreg_raw(val2, double_tap2_min_delay); + if (raw < 0) + return -EINVAL; + + return regmap_update_bits(data->regmap, + BMA400_TAP_CONFIG1, + BMA400_TAP_QUIETDT_MSK, + FIELD_PREP(BMA400_TAP_QUIETDT_MSK, + raw)); + default: + return -EINVAL; + } default: return -EINVAL; } @@ -1287,6 +1582,7 @@ static const struct iio_info bma400_info = { .write_event_config = bma400_write_event_config, .write_event_value = bma400_write_event_value, .read_event_value = bma400_read_event_value, + .event_attrs = &bma400_event_attribute_group, }; static const struct iio_trigger_ops bma400_trigger_ops = { @@ -1350,6 +1646,32 @@ static irqreturn_t bma400_interrupt(int irq, void *private) if (ret || !data->status) goto unlock_err; + /* + * Disable all advance interrupts if interrupt engine overrun occurs. + * See section 4.7 "Interrupt engine overrun" in datasheet v1.2. + */ + if (FIELD_GET(BMA400_INT_ENG_OVRUN_MSK, le16_to_cpu(data->status))) { + bma400_disable_adv_interrupt(data); + dev_err(data->dev, "Interrupt engine overrun\n"); + goto unlock_err; + } + + if (FIELD_GET(BMA400_INT_S_TAP_MSK, le16_to_cpu(data->status))) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, + IIO_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_GESTURE, + IIO_EV_DIR_SINGLETAP), + timestamp); + + if (FIELD_GET(BMA400_INT_D_TAP_MSK, le16_to_cpu(data->status))) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, + IIO_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_GESTURE, + IIO_EV_DIR_DOUBLETAP), + timestamp); + if (FIELD_GET(BMA400_INT_GEN1_MSK, le16_to_cpu(data->status))) ev_dir = IIO_EV_DIR_RISING; @@ -1467,5 +1789,6 @@ int bma400_probe(struct device *dev, struct regmap *regmap, int irq, EXPORT_SYMBOL_NS(bma400_probe, IIO_BMA400); MODULE_AUTHOR("Dan Robertson "); +MODULE_AUTHOR("Jagath Jog J "); MODULE_DESCRIPTION("Bosch BMA400 triaxial acceleration sensor core"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From c13219cececed248df6feacb0d33326266234b7b Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 21 Aug 2022 17:10:58 +0100 Subject: iio: adc: max1363: Drop provision to provide an IIO channel map via platform data Back in the days of board files, platform data was used to provide information on the mapping from ADC channel to an analog signal from another device. We've long since moved to doing this via device tree. Hence drop the support from the max1363 driver which is the only driver still providing this. Signed-off-by: Jonathan Cameron Link: https://lore.kernel.org/r/20220821161058.2207185-1-jic23@kernel.org --- drivers/iio/adc/max1363.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index eef55ed4814a..a28cf86cdce8 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -1595,11 +1594,6 @@ static int max1363_probe(struct i2c_client *client, if (!indio_dev) return -ENOMEM; - ret = devm_iio_map_array_register(&client->dev, indio_dev, - client->dev.platform_data); - if (ret < 0) - return ret; - st = iio_priv(indio_dev); mutex_init(&st->lock); -- cgit v1.2.3 From 2f61ff8272967c9bdcba810aa978170814b08f7c Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 7 Aug 2022 20:04:13 +0100 Subject: iio: pressure: icp10100: Switch from UNIVERSAL to DEFINE_RUNTIME_DEV_PM_OPS(). The suspend and resume callbacks in this driver appear to be safe to call repeatedly, but why do so when we can use the DEFINE_RUNTIME_DEV_PM_OPS() macro to supply callbacks that check if we are already runtime suspended before doing unnecessary work. Signed-off-by: Jonathan Cameron Cc: Jean-Baptiste Maneyrol Acked-by: Jean-Baptiste Maneyrol Link: https://lore.kernel.org/r/20220807190414.1039028-2-jic23@kernel.org --- drivers/iio/pressure/icp10100.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/iio/pressure/icp10100.c b/drivers/iio/pressure/icp10100.c index af4621eaa6b5..b62f28585db5 100644 --- a/drivers/iio/pressure/icp10100.c +++ b/drivers/iio/pressure/icp10100.c @@ -595,7 +595,7 @@ static int icp10100_probe(struct i2c_client *client, return devm_iio_device_register(&client->dev, indio_dev); } -static int __maybe_unused icp10100_suspend(struct device *dev) +static int icp10100_suspend(struct device *dev) { struct icp10100_state *st = iio_priv(dev_get_drvdata(dev)); int ret; @@ -607,7 +607,7 @@ static int __maybe_unused icp10100_suspend(struct device *dev) return ret; } -static int __maybe_unused icp10100_resume(struct device *dev) +static int icp10100_resume(struct device *dev) { struct icp10100_state *st = iio_priv(dev_get_drvdata(dev)); int ret; @@ -626,8 +626,8 @@ out_unlock: return ret; } -static UNIVERSAL_DEV_PM_OPS(icp10100_pm, icp10100_suspend, icp10100_resume, - NULL); +static DEFINE_RUNTIME_DEV_PM_OPS(icp10100_pm, icp10100_suspend, icp10100_resume, + NULL); static const struct of_device_id icp10100_of_match[] = { { @@ -646,7 +646,7 @@ MODULE_DEVICE_TABLE(i2c, icp10100_id); static struct i2c_driver icp10100_driver = { .driver = { .name = "icp10100", - .pm = &icp10100_pm, + .pm = pm_ptr(&icp10100_pm), .of_match_table = icp10100_of_match, }, .probe = icp10100_probe, -- cgit v1.2.3 From e3c9b0ddfd144663e4d21d6d95e9c76a45c49e3f Mon Sep 17 00:00:00 2001 From: Kumaravel Thiagarajan Date: Tue, 6 Sep 2022 18:19:51 +0530 Subject: misc: microchip: pci1xxxx: fix dependency issues in building the pci1xxxx's aux bus driver. build errors and warnings listed below and reported by kernel test robot on the char-misc-next branch are fixed in this add-on patch. errors: ERROR: modpost: "auxiliary_device_init" [drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.ko] undefined! ERROR: modpost: "__auxiliary_device_add" [drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.ko] undefined! ERROR: modpost: "auxiliary_driver_unregister" [drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.ko] undefined! ERROR: modpost: "__auxiliary_driver_register" [drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.ko] undefined! ia64-linux-ld: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.o: in function `gp_aux_bus_probe.part.0': mchp_pci1xxxx_gp.c:(.text+0x342): undefined reference to `auxiliary_device_init' ia64-linux-ld: mchp_pci1xxxx_gp.c:(.text+0x392): undefined reference to `__auxiliary_device_add' ia64-linux-ld: mchp_pci1xxxx_gp.c:(.text+0x5c2): undefined reference to `auxiliary_device_init' ia64-linux-ld: mchp_pci1xxxx_gp.c:(.text+0x612): undefined reference to `__auxiliary_device_add' ia64-linux-ld: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.o: in function `pci1xxxx_gpio_driver_init': mchp_pci1xxxx_gpio.c:(.init.text+0x42): undefined reference to `__auxiliary_driver_register' warnings: unmet direct dependencies detected for GPIOLIB_IRQCHIP when selected by GP_PCI1XXXX Fixes: 393fc2f5948f ("misc: microchip: pci1xxxx: load auxiliary bus driver for the PIO function in the multi-function endpoint of pci1xxxx device.") Reported-by: kernel test robot Signed-off-by: Kumaravel Thiagarajan Link: https://lore.kernel.org/r/20220906124951.696776-1-kumaravel.thiagarajan@microchip.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mchp_pci1xxxx/Kconfig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/misc/mchp_pci1xxxx/Kconfig b/drivers/misc/mchp_pci1xxxx/Kconfig index f6a6970d2a59..4abb47de7219 100644 --- a/drivers/misc/mchp_pci1xxxx/Kconfig +++ b/drivers/misc/mchp_pci1xxxx/Kconfig @@ -1,7 +1,9 @@ config GP_PCI1XXXX tristate "Microchip PCI1XXXX PCIe to GPIO Expander + OTP/EEPROM manager" - depends on PCI + depends on PCI + depends on GPIOLIB select GPIOLIB_IRQCHIP + select AUXILIARY_BUS help PCI1XXXX is a PCIe GEN 3 switch with one of the endpoints having multiple functions and one of the functions is a GPIO controller -- cgit v1.2.3 From e66b77e50522845d494a4a61ea2ad8f0d046a56f Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Tue, 6 Sep 2022 13:59:45 +0000 Subject: binder: rename alloc->vma_vm_mm to alloc->mm Rename ->vma_vm_mm to ->mm to reflect the fact that we no longer cache this reference from vma->vm_mm but from current->mm instead. No functional changes in this patch. Reviewed-by: Christian Brauner (Microsoft) Acked-by: Todd Kjos Signed-off-by: Carlos Llamas Link: https://lore.kernel.org/r/20220906135948.3048225-2-cmllamas@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder_alloc.c | 34 +++++++++++++++++----------------- drivers/android/binder_alloc.h | 4 ++-- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index 9b1778c00610..749a4cd30a83 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -208,8 +208,8 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate, } } - if (need_mm && mmget_not_zero(alloc->vma_vm_mm)) - mm = alloc->vma_vm_mm; + if (need_mm && mmget_not_zero(alloc->mm)) + mm = alloc->mm; if (mm) { mmap_read_lock(mm); @@ -322,9 +322,9 @@ static inline void binder_alloc_set_vma(struct binder_alloc *alloc, */ if (vma) { vm_start = vma->vm_start; - mmap_assert_write_locked(alloc->vma_vm_mm); + mmap_assert_write_locked(alloc->mm); } else { - mmap_assert_locked(alloc->vma_vm_mm); + mmap_assert_locked(alloc->mm); } alloc->vma_addr = vm_start; @@ -336,7 +336,7 @@ static inline struct vm_area_struct *binder_alloc_get_vma( struct vm_area_struct *vma = NULL; if (alloc->vma_addr) - vma = vma_lookup(alloc->vma_vm_mm, alloc->vma_addr); + vma = vma_lookup(alloc->mm, alloc->vma_addr); return vma; } @@ -401,15 +401,15 @@ static struct binder_buffer *binder_alloc_new_buf_locked( size_t size, data_offsets_size; int ret; - mmap_read_lock(alloc->vma_vm_mm); + mmap_read_lock(alloc->mm); if (!binder_alloc_get_vma(alloc)) { - mmap_read_unlock(alloc->vma_vm_mm); + mmap_read_unlock(alloc->mm); binder_alloc_debug(BINDER_DEBUG_USER_ERROR, "%d: binder_alloc_buf, no vma\n", alloc->pid); return ERR_PTR(-ESRCH); } - mmap_read_unlock(alloc->vma_vm_mm); + mmap_read_unlock(alloc->mm); data_offsets_size = ALIGN(data_size, sizeof(void *)) + ALIGN(offsets_size, sizeof(void *)); @@ -823,7 +823,7 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc) buffers = 0; mutex_lock(&alloc->mutex); BUG_ON(alloc->vma_addr && - vma_lookup(alloc->vma_vm_mm, alloc->vma_addr)); + vma_lookup(alloc->mm, alloc->vma_addr)); while ((n = rb_first(&alloc->allocated_buffers))) { buffer = rb_entry(n, struct binder_buffer, rb_node); @@ -873,8 +873,8 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc) kfree(alloc->pages); } mutex_unlock(&alloc->mutex); - if (alloc->vma_vm_mm) - mmdrop(alloc->vma_vm_mm); + if (alloc->mm) + mmdrop(alloc->mm); binder_alloc_debug(BINDER_DEBUG_OPEN_CLOSE, "%s: %d buffers %d, pages %d\n", @@ -931,13 +931,13 @@ void binder_alloc_print_pages(struct seq_file *m, * read inconsistent state. */ - mmap_read_lock(alloc->vma_vm_mm); + mmap_read_lock(alloc->mm); if (binder_alloc_get_vma(alloc) == NULL) { - mmap_read_unlock(alloc->vma_vm_mm); + mmap_read_unlock(alloc->mm); goto uninitialized; } - mmap_read_unlock(alloc->vma_vm_mm); + mmap_read_unlock(alloc->mm); for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) { page = &alloc->pages[i]; if (!page->page_ptr) @@ -1020,7 +1020,7 @@ enum lru_status binder_alloc_free_page(struct list_head *item, index = page - alloc->pages; page_addr = (uintptr_t)alloc->buffer + index * PAGE_SIZE; - mm = alloc->vma_vm_mm; + mm = alloc->mm; if (!mmget_not_zero(mm)) goto err_mmget; if (!mmap_read_trylock(mm)) @@ -1089,8 +1089,8 @@ static struct shrinker binder_shrinker = { void binder_alloc_init(struct binder_alloc *alloc) { alloc->pid = current->group_leader->pid; - alloc->vma_vm_mm = current->mm; - mmgrab(alloc->vma_vm_mm); + alloc->mm = current->mm; + mmgrab(alloc->mm); mutex_init(&alloc->mutex); INIT_LIST_HEAD(&alloc->buffers); } diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h index f61a12d5c1e7..ab3b027bcd29 100644 --- a/drivers/android/binder_alloc.h +++ b/drivers/android/binder_alloc.h @@ -78,7 +78,7 @@ struct binder_lru_page { * (invariant after mmap) * @tsk: tid for task that called init for this proc * (invariant after init) - * @vma_vm_mm: copy of vma->vm_mm (invariant after mmap) + * @mm: copy of task->mm (invariant after open) * @buffer: base of per-proc address space mapped via mmap * @buffers: list of all buffers for this proc * @free_buffers: rb tree of buffers available for allocation @@ -101,7 +101,7 @@ struct binder_lru_page { struct binder_alloc { struct mutex mutex; unsigned long vma_addr; - struct mm_struct *vma_vm_mm; + struct mm_struct *mm; void __user *buffer; struct list_head buffers; struct rb_root free_buffers; -- cgit v1.2.3 From d6d04d71daae9377129b13641f97ba2a961b2b26 Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Tue, 6 Sep 2022 13:59:46 +0000 Subject: binder: remove binder_alloc_set_vma() The mmap_locked asserts here are not needed since this is only called back from the mmap stack in ->mmap() and ->close() which always acquire the lock first. Remove these asserts along with binder_alloc_set_vma() altogether since it's trivial enough to be consumed by callers. Cc: Liam R. Howlett Signed-off-by: Carlos Llamas Link: https://lore.kernel.org/r/20220906135948.3048225-3-cmllamas@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder_alloc.c | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index 749a4cd30a83..1c39cfce32fa 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -309,27 +309,6 @@ err_no_vma: return vma ? -ENOMEM : -ESRCH; } - -static inline void binder_alloc_set_vma(struct binder_alloc *alloc, - struct vm_area_struct *vma) -{ - unsigned long vm_start = 0; - - /* - * Allow clearing the vma with holding just the read lock to allow - * munmapping downgrade of the write lock before freeing and closing the - * file using binder_alloc_vma_close(). - */ - if (vma) { - vm_start = vma->vm_start; - mmap_assert_write_locked(alloc->mm); - } else { - mmap_assert_locked(alloc->mm); - } - - alloc->vma_addr = vm_start; -} - static inline struct vm_area_struct *binder_alloc_get_vma( struct binder_alloc *alloc) { @@ -793,7 +772,7 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc, buffer->free = 1; binder_insert_free_buffer(alloc, buffer); alloc->free_async_space = alloc->buffer_size / 2; - binder_alloc_set_vma(alloc, vma); + alloc->vma_addr = vma->vm_start; return 0; @@ -983,7 +962,7 @@ int binder_alloc_get_allocated_count(struct binder_alloc *alloc) */ void binder_alloc_vma_close(struct binder_alloc *alloc) { - binder_alloc_set_vma(alloc, NULL); + alloc->vma_addr = 0; } /** -- cgit v1.2.3 From 7b0dbd94076567170f89172e0f07583915010ac6 Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Tue, 6 Sep 2022 13:59:47 +0000 Subject: binder: fix binder_alloc kernel-doc warnings Update the kernel-doc section of struct binder_alloc to fix the following warnings reported by ./scripts/kernel-doc: warning: Function parameter or member 'mutex' not described in 'binder_alloc' warning: Function parameter or member 'vma_addr' not described in 'binder_alloc' No functional changes in this patch. Reviewed-by: Christian Brauner (Microsoft) Acked-by: Todd Kjos Signed-off-by: Carlos Llamas Link: https://lore.kernel.org/r/20220906135948.3048225-4-cmllamas@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder_alloc.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h index ab3b027bcd29..0f811ac4bcff 100644 --- a/drivers/android/binder_alloc.h +++ b/drivers/android/binder_alloc.h @@ -74,10 +74,9 @@ struct binder_lru_page { /** * struct binder_alloc - per-binder proc state for binder allocator - * @vma: vm_area_struct passed to mmap_handler + * @mutex: protects binder_alloc fields + * @vma_addr: vm_area_struct->vm_start passed to mmap_handler * (invariant after mmap) - * @tsk: tid for task that called init for this proc - * (invariant after init) * @mm: copy of task->mm (invariant after open) * @buffer: base of per-proc address space mapped via mmap * @buffers: list of all buffers for this proc -- cgit v1.2.3 From 269e633dad16c951449c99cc3bfce46110f5029e Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Thu, 1 Sep 2022 12:50:55 -0700 Subject: coresight: cti-sysfs: Mark coresight_cti_reg_store() as __maybe_unused When building without CONFIG_CORESIGHT_CTI_INTEGRATION_REGS, there is a warning about coresight_cti_reg_store() being unused in the file: drivers/hwtracing/coresight/coresight-cti-sysfs.c:184:16: warning: 'coresight_cti_reg_store' defined but not used [-Wunused-function] 184 | static ssize_t coresight_cti_reg_store(struct device *dev, | ^~~~~~~~~~~~~~~~~~~~~~~ This is expected as coresight_cti_reg_store() is only used in the coresight_cti_reg_rw macro, which is only used in a block guarded by CONFIG_CORESIGHT_CTI_INTEGRATION_REGS. Mark coresight_cti_reg_store() as __maybe_unused to clearly indicate that the function may be unused depending on the configuration. Fixes: fbca79e55429 ("coresight: cti-sysfs: Re-use same functions for similar sysfs register accessors") Signed-off-by: Nathan Chancellor Reviewed-by: James Clark Link: https://lore.kernel.org/r/20220901195055.1932340-1-nathan@kernel.org Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-cti-sysfs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index 478b8d38b744..6d59c815ecf5 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -181,9 +181,9 @@ static ssize_t coresight_cti_reg_show(struct device *dev, } /* Write registers with power check only (no enable check). */ -static ssize_t coresight_cti_reg_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) +static __maybe_unused ssize_t coresight_cti_reg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) { struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); struct cs_off_attribute *cti_attr = container_of(attr, struct cs_off_attribute, attr); -- cgit v1.2.3 From a6d0ca93abe3c19aa5b79fd11a832116b6caca26 Mon Sep 17 00:00:00 2001 From: Vishnu Dasa Date: Tue, 6 Sep 2022 10:27:20 -0700 Subject: MAINTAINERS: Change VMware PVSCSI driver entry to upper case Change 'VMware PVSCSI driver' entry to upper case. This is a trivial change being done for uniformity. Signed-off-by: Vishnu Dasa Link: https://lore.kernel.org/r/20220906172722.19862-2-vdasa@vmware.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index f0d70555be17..b5e751760b24 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21714,7 +21714,7 @@ L: linux-rdma@vger.kernel.org S: Maintained F: drivers/infiniband/hw/vmw_pvrdma/ -VMware PVSCSI driver +VMWARE PVSCSI DRIVER M: Vishal Bhakta R: VMware PV-Drivers Reviewers L: linux-scsi@vger.kernel.org -- cgit v1.2.3 From 0f174bc27137f6b6d1eb63e92934ad0b1b404658 Mon Sep 17 00:00:00 2001 From: Vishnu Dasa Date: Tue, 6 Sep 2022 10:27:21 -0700 Subject: MAINTAINERS: Change status of some VMware drivers Change the status from 'Maintained' to 'Supported' for VMWARE BALLOON DRIVER, VMWARE PVRDMA DRIVER, VMWARE PVSCSI driver, VMWARE VMCI DRIVER, VMWARE VMMOUSE SUBDRIVER and VMWARE VMXNET3 ETHERNET DRIVER. This needs to be done to conform to the guidelines in [1]. Maintainers for these drivers are VMware employees. [1] https://docs.kernel.org/process/maintainers.html Acked-by: Nadav Amit Signed-off-by: Vishnu Dasa Link: https://lore.kernel.org/r/20220906172722.19862-3-vdasa@vmware.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index b5e751760b24..6cb4fe83f7be 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21692,7 +21692,7 @@ VMWARE BALLOON DRIVER M: Nadav Amit R: VMware PV-Drivers Reviewers L: linux-kernel@vger.kernel.org -S: Maintained +S: Supported F: drivers/misc/vmw_balloon.c VMWARE HYPERVISOR INTERFACE @@ -21711,14 +21711,14 @@ M: Bryan Tan M: Vishnu Dasa R: VMware PV-Drivers Reviewers L: linux-rdma@vger.kernel.org -S: Maintained +S: Supported F: drivers/infiniband/hw/vmw_pvrdma/ VMWARE PVSCSI DRIVER M: Vishal Bhakta R: VMware PV-Drivers Reviewers L: linux-scsi@vger.kernel.org -S: Maintained +S: Supported F: drivers/scsi/vmw_pvscsi.c F: drivers/scsi/vmw_pvscsi.h @@ -21734,7 +21734,7 @@ M: Bryan Tan M: Vishnu Dasa R: VMware PV-Drivers Reviewers L: linux-kernel@vger.kernel.org -S: Maintained +S: Supported F: drivers/misc/vmw_vmci/ VMWARE VMMOUSE SUBDRIVER @@ -21742,7 +21742,7 @@ M: Zack Rusin R: VMware Graphics Reviewers R: VMware PV-Drivers Reviewers L: linux-input@vger.kernel.org -S: Maintained +S: Supported F: drivers/input/mouse/vmmouse.c F: drivers/input/mouse/vmmouse.h @@ -21750,7 +21750,7 @@ VMWARE VMXNET3 ETHERNET DRIVER M: Ronak Doshi R: VMware PV-Drivers Reviewers L: netdev@vger.kernel.org -S: Maintained +S: Supported F: drivers/net/vmxnet3/ VOCORE VOCORE2 BOARD -- cgit v1.2.3 From d3afabf94ad383303026647db4ec802754ac06ba Mon Sep 17 00:00:00 2001 From: Vishnu Dasa Date: Tue, 6 Sep 2022 10:27:22 -0700 Subject: MAINTAINERS: Add a new entry for VMWARE VSOCK VMCI TRANSPORT DRIVER Add a new entry for VMWARE VSOCK VMCI TRANSPORT DRIVER in the MAINTAINERS file. Acked-by: Stefano Garzarella Signed-off-by: Vishnu Dasa Link: https://lore.kernel.org/r/20220906172722.19862-4-vdasa@vmware.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6cb4fe83f7be..da6d4fd517b0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21753,6 +21753,14 @@ L: netdev@vger.kernel.org S: Supported F: drivers/net/vmxnet3/ +VMWARE VSOCK VMCI TRANSPORT DRIVER +M: Bryan Tan +M: Vishnu Dasa +R: VMware PV-Drivers Reviewers +L: linux-kernel@vger.kernel.org +S: Supported +F: net/vmw_vsock/vmci_transport* + VOCORE VOCORE2 BOARD M: Harvey Hunt L: linux-mips@vger.kernel.org -- cgit v1.2.3 From 24b6c7798a0122012ca848ea0d25e973334266b0 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Tue, 16 Aug 2022 19:44:10 +0800 Subject: iommu/arm-smmu-v3: Make default domain type of HiSilicon PTT device to identity The DMA operations of HiSilicon PTT device can only work properly with identical mappings. So add a quirk for the device to force the domain as passthrough. Acked-by: Will Deacon Signed-off-by: Yicong Yang Reviewed-by: John Garry Link: https://lore.kernel.org/r/20220816114414.4092-2-yangyicong@huawei.com Signed-off-by: Mathieu Poirier --- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c index d32b02336411..71f7edded9cf 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -2817,6 +2817,26 @@ static int arm_smmu_dev_disable_feature(struct device *dev, } } +/* + * HiSilicon PCIe tune and trace device can be used to trace TLP headers on the + * PCIe link and save the data to memory by DMA. The hardware is restricted to + * use identity mapping only. + */ +#define IS_HISI_PTT_DEVICE(pdev) ((pdev)->vendor == PCI_VENDOR_ID_HUAWEI && \ + (pdev)->device == 0xa12e) + +static int arm_smmu_def_domain_type(struct device *dev) +{ + if (dev_is_pci(dev)) { + struct pci_dev *pdev = to_pci_dev(dev); + + if (IS_HISI_PTT_DEVICE(pdev)) + return IOMMU_DOMAIN_IDENTITY; + } + + return 0; +} + static struct iommu_ops arm_smmu_ops = { .capable = arm_smmu_capable, .domain_alloc = arm_smmu_domain_alloc, @@ -2831,6 +2851,7 @@ static struct iommu_ops arm_smmu_ops = { .sva_unbind = arm_smmu_sva_unbind, .sva_get_pasid = arm_smmu_sva_get_pasid, .page_response = arm_smmu_page_response, + .def_domain_type = arm_smmu_def_domain_type, .pgsize_bitmap = -1UL, /* Restricted during device attach */ .owner = THIS_MODULE, .default_domain_ops = &(const struct iommu_domain_ops) { -- cgit v1.2.3 From ff0de066b4632ccb2b2e50f90c0c5be7f4689de7 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Tue, 16 Aug 2022 19:44:11 +0800 Subject: hwtracing: hisi_ptt: Add trace function support for HiSilicon PCIe Tune and Trace device HiSilicon PCIe tune and trace device(PTT) is a PCIe Root Complex integrated Endpoint(RCiEP) device, providing the capability to dynamically monitor and tune the PCIe traffic and trace the TLP headers. Add the driver for the device to enable the trace function. Register PMU device of PTT trace, then users can use trace through perf command. The driver makes use of perf AUX trace function and support the following events to configure the trace: - filter: select Root port or Endpoint to trace - type: select the type of traced TLP headers - direction: select the direction of traced TLP headers - format: select the data format of the traced TLP headers This patch initially add basic trace support of PTT device. Acked-by: Mathieu Poirier Reviewed-by: Jonathan Cameron Reviewed-by: John Garry Signed-off-by: Yicong Yang Link: https://lore.kernel.org/r/20220816114414.4092-3-yangyicong@huawei.com Signed-off-by: Mathieu Poirier --- drivers/Makefile | 1 + drivers/hwtracing/Kconfig | 2 + drivers/hwtracing/ptt/Kconfig | 12 + drivers/hwtracing/ptt/Makefile | 2 + drivers/hwtracing/ptt/hisi_ptt.c | 916 +++++++++++++++++++++++++++++++++++++++ drivers/hwtracing/ptt/hisi_ptt.h | 177 ++++++++ 6 files changed, 1110 insertions(+) create mode 100644 drivers/hwtracing/ptt/Kconfig create mode 100644 drivers/hwtracing/ptt/Makefile create mode 100644 drivers/hwtracing/ptt/hisi_ptt.c create mode 100644 drivers/hwtracing/ptt/hisi_ptt.h diff --git a/drivers/Makefile b/drivers/Makefile index 057857258bfd..bdf1c66141c9 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -175,6 +175,7 @@ obj-$(CONFIG_USB4) += thunderbolt/ obj-$(CONFIG_CORESIGHT) += hwtracing/coresight/ obj-y += hwtracing/intel_th/ obj-$(CONFIG_STM) += hwtracing/stm/ +obj-$(CONFIG_HISI_PTT) += hwtracing/ptt/ obj-y += android/ obj-$(CONFIG_NVMEM) += nvmem/ obj-$(CONFIG_FPGA) += fpga/ diff --git a/drivers/hwtracing/Kconfig b/drivers/hwtracing/Kconfig index 13085835a636..911ee977103c 100644 --- a/drivers/hwtracing/Kconfig +++ b/drivers/hwtracing/Kconfig @@ -5,4 +5,6 @@ source "drivers/hwtracing/stm/Kconfig" source "drivers/hwtracing/intel_th/Kconfig" +source "drivers/hwtracing/ptt/Kconfig" + endmenu diff --git a/drivers/hwtracing/ptt/Kconfig b/drivers/hwtracing/ptt/Kconfig new file mode 100644 index 000000000000..6d46a09ffeb9 --- /dev/null +++ b/drivers/hwtracing/ptt/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-only +config HISI_PTT + tristate "HiSilicon PCIe Tune and Trace Device" + depends on ARM64 || (COMPILE_TEST && 64BIT) + depends on PCI && HAS_DMA && HAS_IOMEM && PERF_EVENTS + help + HiSilicon PCIe Tune and Trace device exists as a PCIe RCiEP + device, and it provides support for PCIe traffic tuning and + tracing TLP headers to the memory. + + This driver can also be built as a module. If so, the module + will be called hisi_ptt. diff --git a/drivers/hwtracing/ptt/Makefile b/drivers/hwtracing/ptt/Makefile new file mode 100644 index 000000000000..908c09a98161 --- /dev/null +++ b/drivers/hwtracing/ptt/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_HISI_PTT) += hisi_ptt.o diff --git a/drivers/hwtracing/ptt/hisi_ptt.c b/drivers/hwtracing/ptt/hisi_ptt.c new file mode 100644 index 000000000000..1a56b31b921a --- /dev/null +++ b/drivers/hwtracing/ptt/hisi_ptt.c @@ -0,0 +1,916 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for HiSilicon PCIe tune and trace device + * + * Copyright (c) 2022 HiSilicon Technologies Co., Ltd. + * Author: Yicong Yang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hisi_ptt.h" + +/* Dynamic CPU hotplug state used by PTT */ +static enum cpuhp_state hisi_ptt_pmu_online; + +static u16 hisi_ptt_get_filter_val(u16 devid, bool is_port) +{ + if (is_port) + return BIT(HISI_PCIE_CORE_PORT_ID(devid & 0xff)); + + return devid; +} + +static bool hisi_ptt_wait_trace_hw_idle(struct hisi_ptt *hisi_ptt) +{ + u32 val; + + return !readl_poll_timeout_atomic(hisi_ptt->iobase + HISI_PTT_TRACE_STS, + val, val & HISI_PTT_TRACE_IDLE, + HISI_PTT_WAIT_POLL_INTERVAL_US, + HISI_PTT_WAIT_TRACE_TIMEOUT_US); +} + +static void hisi_ptt_wait_dma_reset_done(struct hisi_ptt *hisi_ptt) +{ + u32 val; + + readl_poll_timeout_atomic(hisi_ptt->iobase + HISI_PTT_TRACE_WR_STS, + val, !val, HISI_PTT_RESET_POLL_INTERVAL_US, + HISI_PTT_RESET_TIMEOUT_US); +} + +static void hisi_ptt_trace_end(struct hisi_ptt *hisi_ptt) +{ + writel(0, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); + hisi_ptt->trace_ctrl.started = false; +} + +static int hisi_ptt_trace_start(struct hisi_ptt *hisi_ptt) +{ + struct hisi_ptt_trace_ctrl *ctrl = &hisi_ptt->trace_ctrl; + u32 val; + int i; + + /* Check device idle before start trace */ + if (!hisi_ptt_wait_trace_hw_idle(hisi_ptt)) { + pci_err(hisi_ptt->pdev, "Failed to start trace, the device is still busy\n"); + return -EBUSY; + } + + ctrl->started = true; + + /* Reset the DMA before start tracing */ + val = readl(hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); + val |= HISI_PTT_TRACE_CTRL_RST; + writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); + + hisi_ptt_wait_dma_reset_done(hisi_ptt); + + val = readl(hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); + val &= ~HISI_PTT_TRACE_CTRL_RST; + writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); + + /* Reset the index of current buffer */ + hisi_ptt->trace_ctrl.buf_index = 0; + + /* Zero the trace buffers */ + for (i = 0; i < HISI_PTT_TRACE_BUF_CNT; i++) + memset(ctrl->trace_buf[i].addr, 0, HISI_PTT_TRACE_BUF_SIZE); + + /* Clear the interrupt status */ + writel(HISI_PTT_TRACE_INT_STAT_MASK, hisi_ptt->iobase + HISI_PTT_TRACE_INT_STAT); + writel(0, hisi_ptt->iobase + HISI_PTT_TRACE_INT_MASK); + + /* Set the trace control register */ + val = FIELD_PREP(HISI_PTT_TRACE_CTRL_TYPE_SEL, ctrl->type); + val |= FIELD_PREP(HISI_PTT_TRACE_CTRL_RXTX_SEL, ctrl->direction); + val |= FIELD_PREP(HISI_PTT_TRACE_CTRL_DATA_FORMAT, ctrl->format); + val |= FIELD_PREP(HISI_PTT_TRACE_CTRL_TARGET_SEL, hisi_ptt->trace_ctrl.filter); + if (!hisi_ptt->trace_ctrl.is_port) + val |= HISI_PTT_TRACE_CTRL_FILTER_MODE; + + /* Start the Trace */ + val |= HISI_PTT_TRACE_CTRL_EN; + writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); + + return 0; +} + +static int hisi_ptt_update_aux(struct hisi_ptt *hisi_ptt, int index, bool stop) +{ + struct hisi_ptt_trace_ctrl *ctrl = &hisi_ptt->trace_ctrl; + struct perf_output_handle *handle = &ctrl->handle; + struct perf_event *event = handle->event; + struct hisi_ptt_pmu_buf *buf; + size_t size; + void *addr; + + buf = perf_get_aux(handle); + if (!buf || !handle->size) + return -EINVAL; + + addr = ctrl->trace_buf[ctrl->buf_index].addr; + + /* + * If we're going to stop, read the size of already traced data from + * HISI_PTT_TRACE_WR_STS. Otherwise we're coming from the interrupt, + * the data size is always HISI_PTT_TRACE_BUF_SIZE. + */ + if (stop) { + u32 reg; + + reg = readl(hisi_ptt->iobase + HISI_PTT_TRACE_WR_STS); + size = FIELD_GET(HISI_PTT_TRACE_WR_STS_WRITE, reg); + } else { + size = HISI_PTT_TRACE_BUF_SIZE; + } + + memcpy(buf->base + buf->pos, addr, size); + buf->pos += size; + + /* + * Just commit the traced data if we're going to stop. Otherwise if the + * resident AUX buffer cannot contain the data of next trace buffer, + * apply a new one. + */ + if (stop) { + perf_aux_output_end(handle, buf->pos); + } else if (buf->length - buf->pos < HISI_PTT_TRACE_BUF_SIZE) { + perf_aux_output_end(handle, buf->pos); + + buf = perf_aux_output_begin(handle, event); + if (!buf) + return -EINVAL; + + buf->pos = handle->head % buf->length; + if (buf->length - buf->pos < HISI_PTT_TRACE_BUF_SIZE) { + perf_aux_output_end(handle, 0); + return -EINVAL; + } + } + + return 0; +} + +static irqreturn_t hisi_ptt_isr(int irq, void *context) +{ + struct hisi_ptt *hisi_ptt = context; + u32 status, buf_idx; + + status = readl(hisi_ptt->iobase + HISI_PTT_TRACE_INT_STAT); + if (!(status & HISI_PTT_TRACE_INT_STAT_MASK)) + return IRQ_NONE; + + buf_idx = ffs(status) - 1; + + /* Clear the interrupt status of buffer @buf_idx */ + writel(status, hisi_ptt->iobase + HISI_PTT_TRACE_INT_STAT); + + /* + * Update the AUX buffer and cache the current buffer index, + * as we need to know this and save the data when the trace + * is ended out of the interrupt handler. End the trace + * if the updating fails. + */ + if (hisi_ptt_update_aux(hisi_ptt, buf_idx, false)) + hisi_ptt_trace_end(hisi_ptt); + else + hisi_ptt->trace_ctrl.buf_index = (buf_idx + 1) % HISI_PTT_TRACE_BUF_CNT; + + return IRQ_HANDLED; +} + +static void hisi_ptt_irq_free_vectors(void *pdev) +{ + pci_free_irq_vectors(pdev); +} + +static int hisi_ptt_register_irq(struct hisi_ptt *hisi_ptt) +{ + struct pci_dev *pdev = hisi_ptt->pdev; + int ret; + + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); + if (ret < 0) { + pci_err(pdev, "failed to allocate irq vector, ret = %d\n", ret); + return ret; + } + + ret = devm_add_action_or_reset(&pdev->dev, hisi_ptt_irq_free_vectors, pdev); + if (ret < 0) + return ret; + + ret = devm_request_threaded_irq(&pdev->dev, + pci_irq_vector(pdev, HISI_PTT_TRACE_DMA_IRQ), + NULL, hisi_ptt_isr, 0, + DRV_NAME, hisi_ptt); + if (ret) { + pci_err(pdev, "failed to request irq %d, ret = %d\n", + pci_irq_vector(pdev, HISI_PTT_TRACE_DMA_IRQ), ret); + return ret; + } + + return 0; +} + +static int hisi_ptt_init_filters(struct pci_dev *pdev, void *data) +{ + struct hisi_ptt_filter_desc *filter; + struct hisi_ptt *hisi_ptt = data; + + /* + * We won't fail the probe if filter allocation failed here. The filters + * should be partial initialized and users would know which filter fails + * through the log. Other functions of PTT device are still available. + */ + filter = kzalloc(sizeof(*filter), GFP_KERNEL); + if (!filter) { + pci_err(hisi_ptt->pdev, "failed to add filter %s\n", pci_name(pdev)); + return -ENOMEM; + } + + filter->devid = PCI_DEVID(pdev->bus->number, pdev->devfn); + + if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT) { + filter->is_port = true; + list_add_tail(&filter->list, &hisi_ptt->port_filters); + + /* Update the available port mask */ + hisi_ptt->port_mask |= hisi_ptt_get_filter_val(filter->devid, true); + } else { + list_add_tail(&filter->list, &hisi_ptt->req_filters); + } + + return 0; +} + +static void hisi_ptt_release_filters(void *data) +{ + struct hisi_ptt_filter_desc *filter, *tmp; + struct hisi_ptt *hisi_ptt = data; + + list_for_each_entry_safe(filter, tmp, &hisi_ptt->req_filters, list) { + list_del(&filter->list); + kfree(filter); + } + + list_for_each_entry_safe(filter, tmp, &hisi_ptt->port_filters, list) { + list_del(&filter->list); + kfree(filter); + } +} + +static int hisi_ptt_config_trace_buf(struct hisi_ptt *hisi_ptt) +{ + struct hisi_ptt_trace_ctrl *ctrl = &hisi_ptt->trace_ctrl; + struct device *dev = &hisi_ptt->pdev->dev; + int i; + + ctrl->trace_buf = devm_kcalloc(dev, HISI_PTT_TRACE_BUF_CNT, + sizeof(*ctrl->trace_buf), GFP_KERNEL); + if (!ctrl->trace_buf) + return -ENOMEM; + + for (i = 0; i < HISI_PTT_TRACE_BUF_CNT; ++i) { + ctrl->trace_buf[i].addr = dmam_alloc_coherent(dev, HISI_PTT_TRACE_BUF_SIZE, + &ctrl->trace_buf[i].dma, + GFP_KERNEL); + if (!ctrl->trace_buf[i].addr) + return -ENOMEM; + } + + /* Configure the trace DMA buffer */ + for (i = 0; i < HISI_PTT_TRACE_BUF_CNT; i++) { + writel(lower_32_bits(ctrl->trace_buf[i].dma), + hisi_ptt->iobase + HISI_PTT_TRACE_ADDR_BASE_LO_0 + + i * HISI_PTT_TRACE_ADDR_STRIDE); + writel(upper_32_bits(ctrl->trace_buf[i].dma), + hisi_ptt->iobase + HISI_PTT_TRACE_ADDR_BASE_HI_0 + + i * HISI_PTT_TRACE_ADDR_STRIDE); + } + writel(HISI_PTT_TRACE_BUF_SIZE, hisi_ptt->iobase + HISI_PTT_TRACE_ADDR_SIZE); + + return 0; +} + +static int hisi_ptt_init_ctrls(struct hisi_ptt *hisi_ptt) +{ + struct pci_dev *pdev = hisi_ptt->pdev; + struct pci_bus *bus; + int ret; + u32 reg; + + INIT_LIST_HEAD(&hisi_ptt->port_filters); + INIT_LIST_HEAD(&hisi_ptt->req_filters); + + ret = hisi_ptt_config_trace_buf(hisi_ptt); + if (ret) + return ret; + + /* + * The device range register provides the information about the root + * ports which the RCiEP can control and trace. The RCiEP and the root + * ports which it supports are on the same PCIe core, with same domain + * number but maybe different bus number. The device range register + * will tell us which root ports we can support, Bit[31:16] indicates + * the upper BDF numbers of the root port, while Bit[15:0] indicates + * the lower. + */ + reg = readl(hisi_ptt->iobase + HISI_PTT_DEVICE_RANGE); + hisi_ptt->upper_bdf = FIELD_GET(HISI_PTT_DEVICE_RANGE_UPPER, reg); + hisi_ptt->lower_bdf = FIELD_GET(HISI_PTT_DEVICE_RANGE_LOWER, reg); + + bus = pci_find_bus(pci_domain_nr(pdev->bus), PCI_BUS_NUM(hisi_ptt->upper_bdf)); + if (bus) + pci_walk_bus(bus, hisi_ptt_init_filters, hisi_ptt); + + ret = devm_add_action_or_reset(&pdev->dev, hisi_ptt_release_filters, hisi_ptt); + if (ret) + return ret; + + hisi_ptt->trace_ctrl.on_cpu = -1; + return 0; +} + +static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct hisi_ptt *hisi_ptt = to_hisi_ptt(dev_get_drvdata(dev)); + const cpumask_t *cpumask = cpumask_of_node(dev_to_node(&hisi_ptt->pdev->dev)); + + return cpumap_print_to_pagebuf(true, buf, cpumask); +} +static DEVICE_ATTR_RO(cpumask); + +static struct attribute *hisi_ptt_cpumask_attrs[] = { + &dev_attr_cpumask.attr, + NULL +}; + +static const struct attribute_group hisi_ptt_cpumask_attr_group = { + .attrs = hisi_ptt_cpumask_attrs, +}; + +/* + * Bit 19 indicates the filter type, 1 for Root Port filter and 0 for Requester + * filter. Bit[15:0] indicates the filter value, for Root Port filter it's + * a bit mask of desired ports and for Requester filter it's the Requester ID + * of the desired PCIe function. Bit[18:16] is reserved for extension. + * + * See hisi_ptt.rst documentation for detailed information. + */ +PMU_FORMAT_ATTR(filter, "config:0-19"); +PMU_FORMAT_ATTR(direction, "config:20-23"); +PMU_FORMAT_ATTR(type, "config:24-31"); +PMU_FORMAT_ATTR(format, "config:32-35"); + +static struct attribute *hisi_ptt_pmu_format_attrs[] = { + &format_attr_filter.attr, + &format_attr_direction.attr, + &format_attr_type.attr, + &format_attr_format.attr, + NULL +}; + +static struct attribute_group hisi_ptt_pmu_format_group = { + .name = "format", + .attrs = hisi_ptt_pmu_format_attrs, +}; + +static const struct attribute_group *hisi_ptt_pmu_groups[] = { + &hisi_ptt_cpumask_attr_group, + &hisi_ptt_pmu_format_group, + NULL +}; + +static int hisi_ptt_trace_valid_direction(u32 val) +{ + /* + * The direction values have different effects according to the data + * format (specified in the parentheses). TLP set A/B means different + * set of TLP types. See hisi_ptt.rst documentation for more details. + */ + static const u32 hisi_ptt_trace_available_direction[] = { + 0, /* inbound(4DW) or reserved(8DW) */ + 1, /* outbound(4DW) */ + 2, /* {in, out}bound(4DW) or inbound(8DW), TLP set A */ + 3, /* {in, out}bound(4DW) or inbound(8DW), TLP set B */ + }; + int i; + + for (i = 0; i < ARRAY_SIZE(hisi_ptt_trace_available_direction); i++) { + if (val == hisi_ptt_trace_available_direction[i]) + return 0; + } + + return -EINVAL; +} + +static int hisi_ptt_trace_valid_type(u32 val) +{ + /* Different types can be set simultaneously */ + static const u32 hisi_ptt_trace_available_type[] = { + 1, /* posted_request */ + 2, /* non-posted_request */ + 4, /* completion */ + }; + int i; + + if (!val) + return -EINVAL; + + /* + * Walk the available list and clear the valid bits of + * the config. If there is any resident bit after the + * walk then the config is invalid. + */ + for (i = 0; i < ARRAY_SIZE(hisi_ptt_trace_available_type); i++) + val &= ~hisi_ptt_trace_available_type[i]; + + if (val) + return -EINVAL; + + return 0; +} + +static int hisi_ptt_trace_valid_format(u32 val) +{ + static const u32 hisi_ptt_trace_availble_format[] = { + 0, /* 4DW */ + 1, /* 8DW */ + }; + int i; + + for (i = 0; i < ARRAY_SIZE(hisi_ptt_trace_availble_format); i++) { + if (val == hisi_ptt_trace_availble_format[i]) + return 0; + } + + return -EINVAL; +} + +static int hisi_ptt_trace_valid_filter(struct hisi_ptt *hisi_ptt, u64 config) +{ + unsigned long val, port_mask = hisi_ptt->port_mask; + struct hisi_ptt_filter_desc *filter; + + hisi_ptt->trace_ctrl.is_port = FIELD_GET(HISI_PTT_PMU_FILTER_IS_PORT, config); + val = FIELD_GET(HISI_PTT_PMU_FILTER_VAL_MASK, config); + + /* + * Port filters are defined as bit mask. For port filters, check + * the bits in the @val are within the range of hisi_ptt->port_mask + * and whether it's empty or not, otherwise user has specified + * some unsupported root ports. + * + * For Requester ID filters, walk the available filter list to see + * whether we have one matched. + */ + if (!hisi_ptt->trace_ctrl.is_port) { + list_for_each_entry(filter, &hisi_ptt->req_filters, list) { + if (val == hisi_ptt_get_filter_val(filter->devid, filter->is_port)) + return 0; + } + } else if (bitmap_subset(&val, &port_mask, BITS_PER_LONG)) { + return 0; + } + + return -EINVAL; +} + +static void hisi_ptt_pmu_init_configs(struct hisi_ptt *hisi_ptt, struct perf_event *event) +{ + struct hisi_ptt_trace_ctrl *ctrl = &hisi_ptt->trace_ctrl; + u32 val; + + val = FIELD_GET(HISI_PTT_PMU_FILTER_VAL_MASK, event->attr.config); + hisi_ptt->trace_ctrl.filter = val; + + val = FIELD_GET(HISI_PTT_PMU_DIRECTION_MASK, event->attr.config); + ctrl->direction = val; + + val = FIELD_GET(HISI_PTT_PMU_TYPE_MASK, event->attr.config); + ctrl->type = val; + + val = FIELD_GET(HISI_PTT_PMU_FORMAT_MASK, event->attr.config); + ctrl->format = val; +} + +static int hisi_ptt_pmu_event_init(struct perf_event *event) +{ + struct hisi_ptt *hisi_ptt = to_hisi_ptt(event->pmu); + int ret; + u32 val; + + if (event->cpu < 0) { + dev_dbg(event->pmu->dev, "Per-task mode not supported\n"); + return -EOPNOTSUPP; + } + + if (event->attr.type != hisi_ptt->hisi_ptt_pmu.type) + return -ENOENT; + + ret = hisi_ptt_trace_valid_filter(hisi_ptt, event->attr.config); + if (ret < 0) + return ret; + + val = FIELD_GET(HISI_PTT_PMU_DIRECTION_MASK, event->attr.config); + ret = hisi_ptt_trace_valid_direction(val); + if (ret < 0) + return ret; + + val = FIELD_GET(HISI_PTT_PMU_TYPE_MASK, event->attr.config); + ret = hisi_ptt_trace_valid_type(val); + if (ret < 0) + return ret; + + val = FIELD_GET(HISI_PTT_PMU_FORMAT_MASK, event->attr.config); + return hisi_ptt_trace_valid_format(val); +} + +static void *hisi_ptt_pmu_setup_aux(struct perf_event *event, void **pages, + int nr_pages, bool overwrite) +{ + struct hisi_ptt_pmu_buf *buf; + struct page **pagelist; + int i; + + if (overwrite) { + dev_warn(event->pmu->dev, "Overwrite mode is not supported\n"); + return NULL; + } + + /* If the pages size less than buffers, we cannot start trace */ + if (nr_pages < HISI_PTT_TRACE_TOTAL_BUF_SIZE / PAGE_SIZE) + return NULL; + + buf = kzalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + return NULL; + + pagelist = kcalloc(nr_pages, sizeof(*pagelist), GFP_KERNEL); + if (!pagelist) + goto err; + + for (i = 0; i < nr_pages; i++) + pagelist[i] = virt_to_page(pages[i]); + + buf->base = vmap(pagelist, nr_pages, VM_MAP, PAGE_KERNEL); + if (!buf->base) { + kfree(pagelist); + goto err; + } + + buf->nr_pages = nr_pages; + buf->length = nr_pages * PAGE_SIZE; + buf->pos = 0; + + kfree(pagelist); + return buf; +err: + kfree(buf); + return NULL; +} + +static void hisi_ptt_pmu_free_aux(void *aux) +{ + struct hisi_ptt_pmu_buf *buf = aux; + + vunmap(buf->base); + kfree(buf); +} + +static void hisi_ptt_pmu_start(struct perf_event *event, int flags) +{ + struct hisi_ptt *hisi_ptt = to_hisi_ptt(event->pmu); + struct perf_output_handle *handle = &hisi_ptt->trace_ctrl.handle; + struct hw_perf_event *hwc = &event->hw; + struct device *dev = event->pmu->dev; + struct hisi_ptt_pmu_buf *buf; + int cpu = event->cpu; + int ret; + + hwc->state = 0; + + /* Serialize the perf process if user specified several CPUs */ + spin_lock(&hisi_ptt->pmu_lock); + if (hisi_ptt->trace_ctrl.started) { + dev_dbg(dev, "trace has already started\n"); + goto stop; + } + + /* + * Handle the interrupt on the same cpu which starts the trace to avoid + * context mismatch. Otherwise we'll trigger the WARN from the perf + * core in event_function_local(). If CPU passed is offline we'll fail + * here, just log it since we can do nothing here. + */ + ret = irq_set_affinity(pci_irq_vector(hisi_ptt->pdev, HISI_PTT_TRACE_DMA_IRQ), + cpumask_of(cpu)); + if (ret) + dev_warn(dev, "failed to set the affinity of trace interrupt\n"); + + hisi_ptt->trace_ctrl.on_cpu = cpu; + + buf = perf_aux_output_begin(handle, event); + if (!buf) { + dev_dbg(dev, "aux output begin failed\n"); + goto stop; + } + + buf->pos = handle->head % buf->length; + + hisi_ptt_pmu_init_configs(hisi_ptt, event); + + ret = hisi_ptt_trace_start(hisi_ptt); + if (ret) { + dev_dbg(dev, "trace start failed, ret = %d\n", ret); + perf_aux_output_end(handle, 0); + goto stop; + } + + spin_unlock(&hisi_ptt->pmu_lock); + return; +stop: + event->hw.state |= PERF_HES_STOPPED; + spin_unlock(&hisi_ptt->pmu_lock); +} + +static void hisi_ptt_pmu_stop(struct perf_event *event, int flags) +{ + struct hisi_ptt *hisi_ptt = to_hisi_ptt(event->pmu); + struct hw_perf_event *hwc = &event->hw; + + if (hwc->state & PERF_HES_STOPPED) + return; + + spin_lock(&hisi_ptt->pmu_lock); + if (hisi_ptt->trace_ctrl.started) { + hisi_ptt_trace_end(hisi_ptt); + + if (!hisi_ptt_wait_trace_hw_idle(hisi_ptt)) + dev_warn(event->pmu->dev, "Device is still busy\n"); + + hisi_ptt_update_aux(hisi_ptt, hisi_ptt->trace_ctrl.buf_index, true); + } + spin_unlock(&hisi_ptt->pmu_lock); + + hwc->state |= PERF_HES_STOPPED; + perf_event_update_userpage(event); + hwc->state |= PERF_HES_UPTODATE; +} + +static int hisi_ptt_pmu_add(struct perf_event *event, int flags) +{ + struct hisi_ptt *hisi_ptt = to_hisi_ptt(event->pmu); + struct hw_perf_event *hwc = &event->hw; + int cpu = event->cpu; + + /* Only allow the cpus on the device's node to add the event */ + if (!cpumask_test_cpu(cpu, cpumask_of_node(dev_to_node(&hisi_ptt->pdev->dev)))) + return 0; + + hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE; + + if (flags & PERF_EF_START) { + hisi_ptt_pmu_start(event, PERF_EF_RELOAD); + if (hwc->state & PERF_HES_STOPPED) + return -EINVAL; + } + + return 0; +} + +static void hisi_ptt_pmu_del(struct perf_event *event, int flags) +{ + hisi_ptt_pmu_stop(event, PERF_EF_UPDATE); +} + +static void hisi_ptt_remove_cpuhp_instance(void *hotplug_node) +{ + cpuhp_state_remove_instance_nocalls(hisi_ptt_pmu_online, hotplug_node); +} + +static void hisi_ptt_unregister_pmu(void *pmu) +{ + perf_pmu_unregister(pmu); +} + +static int hisi_ptt_register_pmu(struct hisi_ptt *hisi_ptt) +{ + u16 core_id, sicl_id; + char *pmu_name; + u32 reg; + int ret; + + ret = cpuhp_state_add_instance_nocalls(hisi_ptt_pmu_online, + &hisi_ptt->hotplug_node); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&hisi_ptt->pdev->dev, + hisi_ptt_remove_cpuhp_instance, + &hisi_ptt->hotplug_node); + if (ret) + return ret; + + spin_lock_init(&hisi_ptt->pmu_lock); + + hisi_ptt->hisi_ptt_pmu = (struct pmu) { + .module = THIS_MODULE, + .capabilities = PERF_PMU_CAP_EXCLUSIVE | PERF_PMU_CAP_ITRACE, + .task_ctx_nr = perf_sw_context, + .attr_groups = hisi_ptt_pmu_groups, + .event_init = hisi_ptt_pmu_event_init, + .setup_aux = hisi_ptt_pmu_setup_aux, + .free_aux = hisi_ptt_pmu_free_aux, + .start = hisi_ptt_pmu_start, + .stop = hisi_ptt_pmu_stop, + .add = hisi_ptt_pmu_add, + .del = hisi_ptt_pmu_del, + }; + + reg = readl(hisi_ptt->iobase + HISI_PTT_LOCATION); + core_id = FIELD_GET(HISI_PTT_CORE_ID, reg); + sicl_id = FIELD_GET(HISI_PTT_SICL_ID, reg); + + pmu_name = devm_kasprintf(&hisi_ptt->pdev->dev, GFP_KERNEL, "hisi_ptt%u_%u", + sicl_id, core_id); + if (!pmu_name) + return -ENOMEM; + + ret = perf_pmu_register(&hisi_ptt->hisi_ptt_pmu, pmu_name, -1); + if (ret) + return ret; + + return devm_add_action_or_reset(&hisi_ptt->pdev->dev, + hisi_ptt_unregister_pmu, + &hisi_ptt->hisi_ptt_pmu); +} + +/* + * The DMA of PTT trace can only use direct mappings due to some + * hardware restriction. Check whether there is no IOMMU or the + * policy of the IOMMU domain is passthrough, otherwise the trace + * cannot work. + * + * The PTT device is supposed to behind an ARM SMMUv3, which + * should have passthrough the device by a quirk. + */ +static int hisi_ptt_check_iommu_mapping(struct pci_dev *pdev) +{ + struct iommu_domain *iommu_domain; + + iommu_domain = iommu_get_domain_for_dev(&pdev->dev); + if (!iommu_domain || iommu_domain->type == IOMMU_DOMAIN_IDENTITY) + return 0; + + return -EOPNOTSUPP; +} + +static int hisi_ptt_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct hisi_ptt *hisi_ptt; + int ret; + + ret = hisi_ptt_check_iommu_mapping(pdev); + if (ret) { + pci_err(pdev, "requires direct DMA mappings\n"); + return ret; + } + + hisi_ptt = devm_kzalloc(&pdev->dev, sizeof(*hisi_ptt), GFP_KERNEL); + if (!hisi_ptt) + return -ENOMEM; + + hisi_ptt->pdev = pdev; + pci_set_drvdata(pdev, hisi_ptt); + + ret = pcim_enable_device(pdev); + if (ret) { + pci_err(pdev, "failed to enable device, ret = %d\n", ret); + return ret; + } + + ret = pcim_iomap_regions(pdev, BIT(2), DRV_NAME); + if (ret) { + pci_err(pdev, "failed to remap io memory, ret = %d\n", ret); + return ret; + } + + hisi_ptt->iobase = pcim_iomap_table(pdev)[2]; + + ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64)); + if (ret) { + pci_err(pdev, "failed to set 64 bit dma mask, ret = %d\n", ret); + return ret; + } + + pci_set_master(pdev); + + ret = hisi_ptt_register_irq(hisi_ptt); + if (ret) + return ret; + + ret = hisi_ptt_init_ctrls(hisi_ptt); + if (ret) { + pci_err(pdev, "failed to init controls, ret = %d\n", ret); + return ret; + } + + ret = hisi_ptt_register_pmu(hisi_ptt); + if (ret) { + pci_err(pdev, "failed to register PMU device, ret = %d", ret); + return ret; + } + + return 0; +} + +static const struct pci_device_id hisi_ptt_id_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_HUAWEI, 0xa12e) }, + { } +}; +MODULE_DEVICE_TABLE(pci, hisi_ptt_id_tbl); + +static struct pci_driver hisi_ptt_driver = { + .name = DRV_NAME, + .id_table = hisi_ptt_id_tbl, + .probe = hisi_ptt_probe, +}; + +static int hisi_ptt_cpu_teardown(unsigned int cpu, struct hlist_node *node) +{ + struct hisi_ptt *hisi_ptt; + struct device *dev; + int target, src; + + hisi_ptt = hlist_entry_safe(node, struct hisi_ptt, hotplug_node); + src = hisi_ptt->trace_ctrl.on_cpu; + dev = hisi_ptt->hisi_ptt_pmu.dev; + + if (!hisi_ptt->trace_ctrl.started || src != cpu) + return 0; + + target = cpumask_any_but(cpumask_of_node(dev_to_node(&hisi_ptt->pdev->dev)), cpu); + if (target >= nr_cpu_ids) { + dev_err(dev, "no available cpu for perf context migration\n"); + return 0; + } + + perf_pmu_migrate_context(&hisi_ptt->hisi_ptt_pmu, src, target); + + /* + * Also make sure the interrupt bind to the migrated CPU as well. Warn + * the user on failure here. + */ + if (irq_set_affinity(pci_irq_vector(hisi_ptt->pdev, HISI_PTT_TRACE_DMA_IRQ), + cpumask_of(target))) + dev_warn(dev, "failed to set the affinity of trace interrupt\n"); + + hisi_ptt->trace_ctrl.on_cpu = target; + return 0; +} + +static int __init hisi_ptt_init(void) +{ + int ret; + + ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, DRV_NAME, NULL, + hisi_ptt_cpu_teardown); + if (ret < 0) + return ret; + hisi_ptt_pmu_online = ret; + + ret = pci_register_driver(&hisi_ptt_driver); + if (ret) + cpuhp_remove_multi_state(hisi_ptt_pmu_online); + + return ret; +} +module_init(hisi_ptt_init); + +static void __exit hisi_ptt_exit(void) +{ + pci_unregister_driver(&hisi_ptt_driver); + cpuhp_remove_multi_state(hisi_ptt_pmu_online); +} +module_exit(hisi_ptt_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yicong Yang "); +MODULE_DESCRIPTION("Driver for HiSilicon PCIe tune and trace device"); diff --git a/drivers/hwtracing/ptt/hisi_ptt.h b/drivers/hwtracing/ptt/hisi_ptt.h new file mode 100644 index 000000000000..10446dce8a86 --- /dev/null +++ b/drivers/hwtracing/ptt/hisi_ptt.h @@ -0,0 +1,177 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Driver for HiSilicon PCIe tune and trace device + * + * Copyright (c) 2022 HiSilicon Technologies Co., Ltd. + * Author: Yicong Yang + */ + +#ifndef _HISI_PTT_H +#define _HISI_PTT_H + +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "hisi_ptt" + +/* + * The definition of the device registers and register fields. + */ +#define HISI_PTT_TRACE_ADDR_SIZE 0x0800 +#define HISI_PTT_TRACE_ADDR_BASE_LO_0 0x0810 +#define HISI_PTT_TRACE_ADDR_BASE_HI_0 0x0814 +#define HISI_PTT_TRACE_ADDR_STRIDE 0x8 +#define HISI_PTT_TRACE_CTRL 0x0850 +#define HISI_PTT_TRACE_CTRL_EN BIT(0) +#define HISI_PTT_TRACE_CTRL_RST BIT(1) +#define HISI_PTT_TRACE_CTRL_RXTX_SEL GENMASK(3, 2) +#define HISI_PTT_TRACE_CTRL_TYPE_SEL GENMASK(7, 4) +#define HISI_PTT_TRACE_CTRL_DATA_FORMAT BIT(14) +#define HISI_PTT_TRACE_CTRL_FILTER_MODE BIT(15) +#define HISI_PTT_TRACE_CTRL_TARGET_SEL GENMASK(31, 16) +#define HISI_PTT_TRACE_INT_STAT 0x0890 +#define HISI_PTT_TRACE_INT_STAT_MASK GENMASK(3, 0) +#define HISI_PTT_TRACE_INT_MASK 0x0894 +#define HISI_PTT_TRACE_WR_STS 0x08a0 +#define HISI_PTT_TRACE_WR_STS_WRITE GENMASK(27, 0) +#define HISI_PTT_TRACE_WR_STS_BUFFER GENMASK(29, 28) +#define HISI_PTT_TRACE_STS 0x08b0 +#define HISI_PTT_TRACE_IDLE BIT(0) +#define HISI_PTT_DEVICE_RANGE 0x0fe0 +#define HISI_PTT_DEVICE_RANGE_UPPER GENMASK(31, 16) +#define HISI_PTT_DEVICE_RANGE_LOWER GENMASK(15, 0) +#define HISI_PTT_LOCATION 0x0fe8 +#define HISI_PTT_CORE_ID GENMASK(15, 0) +#define HISI_PTT_SICL_ID GENMASK(31, 16) + +/* Parameters of PTT trace DMA part. */ +#define HISI_PTT_TRACE_DMA_IRQ 0 +#define HISI_PTT_TRACE_BUF_CNT 4 +#define HISI_PTT_TRACE_BUF_SIZE SZ_4M +#define HISI_PTT_TRACE_TOTAL_BUF_SIZE (HISI_PTT_TRACE_BUF_SIZE * \ + HISI_PTT_TRACE_BUF_CNT) +/* Wait time for hardware DMA to reset */ +#define HISI_PTT_RESET_TIMEOUT_US 10UL +#define HISI_PTT_RESET_POLL_INTERVAL_US 1UL +/* Poll timeout and interval for waiting hardware work to finish */ +#define HISI_PTT_WAIT_TRACE_TIMEOUT_US 100UL +#define HISI_PTT_WAIT_POLL_INTERVAL_US 10UL + +#define HISI_PCIE_CORE_PORT_ID(devfn) ((PCI_SLOT(devfn) & 0x7) << 1) + +/* Definition of the PMU configs */ +#define HISI_PTT_PMU_FILTER_IS_PORT BIT(19) +#define HISI_PTT_PMU_FILTER_VAL_MASK GENMASK(15, 0) +#define HISI_PTT_PMU_DIRECTION_MASK GENMASK(23, 20) +#define HISI_PTT_PMU_TYPE_MASK GENMASK(31, 24) +#define HISI_PTT_PMU_FORMAT_MASK GENMASK(35, 32) + +/** + * struct hisi_ptt_dma_buffer - Describe a single trace buffer of PTT trace. + * The detail of the data format is described + * in the documentation of PTT device. + * @dma: DMA address of this buffer visible to the device + * @addr: virtual address of this buffer visible to the cpu + */ +struct hisi_ptt_dma_buffer { + dma_addr_t dma; + void *addr; +}; + +/** + * struct hisi_ptt_trace_ctrl - Control and status of PTT trace + * @trace_buf: array of the trace buffers for holding the trace data. + * the length will be HISI_PTT_TRACE_BUF_CNT. + * @handle: perf output handle of current trace session + * @buf_index: the index of current using trace buffer + * @on_cpu: current tracing cpu + * @started: current trace status, true for started + * @is_port: whether we're tracing root port or not + * @direction: direction of the TLP headers to trace + * @filter: filter value for tracing the TLP headers + * @format: format of the TLP headers to trace + * @type: type of the TLP headers to trace + */ +struct hisi_ptt_trace_ctrl { + struct hisi_ptt_dma_buffer *trace_buf; + struct perf_output_handle handle; + u32 buf_index; + int on_cpu; + bool started; + bool is_port; + u32 direction:2; + u32 filter:16; + u32 format:1; + u32 type:4; +}; + +/** + * struct hisi_ptt_filter_desc - Descriptor of the PTT trace filter + * @list: entry of this descriptor in the filter list + * @is_port: the PCI device of the filter is a Root Port or not + * @devid: the PCI device's devid of the filter + */ +struct hisi_ptt_filter_desc { + struct list_head list; + bool is_port; + u16 devid; +}; + +/** + * struct hisi_ptt_pmu_buf - Descriptor of the AUX buffer of PTT trace + * @length: size of the AUX buffer + * @nr_pages: number of pages of the AUX buffer + * @base: start address of AUX buffer + * @pos: position in the AUX buffer to commit traced data + */ +struct hisi_ptt_pmu_buf { + size_t length; + int nr_pages; + void *base; + long pos; +}; + +/** + * struct hisi_ptt - Per PTT device data + * @trace_ctrl: the control information of PTT trace + * @hotplug_node: node for register cpu hotplug event + * @hisi_ptt_pmu: the pum device of trace + * @iobase: base IO address of the device + * @pdev: pci_dev of this PTT device + * @pmu_lock: lock to serialize the perf process + * @upper_bdf: the upper BDF range of the PCI devices managed by this PTT device + * @lower_bdf: the lower BDF range of the PCI devices managed by this PTT device + * @port_filters: the filter list of root ports + * @req_filters: the filter list of requester ID + * @port_mask: port mask of the managed root ports + */ +struct hisi_ptt { + struct hisi_ptt_trace_ctrl trace_ctrl; + struct hlist_node hotplug_node; + struct pmu hisi_ptt_pmu; + void __iomem *iobase; + struct pci_dev *pdev; + spinlock_t pmu_lock; + u32 upper_bdf; + u32 lower_bdf; + + /* + * The trace TLP headers can either be filtered by certain + * root port, or by the requester ID. Organize the filters + * by @port_filters and @req_filters here. The mask of all + * the valid ports is also cached for doing sanity check + * of user input. + */ + struct list_head port_filters; + struct list_head req_filters; + u16 port_mask; +}; + +#define to_hisi_ptt(pmu) container_of(pmu, struct hisi_ptt, hisi_ptt_pmu) + +#endif /* _HISI_PTT_H */ -- cgit v1.2.3 From 5ca57b03d8c5de4c59234cc11fe9dd9f13d57f48 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Tue, 16 Aug 2022 19:44:12 +0800 Subject: hwtracing: hisi_ptt: Add tune function support for HiSilicon PCIe Tune and Trace device Add tune function for the HiSilicon Tune and Trace device. The interface of tune is exposed through sysfs attributes of PTT PMU device. Acked-by: Mathieu Poirier Reviewed-by: Jonathan Cameron Reviewed-by: John Garry Signed-off-by: Yicong Yang Link: https://lore.kernel.org/r/20220816114414.4092-4-yangyicong@huawei.com Signed-off-by: Mathieu Poirier --- drivers/hwtracing/ptt/hisi_ptt.c | 131 +++++++++++++++++++++++++++++++++++++++ drivers/hwtracing/ptt/hisi_ptt.h | 23 +++++++ 2 files changed, 154 insertions(+) diff --git a/drivers/hwtracing/ptt/hisi_ptt.c b/drivers/hwtracing/ptt/hisi_ptt.c index 1a56b31b921a..666a0f14b6c4 100644 --- a/drivers/hwtracing/ptt/hisi_ptt.c +++ b/drivers/hwtracing/ptt/hisi_ptt.c @@ -25,6 +25,135 @@ /* Dynamic CPU hotplug state used by PTT */ static enum cpuhp_state hisi_ptt_pmu_online; +static bool hisi_ptt_wait_tuning_finish(struct hisi_ptt *hisi_ptt) +{ + u32 val; + + return !readl_poll_timeout(hisi_ptt->iobase + HISI_PTT_TUNING_INT_STAT, + val, !(val & HISI_PTT_TUNING_INT_STAT_MASK), + HISI_PTT_WAIT_POLL_INTERVAL_US, + HISI_PTT_WAIT_TUNE_TIMEOUT_US); +} + +static ssize_t hisi_ptt_tune_attr_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hisi_ptt *hisi_ptt = to_hisi_ptt(dev_get_drvdata(dev)); + struct dev_ext_attribute *ext_attr; + struct hisi_ptt_tune_desc *desc; + u32 reg; + u16 val; + + ext_attr = container_of(attr, struct dev_ext_attribute, attr); + desc = ext_attr->var; + + mutex_lock(&hisi_ptt->tune_lock); + + reg = readl(hisi_ptt->iobase + HISI_PTT_TUNING_CTRL); + reg &= ~(HISI_PTT_TUNING_CTRL_CODE | HISI_PTT_TUNING_CTRL_SUB); + reg |= FIELD_PREP(HISI_PTT_TUNING_CTRL_CODE | HISI_PTT_TUNING_CTRL_SUB, + desc->event_code); + writel(reg, hisi_ptt->iobase + HISI_PTT_TUNING_CTRL); + + /* Write all 1 to indicates it's the read process */ + writel(~0U, hisi_ptt->iobase + HISI_PTT_TUNING_DATA); + + if (!hisi_ptt_wait_tuning_finish(hisi_ptt)) { + mutex_unlock(&hisi_ptt->tune_lock); + return -ETIMEDOUT; + } + + reg = readl(hisi_ptt->iobase + HISI_PTT_TUNING_DATA); + reg &= HISI_PTT_TUNING_DATA_VAL_MASK; + val = FIELD_GET(HISI_PTT_TUNING_DATA_VAL_MASK, reg); + + mutex_unlock(&hisi_ptt->tune_lock); + return sysfs_emit(buf, "%u\n", val); +} + +static ssize_t hisi_ptt_tune_attr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct hisi_ptt *hisi_ptt = to_hisi_ptt(dev_get_drvdata(dev)); + struct dev_ext_attribute *ext_attr; + struct hisi_ptt_tune_desc *desc; + u32 reg; + u16 val; + + ext_attr = container_of(attr, struct dev_ext_attribute, attr); + desc = ext_attr->var; + + if (kstrtou16(buf, 10, &val)) + return -EINVAL; + + mutex_lock(&hisi_ptt->tune_lock); + + reg = readl(hisi_ptt->iobase + HISI_PTT_TUNING_CTRL); + reg &= ~(HISI_PTT_TUNING_CTRL_CODE | HISI_PTT_TUNING_CTRL_SUB); + reg |= FIELD_PREP(HISI_PTT_TUNING_CTRL_CODE | HISI_PTT_TUNING_CTRL_SUB, + desc->event_code); + writel(reg, hisi_ptt->iobase + HISI_PTT_TUNING_CTRL); + writel(FIELD_PREP(HISI_PTT_TUNING_DATA_VAL_MASK, val), + hisi_ptt->iobase + HISI_PTT_TUNING_DATA); + + if (!hisi_ptt_wait_tuning_finish(hisi_ptt)) { + mutex_unlock(&hisi_ptt->tune_lock); + return -ETIMEDOUT; + } + + mutex_unlock(&hisi_ptt->tune_lock); + return count; +} + +#define HISI_PTT_TUNE_ATTR(_name, _val, _show, _store) \ + static struct hisi_ptt_tune_desc _name##_desc = { \ + .name = #_name, \ + .event_code = (_val), \ + }; \ + static struct dev_ext_attribute hisi_ptt_##_name##_attr = { \ + .attr = __ATTR(_name, 0600, _show, _store), \ + .var = &_name##_desc, \ + } + +#define HISI_PTT_TUNE_ATTR_COMMON(_name, _val) \ + HISI_PTT_TUNE_ATTR(_name, _val, \ + hisi_ptt_tune_attr_show, \ + hisi_ptt_tune_attr_store) + +/* + * The value of the tuning event are composed of two parts: main event code + * in BIT[0,15] and subevent code in BIT[16,23]. For example, qox_tx_cpl is + * a subevent of 'Tx path QoS control' which for tuning the weight of Tx + * completion TLPs. See hisi_ptt.rst documentation for more information. + */ +#define HISI_PTT_TUNE_QOS_TX_CPL (0x4 | (3 << 16)) +#define HISI_PTT_TUNE_QOS_TX_NP (0x4 | (4 << 16)) +#define HISI_PTT_TUNE_QOS_TX_P (0x4 | (5 << 16)) +#define HISI_PTT_TUNE_RX_ALLOC_BUF_LEVEL (0x5 | (6 << 16)) +#define HISI_PTT_TUNE_TX_ALLOC_BUF_LEVEL (0x5 | (7 << 16)) + +HISI_PTT_TUNE_ATTR_COMMON(qos_tx_cpl, HISI_PTT_TUNE_QOS_TX_CPL); +HISI_PTT_TUNE_ATTR_COMMON(qos_tx_np, HISI_PTT_TUNE_QOS_TX_NP); +HISI_PTT_TUNE_ATTR_COMMON(qos_tx_p, HISI_PTT_TUNE_QOS_TX_P); +HISI_PTT_TUNE_ATTR_COMMON(rx_alloc_buf_level, HISI_PTT_TUNE_RX_ALLOC_BUF_LEVEL); +HISI_PTT_TUNE_ATTR_COMMON(tx_alloc_buf_level, HISI_PTT_TUNE_TX_ALLOC_BUF_LEVEL); + +static struct attribute *hisi_ptt_tune_attrs[] = { + &hisi_ptt_qos_tx_cpl_attr.attr.attr, + &hisi_ptt_qos_tx_np_attr.attr.attr, + &hisi_ptt_qos_tx_p_attr.attr.attr, + &hisi_ptt_rx_alloc_buf_level_attr.attr.attr, + &hisi_ptt_tx_alloc_buf_level_attr.attr.attr, + NULL, +}; + +static struct attribute_group hisi_ptt_tune_group = { + .name = "tune", + .attrs = hisi_ptt_tune_attrs, +}; + static u16 hisi_ptt_get_filter_val(u16 devid, bool is_port) { if (is_port) @@ -393,6 +522,7 @@ static struct attribute_group hisi_ptt_pmu_format_group = { static const struct attribute_group *hisi_ptt_pmu_groups[] = { &hisi_ptt_cpumask_attr_group, &hisi_ptt_pmu_format_group, + &hisi_ptt_tune_group, NULL }; @@ -727,6 +857,7 @@ static int hisi_ptt_register_pmu(struct hisi_ptt *hisi_ptt) if (ret) return ret; + mutex_init(&hisi_ptt->tune_lock); spin_lock_init(&hisi_ptt->pmu_lock); hisi_ptt->hisi_ptt_pmu = (struct pmu) { diff --git a/drivers/hwtracing/ptt/hisi_ptt.h b/drivers/hwtracing/ptt/hisi_ptt.h index 10446dce8a86..5beb1648c93a 100644 --- a/drivers/hwtracing/ptt/hisi_ptt.h +++ b/drivers/hwtracing/ptt/hisi_ptt.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -22,6 +23,11 @@ /* * The definition of the device registers and register fields. */ +#define HISI_PTT_TUNING_CTRL 0x0000 +#define HISI_PTT_TUNING_CTRL_CODE GENMASK(15, 0) +#define HISI_PTT_TUNING_CTRL_SUB GENMASK(23, 16) +#define HISI_PTT_TUNING_DATA 0x0004 +#define HISI_PTT_TUNING_DATA_VAL_MASK GENMASK(15, 0) #define HISI_PTT_TRACE_ADDR_SIZE 0x0800 #define HISI_PTT_TRACE_ADDR_BASE_LO_0 0x0810 #define HISI_PTT_TRACE_ADDR_BASE_HI_0 0x0814 @@ -37,6 +43,8 @@ #define HISI_PTT_TRACE_INT_STAT 0x0890 #define HISI_PTT_TRACE_INT_STAT_MASK GENMASK(3, 0) #define HISI_PTT_TRACE_INT_MASK 0x0894 +#define HISI_PTT_TUNING_INT_STAT 0x0898 +#define HISI_PTT_TUNING_INT_STAT_MASK BIT(0) #define HISI_PTT_TRACE_WR_STS 0x08a0 #define HISI_PTT_TRACE_WR_STS_WRITE GENMASK(27, 0) #define HISI_PTT_TRACE_WR_STS_BUFFER GENMASK(29, 28) @@ -59,6 +67,7 @@ #define HISI_PTT_RESET_TIMEOUT_US 10UL #define HISI_PTT_RESET_POLL_INTERVAL_US 1UL /* Poll timeout and interval for waiting hardware work to finish */ +#define HISI_PTT_WAIT_TUNE_TIMEOUT_US 1000000UL #define HISI_PTT_WAIT_TRACE_TIMEOUT_US 100UL #define HISI_PTT_WAIT_POLL_INTERVAL_US 10UL @@ -71,6 +80,18 @@ #define HISI_PTT_PMU_TYPE_MASK GENMASK(31, 24) #define HISI_PTT_PMU_FORMAT_MASK GENMASK(35, 32) +/** + * struct hisi_ptt_tune_desc - Describe tune event for PTT tune + * @hisi_ptt: PTT device this tune event belongs to + * @name: name of this event + * @event_code: code of the event + */ +struct hisi_ptt_tune_desc { + struct hisi_ptt *hisi_ptt; + const char *name; + u32 event_code; +}; + /** * struct hisi_ptt_dma_buffer - Describe a single trace buffer of PTT trace. * The detail of the data format is described @@ -143,6 +164,7 @@ struct hisi_ptt_pmu_buf { * @hisi_ptt_pmu: the pum device of trace * @iobase: base IO address of the device * @pdev: pci_dev of this PTT device + * @tune_lock: lock to serialize the tune process * @pmu_lock: lock to serialize the perf process * @upper_bdf: the upper BDF range of the PCI devices managed by this PTT device * @lower_bdf: the lower BDF range of the PCI devices managed by this PTT device @@ -156,6 +178,7 @@ struct hisi_ptt { struct pmu hisi_ptt_pmu; void __iomem *iobase; struct pci_dev *pdev; + struct mutex tune_lock; spinlock_t pmu_lock; u32 upper_bdf; u32 lower_bdf; -- cgit v1.2.3 From a7112b747c324dda8937d4f47b14dc0af0b465d1 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Tue, 16 Aug 2022 19:44:13 +0800 Subject: docs: trace: Add HiSilicon PTT device driver documentation Document the introduction and usage of HiSilicon PTT device driver as well as the sysfs attributes description provided by the driver. Signed-off-by: Yicong Yang Reviewed-by: Jonathan Cameron Reviewed-by: Bagas Sanjaya [Fixed month and kernel version] Link: https://lore.kernel.org/r/20220816114414.4092-5-yangyicong@huawei.com Signed-off-by: Mathieu Poirier --- Documentation/ABI/testing/sysfs-devices-hisi_ptt | 61 +++++ Documentation/trace/hisi-ptt.rst | 298 +++++++++++++++++++++++ Documentation/trace/index.rst | 1 + 3 files changed, 360 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-devices-hisi_ptt create mode 100644 Documentation/trace/hisi-ptt.rst diff --git a/Documentation/ABI/testing/sysfs-devices-hisi_ptt b/Documentation/ABI/testing/sysfs-devices-hisi_ptt new file mode 100644 index 000000000000..82de6d710266 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-hisi_ptt @@ -0,0 +1,61 @@ +What: /sys/devices/hisi_ptt_/tune +Date: October 2022 +KernelVersion: 6.1 +Contact: Yicong Yang +Description: This directory contains files for tuning the PCIe link + parameters(events). Each file is named after the event + of the PCIe link. + + See Documentation/trace/hisi-ptt.rst for more information. + +What: /sys/devices/hisi_ptt_/tune/qos_tx_cpl +Date: October 2022 +KernelVersion: 6.1 +Contact: Yicong Yang +Description: (RW) Controls the weight of Tx completion TLPs, which influence + the proportion of outbound completion TLPs on the PCIe link. + The available tune data is [0, 1, 2]. Writing a negative value + will return an error, and out of range values will be converted + to 2. The value indicates a probable level of the event. + +What: /sys/devices/hisi_ptt_/tune/qos_tx_np +Date: October 2022 +KernelVersion: 6.1 +Contact: Yicong Yang +Description: (RW) Controls the weight of Tx non-posted TLPs, which influence + the proportion of outbound non-posted TLPs on the PCIe link. + The available tune data is [0, 1, 2]. Writing a negative value + will return an error, and out of range values will be converted + to 2. The value indicates a probable level of the event. + +What: /sys/devices/hisi_ptt_/tune/qos_tx_p +Date: October 2022 +KernelVersion: 6.1 +Contact: Yicong Yang +Description: (RW) Controls the weight of Tx posted TLPs, which influence the + proportion of outbound posted TLPs on the PCIe link. + The available tune data is [0, 1, 2]. Writing a negative value + will return an error, and out of range values will be converted + to 2. The value indicates a probable level of the event. + +What: /sys/devices/hisi_ptt_/tune/rx_alloc_buf_level +Date: October 2022 +KernelVersion: 6.1 +Contact: Yicong Yang +Description: (RW) Control the allocated buffer watermark for inbound packets. + The packets will be stored in the buffer first and then transmitted + either when the watermark reached or when timed out. + The available tune data is [0, 1, 2]. Writing a negative value + will return an error, and out of range values will be converted + to 2. The value indicates a probable level of the event. + +What: /sys/devices/hisi_ptt_/tune/tx_alloc_buf_level +Date: October 2022 +KernelVersion: 6.1 +Contact: Yicong Yang +Description: (RW) Control the allocated buffer watermark of outbound packets. + The packets will be stored in the buffer first and then transmitted + either when the watermark reached or when timed out. + The available tune data is [0, 1, 2]. Writing a negative value + will return an error, and out of range values will be converted + to 2. The value indicates a probable level of the event. diff --git a/Documentation/trace/hisi-ptt.rst b/Documentation/trace/hisi-ptt.rst new file mode 100644 index 000000000000..4f87d8e21065 --- /dev/null +++ b/Documentation/trace/hisi-ptt.rst @@ -0,0 +1,298 @@ +.. SPDX-License-Identifier: GPL-2.0 + +====================================== +HiSilicon PCIe Tune and Trace device +====================================== + +Introduction +============ + +HiSilicon PCIe tune and trace device (PTT) is a PCIe Root Complex +integrated Endpoint (RCiEP) device, providing the capability +to dynamically monitor and tune the PCIe link's events (tune), +and trace the TLP headers (trace). The two functions are independent, +but is recommended to use them together to analyze and enhance the +PCIe link's performance. + +On Kunpeng 930 SoC, the PCIe Root Complex is composed of several +PCIe cores. Each PCIe core includes several Root Ports and a PTT +RCiEP, like below. The PTT device is capable of tuning and +tracing the links of the PCIe core. +:: + + +--------------Core 0-------+ + | | [ PTT ] | + | | [Root Port]---[Endpoint] + | | [Root Port]---[Endpoint] + | | [Root Port]---[Endpoint] + Root Complex |------Core 1-------+ + | | [ PTT ] | + | | [Root Port]---[ Switch ]---[Endpoint] + | | [Root Port]---[Endpoint] `-[Endpoint] + | | [Root Port]---[Endpoint] + +---------------------------+ + +The PTT device driver registers one PMU device for each PTT device. +The name of each PTT device is composed of 'hisi_ptt' prefix with +the id of the SICL and the Core where it locates. The Kunpeng 930 +SoC encapsulates multiple CPU dies (SCCL, Super CPU Cluster) and +IO dies (SICL, Super I/O Cluster), where there's one PCIe Root +Complex for each SICL. +:: + + /sys/devices/hisi_ptt_ + +Tune +==== + +PTT tune is designed for monitoring and adjusting PCIe link parameters (events). +Currently we support events in 2 classes. The scope of the events +covers the PCIe core to which the PTT device belongs. + +Each event is presented as a file under $(PTT PMU dir)/tune, and +a simple open/read/write/close cycle will be used to tune the event. +:: + + $ cd /sys/devices/hisi_ptt_/tune + $ ls + qos_tx_cpl qos_tx_np qos_tx_p + tx_path_rx_req_alloc_buf_level + tx_path_tx_req_alloc_buf_level + $ cat qos_tx_dp + 1 + $ echo 2 > qos_tx_dp + $ cat qos_tx_dp + 2 + +Current value (numerical value) of the event can be simply read +from the file, and the desired value written to the file to tune. + +1. Tx Path QoS Control +------------------------ + +The following files are provided to tune the QoS of the tx path of +the PCIe core. + +- qos_tx_cpl: weight of Tx completion TLPs +- qos_tx_np: weight of Tx non-posted TLPs +- qos_tx_p: weight of Tx posted TLPs + +The weight influences the proportion of certain packets on the PCIe link. +For example, for the storage scenario, increase the proportion +of the completion packets on the link to enhance the performance as +more completions are consumed. + +The available tune data of these events is [0, 1, 2]. +Writing a negative value will return an error, and out of range +values will be converted to 2. Note that the event value just +indicates a probable level, but is not precise. + +2. Tx Path Buffer Control +------------------------- + +Following files are provided to tune the buffer of tx path of the PCIe core. + +- rx_alloc_buf_level: watermark of Rx requested +- tx_alloc_buf_level: watermark of Tx requested + +These events influence the watermark of the buffer allocated for each +type. Rx means the inbound while Tx means outbound. The packets will +be stored in the buffer first and then transmitted either when the +watermark reached or when timed out. For a busy direction, you should +increase the related buffer watermark to avoid frequently posting and +thus enhance the performance. In most cases just keep the default value. + +The available tune data of above events is [0, 1, 2]. +Writing a negative value will return an error, and out of range +values will be converted to 2. Note that the event value just +indicates a probable level, but is not precise. + +Trace +===== + +PTT trace is designed for dumping the TLP headers to the memory, which +can be used to analyze the transactions and usage condition of the PCIe +Link. You can choose to filter the traced headers by either Requester ID, +or those downstream of a set of Root Ports on the same core of the PTT +device. It's also supported to trace the headers of certain type and of +certain direction. + +You can use the perf command `perf record` to set the parameters, start +trace and get the data. It's also supported to decode the trace +data with `perf report`. The control parameters for trace is inputted +as event code for each events, which will be further illustrated later. +An example usage is like +:: + + $ perf record -e hisi_ptt0_2/filter=0x80001,type=1,direction=1, + format=1/ -- sleep 5 + +This will trace the TLP headers downstream root port 0000:00:10.1 (event +code for event 'filter' is 0x80001) with type of posted TLP requests, +direction of inbound and traced data format of 8DW. + +1. Filter +--------- + +The TLP headers to trace can be filtered by the Root Ports or the Requester ID +of the Endpoint, which are located on the same core of the PTT device. You can +set the filter by specifying the `filter` parameter which is required to start +the trace. The parameter value is 20 bit. Bit 19 indicates the filter type. +1 for Root Port filter and 0 for Requester filter. Bit[15:0] indicates the +filter value. The value for a Root Port is a mask of the core port id which is +calculated from its PCI Slot ID as (slotid & 7) * 2. The value for a Requester +is the Requester ID (Device ID of the PCIe function). Bit[18:16] is currently +reserved for extension. + +For example, if the desired filter is Endpoint function 0000:01:00.1 the filter +value will be 0x00101. If the desired filter is Root Port 0000:00:10.0 then +then filter value is calculated as 0x80001. + +Note that multiple Root Ports can be specified at one time, but only one +Endpoint function can be specified in one trace. Specifying both Root Port +and function at the same time is not supported. Driver maintains a list of +available filters and will check the invalid inputs. + +Currently the available filters are detected in driver's probe. If the supported +devices are removed/added after probe, you may need to reload the driver to update +the filters. + +2. Type +------- + +You can trace the TLP headers of certain types by specifying the `type` +parameter, which is required to start the trace. The parameter value is +8 bit. Current supported types and related values are shown below: + +- 8'b00000001: posted requests (P) +- 8'b00000010: non-posted requests (NP) +- 8'b00000100: completions (CPL) + +You can specify multiple types when tracing inbound TLP headers, but can only +specify one when tracing outbound TLP headers. + +3. Direction +------------ + +You can trace the TLP headers from certain direction, which is relative +to the Root Port or the PCIe core, by specifying the `direction` parameter. +This is optional and the default parameter is inbound. The parameter value +is 4 bit. When the desired format is 4DW, directions and related values +supported are shown below: + +- 4'b0000: inbound TLPs (P, NP, CPL) +- 4'b0001: outbound TLPs (P, NP, CPL) +- 4'b0010: outbound TLPs (P, NP, CPL) and inbound TLPs (P, NP, CPL B) +- 4'b0011: outbound TLPs (P, NP, CPL) and inbound TLPs (CPL A) + +When the desired format is 8DW, directions and related values supported are +shown below: + +- 4'b0000: reserved +- 4'b0001: outbound TLPs (P, NP, CPL) +- 4'b0010: inbound TLPs (P, NP, CPL B) +- 4'b0011: inbound TLPs (CPL A) + +Inbound completions are classified into two types: + +- completion A (CPL A): completion of CHI/DMA/Native non-posted requests, except for CPL B +- completion B (CPL B): completion of DMA remote2local and P2P non-posted requests + +4. Format +-------------- + +You can change the format of the traced TLP headers by specifying the +`format` parameter. The default format is 4DW. The parameter value is 4 bit. +Current supported formats and related values are shown below: + +- 4'b0000: 4DW length per TLP header +- 4'b0001: 8DW length per TLP header + +The traced TLP header format is different from the PCIe standard. + +When using the 8DW data format, the entire TLP header is logged +(Header DW0-3 shown below). For example, the TLP header for Memory +Reads with 64-bit addresses is shown in PCIe r5.0, Figure 2-17; +the header for Configuration Requests is shown in Figure 2.20, etc. + +In addition, 8DW trace buffer entries contain a timestamp and +possibly a prefix for a PASID TLP prefix (see Figure 6-20, PCIe r5.0). +Otherwise this field will be all 0. + +The bit[31:11] of DW0 is always 0x1fffff, which can be +used to distinguish the data format. 8DW format is like +:: + + bits [ 31:11 ][ 10:0 ] + |---------------------------------------|-------------------| + DW0 [ 0x1fffff ][ Reserved (0x7ff) ] + DW1 [ Prefix ] + DW2 [ Header DW0 ] + DW3 [ Header DW1 ] + DW4 [ Header DW2 ] + DW5 [ Header DW3 ] + DW6 [ Reserved (0x0) ] + DW7 [ Time ] + +When using the 4DW data format, DW0 of the trace buffer entry +contains selected fields of DW0 of the TLP, together with a +timestamp. DW1-DW3 of the trace buffer entry contain DW1-DW3 +directly from the TLP header. + +4DW format is like +:: + + bits [31:30] [ 29:25 ][24][23][22][21][ 20:11 ][ 10:0 ] + |-----|---------|---|---|---|---|-------------|-------------| + DW0 [ Fmt ][ Type ][T9][T8][TH][SO][ Length ][ Time ] + DW1 [ Header DW1 ] + DW2 [ Header DW2 ] + DW3 [ Header DW3 ] + +5. Memory Management +-------------------- + +The traced TLP headers will be written to the memory allocated +by the driver. The hardware accepts 4 DMA address with same size, +and writes the buffer sequentially like below. If DMA addr 3 is +finished and the trace is still on, it will return to addr 0. +:: + + +->[DMA addr 0]->[DMA addr 1]->[DMA addr 2]->[DMA addr 3]-+ + +---------------------------------------------------------+ + +Driver will allocate each DMA buffer of 4MiB. The finished buffer +will be copied to the perf AUX buffer allocated by the perf core. +Once the AUX buffer is full while the trace is still on, driver +will commit the AUX buffer first and then apply for a new one with +the same size. The size of AUX buffer is default to 16MiB. User can +adjust the size by specifying the `-m` parameter of the perf command. + +6. Decoding +----------- + +You can decode the traced data with `perf report -D` command (currently +only support to dump the raw trace data). The traced data will be decoded +according to the format described previously (take 8DW as an example): +:: + + [...perf headers and other information] + . ... HISI PTT data: size 4194304 bytes + . 00000000: 00 00 00 00 Prefix + . 00000004: 01 00 00 60 Header DW0 + . 00000008: 0f 1e 00 01 Header DW1 + . 0000000c: 04 00 00 00 Header DW2 + . 00000010: 40 00 81 02 Header DW3 + . 00000014: 33 c0 04 00 Time + . 00000020: 00 00 00 00 Prefix + . 00000024: 01 00 00 60 Header DW0 + . 00000028: 0f 1e 00 01 Header DW1 + . 0000002c: 04 00 00 00 Header DW2 + . 00000030: 40 00 81 02 Header DW3 + . 00000034: 02 00 00 00 Time + . 00000040: 00 00 00 00 Prefix + . 00000044: 01 00 00 60 Header DW0 + . 00000048: 0f 1e 00 01 Header DW1 + . 0000004c: 04 00 00 00 Header DW2 + . 00000050: 40 00 81 02 Header DW3 + [...] diff --git a/Documentation/trace/index.rst b/Documentation/trace/index.rst index 2d73e8697523..ea25a9220f92 100644 --- a/Documentation/trace/index.rst +++ b/Documentation/trace/index.rst @@ -33,3 +33,4 @@ Linux Tracing Technologies coresight/index user_events rv/index + hisi-ptt -- cgit v1.2.3 From 366317eae983a0d96aeed78ad219b9c4ed2a719a Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Tue, 16 Aug 2022 19:44:14 +0800 Subject: MAINTAINERS: Add maintainer for HiSilicon PTT driver Add maintainer for driver and documentation of HiSilicon PTT device. Signed-off-by: Yicong Yang Reviewed-by: Jonathan Cameron Link: https://lore.kernel.org/r/20220816114414.4092-6-yangyicong@huawei.com Signed-off-by: Mathieu Poirier --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 9d7f64dc0efe..723e0a6bc683 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9186,6 +9186,14 @@ S: Supported F: Documentation/admin-guide/perf/hns3-pmu.rst F: drivers/perf/hisilicon/hns3_pmu.c +HISILICON PTT DRIVER +M: Yicong Yang +L: linux-kernel@vger.kernel.org +S: Maintained +F: Documentation/ABI/testing/sysfs-devices-hisi_ptt +F: Documentation/trace/hisi-ptt.rst +F: drivers/hwtracing/ptt/ + HISILICON QM DRIVER M: Weili Qian M: Zhou Wang -- cgit v1.2.3 From c15d7e11ae4de6e592c69b7f30312238571a7c78 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 7 Sep 2022 14:58:04 +0000 Subject: misc: microchip: pci1xxxx: fix error handling in gp_aux_bus_probe() In some error handling path, resoures alloced may not released. This patch fix them. Fixes: 393fc2f5948f ("misc: microchip: pci1xxxx: load auxiliary bus driver for the PIO function in the multi-function endpoint of pci1xxxx device.") Reviewed-by: Kumaravel Thiagarajan Signed-off-by: Wei Yongjun Link: https://lore.kernel.org/r/20220907145808.1789249-1-weiyongjun@huaweicloud.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c index bfc03028b34d..edff3ee73f6f 100644 --- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c @@ -87,12 +87,13 @@ static int gp_aux_bus_probe(struct pci_dev *pdev, const struct pci_device_id *id retval = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); if (retval < 0) - return retval; + goto err_aux_dev_init_1; - pdev->irq = pci_irq_vector(pdev, 0); - if (pdev->irq < 0) - return retval; + retval = pci_irq_vector(pdev, 0); + if (retval < 0) + goto err_aux_dev_init_1; + pdev->irq = retval; aux_bus->aux_device_wrapper[1]->gp_aux_data.irq_num = pdev->irq; retval = auxiliary_device_init(&aux_bus->aux_device_wrapper[1]->aux_dev); -- cgit v1.2.3 From 8c297fbdc3a412f6874afade72540470b6ab34ee Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 7 Sep 2022 14:58:05 +0000 Subject: misc: microchip: pci1xxxx: Fix missing spin_lock_init() The driver allocates the spinlock but not initialize it. Use spin_lock_init() on it to initialize it correctly. Fixes: 7d3e4d807df2 ("misc: microchip: pci1xxxx: load gpio driver for the gpio controller auxiliary device enumerated by the auxiliary bus driver.") Reviewed-by: Kumaravel Thiagarajan Signed-off-by: Wei Yongjun Link: https://lore.kernel.org/r/20220907145808.1789249-2-weiyongjun@huaweicloud.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c index 230503cca2ff..47e6e87938ae 100644 --- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c @@ -383,6 +383,7 @@ static int pci1xxxx_gpio_probe(struct auxiliary_device *aux_dev, if (!priv) return -ENOMEM; + spin_lock_init(&priv->lock); priv->aux_dev = aux_dev; if (!devm_request_mem_region(&aux_dev->dev, pdata->region_start, 0x800, aux_dev->name)) -- cgit v1.2.3 From 6b9a8679c2d76c592aca335b5981fde5753546eb Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 7 Sep 2022 14:58:06 +0000 Subject: misc: microchip: pci1xxxx: Make symbol 'pci1xxxx_gpio_auxiliary_id_table' static The sparse tool complains as follows: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c:409:34: warning: symbol 'pci1xxxx_gpio_auxiliary_id_table' was not declared. Should it be static? This symbol is not used outside of mchp_pci1xxxx_gpio.c, so marks it static. Fixes: 7d3e4d807df2 ("misc: microchip: pci1xxxx: load gpio driver for the gpio controller auxiliary device enumerated by the auxiliary bus driver.") Reported-by: kernel test robot Reviewed-by: Kumaravel Thiagarajan Signed-off-by: Wei Yongjun Link: https://lore.kernel.org/r/20220907145808.1789249-3-weiyongjun@huaweicloud.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c index 47e6e87938ae..4f26871994ee 100644 --- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c @@ -407,7 +407,7 @@ static int pci1xxxx_gpio_probe(struct auxiliary_device *aux_dev, static SIMPLE_DEV_PM_OPS(pci1xxxx_gpio_pm_ops, pci1xxxx_gpio_suspend, pci1xxxx_gpio_resume); -const struct auxiliary_device_id pci1xxxx_gpio_auxiliary_id_table[] = { +static const struct auxiliary_device_id pci1xxxx_gpio_auxiliary_id_table[] = { {.name = "mchp_pci1xxxx_gp.gp_gpio"}, {} }; -- cgit v1.2.3 From e102ef816f08f4745c4d68ec0560bdd072a568d4 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 7 Sep 2022 14:58:07 +0000 Subject: misc: microchip: pci1xxxx: Add missing MODULE_DEVICE_TABLE This patch adds missing MODULE_DEVICE_TABLE definition which generates correct modalias for automatic loading of this driver when it is built as an external module. Fixes: 7d3e4d807df2 ("misc: microchip: pci1xxxx: load gpio driver for the gpio controller auxiliary device enumerated by the auxiliary bus driver.") Reviewed-by: Kumaravel Thiagarajan Signed-off-by: Wei Yongjun Link: https://lore.kernel.org/r/20220907145808.1789249-4-weiyongjun@huaweicloud.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c index 4f26871994ee..fa80a7788596 100644 --- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c @@ -411,6 +411,7 @@ static const struct auxiliary_device_id pci1xxxx_gpio_auxiliary_id_table[] = { {.name = "mchp_pci1xxxx_gp.gp_gpio"}, {} }; +MODULE_DEVICE_TABLE(auxiliary, pci1xxxx_gpio_auxiliary_id_table); static struct auxiliary_driver pci1xxxx_gpio_driver = { .driver = { -- cgit v1.2.3 From c5144241d28cfbca11d75ad334bbacabad0becaf Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 7 Sep 2022 14:58:08 +0000 Subject: misc: microchip: pci1xxxx: use module_auxiliary_driver Use the module_auxiliary_driver() macro to make the code simpler by eliminating module_init and module_exit calls. Fixes: 7d3e4d807df2 ("misc: microchip: pci1xxxx: load gpio driver for the gpio controller auxiliary device enumerated by the auxiliary bus driver.") Reviewed-by: Kumaravel Thiagarajan Signed-off-by: Wei Yongjun Link: https://lore.kernel.org/r/20220907145808.1789249-5-weiyongjun@huaweicloud.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c index fa80a7788596..9cc771c604ed 100644 --- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c @@ -421,19 +421,7 @@ static struct auxiliary_driver pci1xxxx_gpio_driver = { .probe = pci1xxxx_gpio_probe, .id_table = pci1xxxx_gpio_auxiliary_id_table }; - -static int __init pci1xxxx_gpio_driver_init(void) -{ - return auxiliary_driver_register(&pci1xxxx_gpio_driver); -} - -static void __exit pci1xxxx_gpio_driver_exit(void) -{ - auxiliary_driver_unregister(&pci1xxxx_gpio_driver); -} - -module_init(pci1xxxx_gpio_driver_init); -module_exit(pci1xxxx_gpio_driver_exit); +module_auxiliary_driver(pci1xxxx_gpio_driver); MODULE_DESCRIPTION("Microchip Technology Inc. PCI1xxxx GPIO controller"); MODULE_AUTHOR("Kumaravel Thiagarajan "); -- cgit v1.2.3 From d4d2c58bdb9190baa1d5e1719c1339fcb2c17642 Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Thu, 1 Sep 2022 22:46:10 +0800 Subject: virt: vbox: convert to use dev_groups The driver core supports the ability to handle the creation and removal of device-specific sysfs files in a race-free manner. Moreover, it can guarantee the success of creation. Therefore, it should be better to convert to use dev_groups. Fixes: 0ba002bc4393 ("virt: Add vboxguest driver for Virtual Box Guest integration") Reviewed-by: Hans de Goede Signed-off-by: Jiasheng Jiang Link: https://lore.kernel.org/r/20220901144610.3550300-1-jiasheng@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- drivers/virt/vboxguest/vboxguest_linux.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/virt/vboxguest/vboxguest_linux.c b/drivers/virt/vboxguest/vboxguest_linux.c index 4ccfd30c2a30..6fc81347ae72 100644 --- a/drivers/virt/vboxguest/vboxguest_linux.c +++ b/drivers/virt/vboxguest/vboxguest_linux.c @@ -270,6 +270,13 @@ static ssize_t host_features_show(struct device *dev, static DEVICE_ATTR_RO(host_version); static DEVICE_ATTR_RO(host_features); +static struct attribute *vbg_pci_attrs[] = { + &dev_attr_host_version.attr, + &dev_attr_host_features.attr, + NULL, +}; +ATTRIBUTE_GROUPS(vbg_pci); + /** * Does the PCI detection and init of the device. * @@ -390,8 +397,6 @@ static int vbg_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) } pci_set_drvdata(pci, gdev); - device_create_file(dev, &dev_attr_host_version); - device_create_file(dev, &dev_attr_host_features); vbg_info("vboxguest: misc device minor %d, IRQ %d, I/O port %x, MMIO at %pap (size %pap)\n", gdev->misc_device.minor, pci->irq, gdev->io_port, @@ -422,8 +427,6 @@ static void vbg_pci_remove(struct pci_dev *pci) mutex_unlock(&vbg_gdev_mutex); free_irq(pci->irq, gdev); - device_remove_file(gdev->dev, &dev_attr_host_features); - device_remove_file(gdev->dev, &dev_attr_host_version); misc_deregister(&gdev->misc_device_user); misc_deregister(&gdev->misc_device); vbg_core_exit(gdev); @@ -488,6 +491,7 @@ MODULE_DEVICE_TABLE(pci, vbg_pci_ids); static struct pci_driver vbg_pci_driver = { .name = DEVICE_NAME, + .dev_groups = vbg_pci_groups, .id_table = vbg_pci_ids, .probe = vbg_pci_probe, .remove = vbg_pci_remove, -- cgit v1.2.3 From 2699e6e9e9481884c4a006f1c62dd6ed59ca38d5 Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Thu, 1 Sep 2022 22:46:19 +0800 Subject: virt: vbox: Remove unproper information When drivers are working properly, they are quiet. Therefore, the vbg_info() should be removed. Suggested-by: Greg Kroah-Hartman Reviewed-by: Hans de Goede Signed-off-by: Jiasheng Jiang Link: https://lore.kernel.org/r/20220901144619.3550352-1-jiasheng@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- drivers/virt/vboxguest/vboxguest_linux.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/virt/vboxguest/vboxguest_linux.c b/drivers/virt/vboxguest/vboxguest_linux.c index 6fc81347ae72..c47e62dc55da 100644 --- a/drivers/virt/vboxguest/vboxguest_linux.c +++ b/drivers/virt/vboxguest/vboxguest_linux.c @@ -398,10 +398,6 @@ static int vbg_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) pci_set_drvdata(pci, gdev); - vbg_info("vboxguest: misc device minor %d, IRQ %d, I/O port %x, MMIO at %pap (size %pap)\n", - gdev->misc_device.minor, pci->irq, gdev->io_port, - &mmio, &mmio_len); - return 0; err_unregister_misc_device_user: -- cgit v1.2.3 From dd8dc442c1e3b1798fad61211f3b090523dd926d Mon Sep 17 00:00:00 2001 From: "Fabio M. De Francesco" Date: Thu, 1 Sep 2022 15:57:14 +0200 Subject: misc/vmw_vmci: Use kmap_local_page() in vmci_queue_pair.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kmap() is being deprecated in favor of kmap_local_page(). There are two main problems with kmap(): (1) It comes with an overhead as the mapping space is restricted and protected by a global lock for synchronization and (2) it also requires global TLB invalidation when the kmap’s pool wraps and it might block when the mapping space is fully utilized until a slot becomes available. With kmap_local_page() the mappings are per thread, CPU local, can take page faults, and can be called from any context (including interrupts). It is faster than kmap() in kernels with HIGHMEM enabled. Furthermore, the tasks can be preempted and, when they are scheduled to run again, the kernel virtual addresses are restored and still valid. Since its use in vmci_queue_pair.c is safe everywhere, replace kmap() with kmap_local_page(). Cc: "Venkataramanan, Anirudh" Suggested-by: Ira Weiny Signed-off-by: Fabio M. De Francesco Link: https://lore.kernel.org/r/20220901135714.16481-1-fmdefrancesco@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_vmci/vmci_queue_pair.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/misc/vmw_vmci/vmci_queue_pair.c b/drivers/misc/vmw_vmci/vmci_queue_pair.c index 8f2de1893245..e71068f7759b 100644 --- a/drivers/misc/vmw_vmci/vmci_queue_pair.c +++ b/drivers/misc/vmw_vmci/vmci_queue_pair.c @@ -324,7 +324,7 @@ static void *qp_alloc_queue(u64 size, u32 flags) /* * Copies from a given buffer or iovector to a VMCI Queue. Uses - * kmap()/kunmap() to dynamically map/unmap required portions of the queue + * kmap_local_page() to dynamically map required portions of the queue * by traversing the offset -> page translation structure for the queue. * Assumes that offset + size does not wrap around in the queue. */ @@ -345,7 +345,7 @@ static int qp_memcpy_to_queue_iter(struct vmci_queue *queue, size_t to_copy; if (kernel_if->host) - va = kmap(kernel_if->u.h.page[page_index]); + va = kmap_local_page(kernel_if->u.h.page[page_index]); else va = kernel_if->u.g.vas[page_index + 1]; /* Skip header. */ @@ -359,12 +359,12 @@ static int qp_memcpy_to_queue_iter(struct vmci_queue *queue, if (!copy_from_iter_full((u8 *)va + page_offset, to_copy, from)) { if (kernel_if->host) - kunmap(kernel_if->u.h.page[page_index]); + kunmap_local(va); return VMCI_ERROR_INVALID_ARGS; } bytes_copied += to_copy; if (kernel_if->host) - kunmap(kernel_if->u.h.page[page_index]); + kunmap_local(va); } return VMCI_SUCCESS; @@ -372,7 +372,7 @@ static int qp_memcpy_to_queue_iter(struct vmci_queue *queue, /* * Copies to a given buffer or iovector from a VMCI Queue. Uses - * kmap()/kunmap() to dynamically map/unmap required portions of the queue + * kmap_local_page() to dynamically map required portions of the queue * by traversing the offset -> page translation structure for the queue. * Assumes that offset + size does not wrap around in the queue. */ @@ -393,7 +393,7 @@ static int qp_memcpy_from_queue_iter(struct iov_iter *to, int err; if (kernel_if->host) - va = kmap(kernel_if->u.h.page[page_index]); + va = kmap_local_page(kernel_if->u.h.page[page_index]); else va = kernel_if->u.g.vas[page_index + 1]; /* Skip header. */ @@ -407,12 +407,12 @@ static int qp_memcpy_from_queue_iter(struct iov_iter *to, err = copy_to_iter((u8 *)va + page_offset, to_copy, to); if (err != to_copy) { if (kernel_if->host) - kunmap(kernel_if->u.h.page[page_index]); + kunmap_local(va); return VMCI_ERROR_INVALID_ARGS; } bytes_copied += to_copy; if (kernel_if->host) - kunmap(kernel_if->u.h.page[page_index]); + kunmap_local(va); } return VMCI_SUCCESS; -- cgit v1.2.3 From e01b08d7f6d36f20533b7510ea3af90756125f35 Mon Sep 17 00:00:00 2001 From: "Fabio M. De Francesco" Date: Thu, 1 Sep 2022 17:44:07 +0200 Subject: misc/xilinx_sdfec: Call kunmap() on pages mapped with kmap() Pages in an array are mapped in a loop but, after the code is done with the virtual addresses, these pages are never unmapped. Therefore, call kunmap() to unmap pages[i]. Cc: "Venkataramanan, Anirudh" Cc: Ira Weiny Signed-off-by: Fabio M. De Francesco Link: https://lore.kernel.org/r/20220901154408.23984-2-fmdefrancesco@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/xilinx_sdfec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/xilinx_sdfec.c b/drivers/misc/xilinx_sdfec.c index d6e3c650bd11..4b1d82ae7312 100644 --- a/drivers/misc/xilinx_sdfec.c +++ b/drivers/misc/xilinx_sdfec.c @@ -645,6 +645,7 @@ static int xsdfec_table_write(struct xsdfec_dev *xsdfec, u32 offset, reg++; } while ((reg < len) && ((reg * XSDFEC_REG_WIDTH_JUMP) % PAGE_SIZE)); + kunmap(pages[i]); unpin_user_page(pages[i]); } return 0; -- cgit v1.2.3 From eb3b3c93afc4a4244590f7d1e715e8b6749585a9 Mon Sep 17 00:00:00 2001 From: "Fabio M. De Francesco" Date: Thu, 1 Sep 2022 17:44:08 +0200 Subject: misc/xilinx_sdfec: Replace kmap() with kmap_local_page() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kmap() is being deprecated in favor of kmap_local_page(). There are two main problems with kmap(): (1) It comes with an overhead as the mapping space is restricted and protected by a global lock for synchronization and (2) it also requires global TLB invalidation when the kmap’s pool wraps and it might block when the mapping space is fully utilized until a slot becomes available. With kmap_local_page() the mappings are per thread, CPU local, can take page faults, and can be called from any context (including interrupts). It is faster than kmap() in kernels with HIGHMEM enabled. Furthermore, the tasks can be preempted and, when they are scheduled to run again, the kernel virtual addresses are restored and still valid. Since its use in xilinx_sdfec.c is safe, replace kmap()i / kunmap() with kmap_local_page() / kunmap_local(). Cc: "Venkataramanan, Anirudh" Suggested-by: Ira Weiny Signed-off-by: Fabio M. De Francesco Link: https://lore.kernel.org/r/20220901154408.23984-3-fmdefrancesco@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/xilinx_sdfec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/xilinx_sdfec.c b/drivers/misc/xilinx_sdfec.c index 4b1d82ae7312..cb9506f9cbd0 100644 --- a/drivers/misc/xilinx_sdfec.c +++ b/drivers/misc/xilinx_sdfec.c @@ -636,7 +636,7 @@ static int xsdfec_table_write(struct xsdfec_dev *xsdfec, u32 offset, } for (i = 0; i < nr_pages; i++) { - addr = kmap(pages[i]); + addr = kmap_local_page(pages[i]); do { xsdfec_regwrite(xsdfec, base_addr + ((offset + reg) * @@ -645,7 +645,7 @@ static int xsdfec_table_write(struct xsdfec_dev *xsdfec, u32 offset, reg++; } while ((reg < len) && ((reg * XSDFEC_REG_WIDTH_JUMP) % PAGE_SIZE)); - kunmap(pages[i]); + kunmap_local(addr); unpin_user_page(pages[i]); } return 0; -- cgit v1.2.3 From 3e42deaac06567c7e86d287c305ccda24db4ae3d Mon Sep 17 00:00:00 2001 From: Shunsuke Mie Date: Wed, 7 Sep 2022 11:00:59 +0900 Subject: misc: pci_endpoint_test: Aggregate params checking for xfer Each transfer test functions have same parameter checking code. This patch unites those to an introduced function. Signed-off-by: Shunsuke Mie Cc: stable Link: https://lore.kernel.org/r/20220907020100.122588-1-mie@igel.co.jp Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pci_endpoint_test.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 8f786a225dcf..39d477bb0989 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -332,6 +332,17 @@ static bool pci_endpoint_test_msi_irq(struct pci_endpoint_test *test, return false; } +static int pci_endpoint_test_validate_xfer_params(struct device *dev, + struct pci_endpoint_test_xfer_param *param, size_t alignment) +{ + if (param->size > SIZE_MAX - alignment) { + dev_dbg(dev, "Maximum transfer data size exceeded\n"); + return -EINVAL; + } + + return 0; +} + static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, unsigned long arg) { @@ -363,9 +374,11 @@ static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, return false; } + err = pci_endpoint_test_validate_xfer_params(dev, ¶m, alignment); + if (err) + return false; + size = param.size; - if (size > SIZE_MAX - alignment) - goto err; use_dma = !!(param.flags & PCITEST_FLAGS_USE_DMA); if (use_dma) @@ -497,9 +510,11 @@ static bool pci_endpoint_test_write(struct pci_endpoint_test *test, return false; } + err = pci_endpoint_test_validate_xfer_params(dev, ¶m, alignment); + if (err) + return false; + size = param.size; - if (size > SIZE_MAX - alignment) - goto err; use_dma = !!(param.flags & PCITEST_FLAGS_USE_DMA); if (use_dma) @@ -595,9 +610,11 @@ static bool pci_endpoint_test_read(struct pci_endpoint_test *test, return false; } + err = pci_endpoint_test_validate_xfer_params(dev, ¶m, alignment); + if (err) + return false; + size = param.size; - if (size > SIZE_MAX - alignment) - goto err; use_dma = !!(param.flags & PCITEST_FLAGS_USE_DMA); if (use_dma) -- cgit v1.2.3 From 8e30538eca016de8e252bef174beadecd64239f0 Mon Sep 17 00:00:00 2001 From: Shunsuke Mie Date: Wed, 7 Sep 2022 11:01:00 +0900 Subject: misc: pci_endpoint_test: Fix pci_endpoint_test_{copy,write,read}() panic The dma_map_single() doesn't permit zero length mapping. It causes a follow panic. A panic was reported on arm64: [ 60.137988] ------------[ cut here ]------------ [ 60.142630] kernel BUG at kernel/dma/swiotlb.c:624! [ 60.147508] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP [ 60.152992] Modules linked in: dw_hdmi_cec crct10dif_ce simple_bridge rcar_fdp1 vsp1 rcar_vin videobuf2_vmalloc rcar_csi2 v4l 2_mem2mem videobuf2_dma_contig videobuf2_memops pci_endpoint_test videobuf2_v4l2 videobuf2_common rcar_fcp v4l2_fwnode v4l2_asyn c videodev mc gpio_bd9571mwv max9611 pwm_rcar ccree at24 authenc libdes phy_rcar_gen3_usb3 usb_dmac display_connector pwm_bl [ 60.186252] CPU: 0 PID: 508 Comm: pcitest Not tainted 6.0.0-rc1rpci-dev+ #237 [ 60.193387] Hardware name: Renesas Salvator-X 2nd version board based on r8a77951 (DT) [ 60.201302] pstate: 00000005 (nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 60.208263] pc : swiotlb_tbl_map_single+0x2c0/0x590 [ 60.213149] lr : swiotlb_map+0x88/0x1f0 [ 60.216982] sp : ffff80000a883bc0 [ 60.220292] x29: ffff80000a883bc0 x28: 0000000000000000 x27: 0000000000000000 [ 60.227430] x26: 0000000000000000 x25: ffff0004c0da20d0 x24: ffff80000a1f77c0 [ 60.234567] x23: 0000000000000002 x22: 0001000040000010 x21: 000000007a000000 [ 60.241703] x20: 0000000000200000 x19: 0000000000000000 x18: 0000000000000000 [ 60.248840] x17: 0000000000000000 x16: 0000000000000000 x15: ffff0006ff7b9180 [ 60.255977] x14: ffff0006ff7b9180 x13: 0000000000000000 x12: 0000000000000000 [ 60.263113] x11: 0000000000000000 x10: 0000000000000000 x9 : 0000000000000000 [ 60.270249] x8 : 0001000000000010 x7 : ffff0004c6754b20 x6 : 0000000000000000 [ 60.277385] x5 : ffff0004c0da2090 x4 : 0000000000000000 x3 : 0000000000000001 [ 60.284521] x2 : 0000000040000000 x1 : 0000000000000000 x0 : 0000000040000010 [ 60.291658] Call trace: [ 60.294100] swiotlb_tbl_map_single+0x2c0/0x590 [ 60.298629] swiotlb_map+0x88/0x1f0 [ 60.302115] dma_map_page_attrs+0x188/0x230 [ 60.306299] pci_endpoint_test_ioctl+0x5e4/0xd90 [pci_endpoint_test] [ 60.312660] __arm64_sys_ioctl+0xa8/0xf0 [ 60.316583] invoke_syscall+0x44/0x108 [ 60.320334] el0_svc_common.constprop.0+0xcc/0xf0 [ 60.325038] do_el0_svc+0x2c/0xb8 [ 60.328351] el0_svc+0x2c/0x88 [ 60.331406] el0t_64_sync_handler+0xb8/0xc0 [ 60.335587] el0t_64_sync+0x18c/0x190 [ 60.339251] Code: 52800013 d2e00414 35fff45c d503201f (d4210000) [ 60.345344] ---[ end trace 0000000000000000 ]--- To fix it, this patch adds a checking the payload length if it is zero. Fixes: 343dc693f7b7 ("misc: pci_endpoint_test: Prevent some integer overflows") Cc: stable Signed-off-by: Shunsuke Mie Link: https://lore.kernel.org/r/20220907020100.122588-2-mie@igel.co.jp Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pci_endpoint_test.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 39d477bb0989..11530b4ec389 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -335,6 +335,11 @@ static bool pci_endpoint_test_msi_irq(struct pci_endpoint_test *test, static int pci_endpoint_test_validate_xfer_params(struct device *dev, struct pci_endpoint_test_xfer_param *param, size_t alignment) { + if (!param->size) { + dev_dbg(dev, "Data size is zero\n"); + return -EINVAL; + } + if (param->size > SIZE_MAX - alignment) { dev_dbg(dev, "Maximum transfer data size exceeded\n"); return -EINVAL; -- cgit v1.2.3 From ceecbbddbf549fe0b7ffa3804a6e255b3360030f Mon Sep 17 00:00:00 2001 From: Xuezhi Zhang Date: Thu, 1 Sep 2022 09:34:23 +0800 Subject: comedi: convert sysfs snprintf to sysfs_emit Follow the advice of the Documentation/filesystems/sysfs.rst and show() should only use sysfs_emit() or sysfs_emit_at() when formatting the value to be returned to user space. Signed-off-by: Xuezhi Zhang Link: https://lore.kernel.org/r/20220901013423.418464-1-zhangxuezhi3@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/comedi_fops.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/comedi/comedi_fops.c b/drivers/comedi/comedi_fops.c index 55a0cae04b8d..e2114bcf815a 100644 --- a/drivers/comedi/comedi_fops.c +++ b/drivers/comedi/comedi_fops.c @@ -396,7 +396,7 @@ static ssize_t max_read_buffer_kb_show(struct device *csdev, mutex_unlock(&dev->mutex); comedi_dev_put(dev); - return snprintf(buf, PAGE_SIZE, "%u\n", size); + return sysfs_emit(buf, "%u\n", size); } static ssize_t max_read_buffer_kb_store(struct device *csdev, @@ -452,7 +452,7 @@ static ssize_t read_buffer_kb_show(struct device *csdev, mutex_unlock(&dev->mutex); comedi_dev_put(dev); - return snprintf(buf, PAGE_SIZE, "%u\n", size); + return sysfs_emit(buf, "%u\n", size); } static ssize_t read_buffer_kb_store(struct device *csdev, @@ -509,7 +509,7 @@ static ssize_t max_write_buffer_kb_show(struct device *csdev, mutex_unlock(&dev->mutex); comedi_dev_put(dev); - return snprintf(buf, PAGE_SIZE, "%u\n", size); + return sysfs_emit(buf, "%u\n", size); } static ssize_t max_write_buffer_kb_store(struct device *csdev, @@ -565,7 +565,7 @@ static ssize_t write_buffer_kb_show(struct device *csdev, mutex_unlock(&dev->mutex); comedi_dev_put(dev); - return snprintf(buf, PAGE_SIZE, "%u\n", size); + return sysfs_emit(buf, "%u\n", size); } static ssize_t write_buffer_kb_store(struct device *csdev, -- cgit v1.2.3 From 562d0bf2cab80810fa50a1f109a6bdb61c25efc2 Mon Sep 17 00:00:00 2001 From: Russ Weight Date: Fri, 2 Sep 2022 09:57:06 -0700 Subject: fpga: m10bmc-sec: d5005 bmc secure update driver Add a driver name for the D5005 BMC secure update driver. Different driver names are used for the N3000 and D5005 devices because future changes will add conditional code based on the device type (N3000 vs D5005). This change enables D5005 secure updates of BCM images, BMC firmware, static-region images, etc. Signed-off-by: Russ Weight Acked-by: Xu Yilun Link: https://lore.kernel.org/r/20220902165706.518074-3-russell.h.weight@intel.com Signed-off-by: Xu Yilun --- drivers/fpga/intel-m10-bmc-sec-update.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/fpga/intel-m10-bmc-sec-update.c b/drivers/fpga/intel-m10-bmc-sec-update.c index 72c677c910de..526c8cdd1474 100644 --- a/drivers/fpga/intel-m10-bmc-sec-update.c +++ b/drivers/fpga/intel-m10-bmc-sec-update.c @@ -605,6 +605,9 @@ static const struct platform_device_id intel_m10bmc_sec_ids[] = { { .name = "n3000bmc-sec-update", }, + { + .name = "d5005bmc-sec-update", + }, { } }; MODULE_DEVICE_TABLE(platform, intel_m10bmc_sec_ids); -- cgit v1.2.3 From 5fc1531dd771cd1481116a66f992a190e01efce6 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Mon, 12 Sep 2022 10:03:59 -0600 Subject: hwtracing: hisi_ptt: Fix up for "iommu/dma: Make header private" drivers/hwtracing/ptt/hisi_ptt.c:13:10: fatal error: linux/dma-iommu.h: No such file or directory 13 | #include | ^~~~~~~~~~~~~~~~~~~ Caused by: commit ff0de066b463 ("hwtracing: hisi_ptt: Add trace function support for HiSilicon PCIe Tune and Trace device") interacting with: commit f2042ed21da7 ("iommu/dma: Make header private") from the iommu tree. Signed-off-by: Stephen Rothwell Acked-by: Robin Murphy Acked-by: Yicong Yang [Fixed subject line and added changelog text] Signed-off-by: Mathieu Poirier --- drivers/hwtracing/ptt/hisi_ptt.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/hwtracing/ptt/hisi_ptt.c b/drivers/hwtracing/ptt/hisi_ptt.c index 666a0f14b6c4..5d5526aa60c4 100644 --- a/drivers/hwtracing/ptt/hisi_ptt.c +++ b/drivers/hwtracing/ptt/hisi_ptt.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From e4e9631b2c8886be172f99bd29f326ee56adc2d2 Mon Sep 17 00:00:00 2001 From: Koen Vandeputte Date: Wed, 31 Aug 2022 12:03:49 +0200 Subject: bus: mhi: host: always print detected modem name This harmless print provides a very easy way of knowing if the modem is detected properly during probing. Promote it to an informational print so no hassle is required enabling kernel debugging info to obtain it. The rationale here is that: On a lot of low-storage embedded devices, extensive kernel debugging info is not always present as this would increase it's size to much causing partition size issues. Signed-off-by: Koen Vandeputte Reviewed-by: Manivannan Sadhasivam Reviewed-by: Loic Poulain Link: https://lore.kernel.org/r/20220831100349.1488762-1-koen.vandeputte@citymesh.com [mani: added missing review tags] Signed-off-by: Manivannan Sadhasivam --- drivers/bus/mhi/host/pci_generic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index 9e545f2a5a26..51e2b901bae0 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -841,7 +841,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) struct mhi_controller *mhi_cntrl; int err; - dev_dbg(&pdev->dev, "MHI PCI device found: %s\n", info->name); + dev_info(&pdev->dev, "MHI PCI device found: %s\n", info->name); /* mhi_pdev.mhi_cntrl must be zero-initialized */ mhi_pdev = devm_kzalloc(&pdev->dev, sizeof(*mhi_pdev), GFP_KERNEL); -- cgit v1.2.3 From 71386e11f262f5fc66000c0d83b4fd8cbbaf9d38 Mon Sep 17 00:00:00 2001 From: Dani Liberman Date: Sun, 3 Jul 2022 17:40:57 +0300 Subject: habanalabs: removed seq_file parameter from is_idle asic functions Change is_idle functions so it would be more usable outside debugfs. Do this by replacing seq_file parameter with regular string. Signed-off-by: Dani Liberman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/debugfs.c | 47 +++++++++++++- drivers/misc/habanalabs/common/habanalabs.h | 17 ++++- drivers/misc/habanalabs/gaudi/gaudi.c | 47 +++++++------- drivers/misc/habanalabs/gaudi2/gaudi2.c | 97 +++++++++++++++-------------- drivers/misc/habanalabs/goya/goya.c | 32 +++++----- 5 files changed, 151 insertions(+), 89 deletions(-) diff --git a/drivers/misc/habanalabs/common/debugfs.c b/drivers/misc/habanalabs/common/debugfs.c index 64439f33a19b..90c91c1b2c10 100644 --- a/drivers/misc/habanalabs/common/debugfs.c +++ b/drivers/misc/habanalabs/common/debugfs.c @@ -17,6 +17,7 @@ #define MMU_ASID_BUF_SIZE 10 #define MMU_KBUF_SIZE (MMU_ADDR_BUF_SIZE + MMU_ASID_BUF_SIZE) #define I2C_MAX_TRANSACTION_LEN 8 +#define ENGINES_DATA_MAX_SIZE SZ_16K static struct dentry *hl_debug_root; @@ -586,11 +587,37 @@ err: return -EINVAL; } +void hl_engine_data_sprintf(struct engines_data *e, const char *fmt, ...) +{ + va_list args; + int str_size; + + va_start(args, fmt); + /* Calculate formatted string length. Assuming each string is null terminated, hence + * increment result by 1 + */ + str_size = vsnprintf(NULL, 0, fmt, args) + 1; + va_end(args); + + if ((e->actual_size + str_size) < e->allocated_buf_size) { + va_start(args, fmt); + vsnprintf(e->buf + e->actual_size, str_size, fmt, args); + va_end(args); + } + + /* Need to update the size even when not updating destination buffer to get the exact size + * of all input strings + */ + e->actual_size += str_size; + +} + static int engines_show(struct seq_file *s, void *data) { struct hl_debugfs_entry *entry = s->private; struct hl_dbg_device_entry *dev_entry = entry->dev_entry; struct hl_device *hdev = dev_entry->hdev; + struct engines_data eng_data; if (hdev->reset_info.in_reset) { dev_warn_ratelimited(hdev->dev, @@ -598,7 +625,25 @@ static int engines_show(struct seq_file *s, void *data) return 0; } - hdev->asic_funcs->is_device_idle(hdev, NULL, 0, s); + eng_data.actual_size = 0; + eng_data.allocated_buf_size = ENGINES_DATA_MAX_SIZE; + eng_data.buf = vmalloc(eng_data.allocated_buf_size); + if (!eng_data.buf) + return -ENOMEM; + + hdev->asic_funcs->is_device_idle(hdev, NULL, 0, &eng_data); + + if (eng_data.actual_size > eng_data.allocated_buf_size) { + dev_err(hdev->dev, + "Engines data size (%d Bytes) is bigger than allocated size (%u Bytes)\n", + eng_data.actual_size, eng_data.allocated_buf_size); + vfree(eng_data.buf); + return -ENOMEM; + } + + seq_write(s, eng_data.buf, eng_data.actual_size); + + vfree(eng_data.buf); return 0; } diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index d59bba9e55c9..440e154dbe31 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -1371,6 +1371,18 @@ struct fw_load_mgr { struct hl_cs; +/** + * struct engines_data - asic engines data + * @buf: buffer for engines data in ascii + * @actual_size: actual size of data that was written by the driver to the allocated buffer + * @allocated_buf_size: total size of allocated buffer + */ +struct engines_data { + char *buf; + int actual_size; + u32 allocated_buf_size; +}; + /** * struct hl_asic_funcs - ASIC specific functions that are can be called from * common code. @@ -1570,8 +1582,8 @@ struct hl_asic_funcs { int (*mmu_prefetch_cache_range)(struct hl_ctx *ctx, u32 flags, u32 asid, u64 va, u64 size); int (*send_heartbeat)(struct hl_device *hdev); int (*debug_coresight)(struct hl_device *hdev, struct hl_ctx *ctx, void *data); - bool (*is_device_idle)(struct hl_device *hdev, u64 *mask_arr, - u8 mask_len, struct seq_file *s); + bool (*is_device_idle)(struct hl_device *hdev, u64 *mask_arr, u8 mask_len, + struct engines_data *e); int (*non_hard_reset_late_init)(struct hl_device *hdev); void (*hw_queues_lock)(struct hl_device *hdev); void (*hw_queues_unlock)(struct hl_device *hdev); @@ -3743,6 +3755,7 @@ struct hl_mmap_mem_buf * hl_mmap_mem_buf_alloc(struct hl_mem_mgr *mmg, struct hl_mmap_mem_buf_behavior *behavior, gfp_t gfp, void *args); +__printf(2, 3) void hl_engine_data_sprintf(struct engines_data *e, const char *fmt, ...); #ifdef CONFIG_DEBUG_FS diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index cb2988e2c7a8..a3eca13c3fa9 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -8066,8 +8066,8 @@ static int gaudi_cpucp_info_get(struct hl_device *hdev) return 0; } -static bool gaudi_is_device_idle(struct hl_device *hdev, u64 *mask_arr, - u8 mask_len, struct seq_file *s) +static bool gaudi_is_device_idle(struct hl_device *hdev, u64 *mask_arr, u8 mask_len, + struct engines_data *e) { struct gaudi_device *gaudi = hdev->asic_specific; const char *fmt = "%-5d%-9s%#-14x%#-12x%#x\n"; @@ -8079,8 +8079,8 @@ static bool gaudi_is_device_idle(struct hl_device *hdev, u64 *mask_arr, u64 offset; int i, dma_id, port; - if (s) - seq_puts(s, + if (e) + hl_engine_data_sprintf(e, "\nDMA is_idle QM_GLBL_STS0 QM_CGM_STS DMA_CORE_STS0\n" "--- ------- ------------ ---------- -------------\n"); @@ -8097,14 +8097,14 @@ static bool gaudi_is_device_idle(struct hl_device *hdev, u64 *mask_arr, if (mask && !is_eng_idle) set_bit(GAUDI_ENGINE_ID_DMA_0 + dma_id, mask); - if (s) - seq_printf(s, fmt, dma_id, + if (e) + hl_engine_data_sprintf(e, fmt, dma_id, is_eng_idle ? "Y" : "N", qm_glbl_sts0, qm_cgm_sts, dma_core_sts0); } - if (s) - seq_puts(s, + if (e) + hl_engine_data_sprintf(e, "\nTPC is_idle QM_GLBL_STS0 QM_CGM_STS CFG_STATUS\n" "--- ------- ------------ ---------- ----------\n"); @@ -8119,14 +8119,14 @@ static bool gaudi_is_device_idle(struct hl_device *hdev, u64 *mask_arr, if (mask && !is_eng_idle) set_bit(GAUDI_ENGINE_ID_TPC_0 + i, mask); - if (s) - seq_printf(s, fmt, i, + if (e) + hl_engine_data_sprintf(e, fmt, i, is_eng_idle ? "Y" : "N", qm_glbl_sts0, qm_cgm_sts, tpc_cfg_sts); } - if (s) - seq_puts(s, + if (e) + hl_engine_data_sprintf(e, "\nMME is_idle QM_GLBL_STS0 QM_CGM_STS ARCH_STATUS\n" "--- ------- ------------ ---------- -----------\n"); @@ -8147,20 +8147,21 @@ static bool gaudi_is_device_idle(struct hl_device *hdev, u64 *mask_arr, if (mask && !is_eng_idle) set_bit(GAUDI_ENGINE_ID_MME_0 + i, mask); - if (s) { + if (e) { if (!is_slave) - seq_printf(s, fmt, i, + hl_engine_data_sprintf(e, fmt, i, is_eng_idle ? "Y" : "N", qm_glbl_sts0, qm_cgm_sts, mme_arch_sts); else - seq_printf(s, mme_slave_fmt, i, + hl_engine_data_sprintf(e, mme_slave_fmt, i, is_eng_idle ? "Y" : "N", "-", "-", mme_arch_sts); } } - if (s) - seq_puts(s, "\nNIC is_idle QM_GLBL_STS0 QM_CGM_STS\n" + if (e) + hl_engine_data_sprintf(e, + "\nNIC is_idle QM_GLBL_STS0 QM_CGM_STS\n" "--- ------- ------------ ----------\n"); for (i = 0 ; i < (NIC_NUMBER_OF_ENGINES / 2) ; i++) { @@ -8174,8 +8175,8 @@ static bool gaudi_is_device_idle(struct hl_device *hdev, u64 *mask_arr, if (mask && !is_eng_idle) set_bit(GAUDI_ENGINE_ID_NIC_0 + port, mask); - if (s) - seq_printf(s, nic_fmt, port, + if (e) + hl_engine_data_sprintf(e, nic_fmt, port, is_eng_idle ? "Y" : "N", qm_glbl_sts0, qm_cgm_sts); } @@ -8189,15 +8190,15 @@ static bool gaudi_is_device_idle(struct hl_device *hdev, u64 *mask_arr, if (mask && !is_eng_idle) set_bit(GAUDI_ENGINE_ID_NIC_0 + port, mask); - if (s) - seq_printf(s, nic_fmt, port, + if (e) + hl_engine_data_sprintf(e, nic_fmt, port, is_eng_idle ? "Y" : "N", qm_glbl_sts0, qm_cgm_sts); } } - if (s) - seq_puts(s, "\n"); + if (e) + hl_engine_data_sprintf(e, "\n"); return is_idle; } diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index 98336a1a84b0..1140cf7db4a3 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -1663,7 +1663,7 @@ struct gaudi2_cache_invld_params { }; struct gaudi2_tpc_idle_data { - struct seq_file *s; + struct engines_data *e; unsigned long *mask; bool *is_idle; const char *tpc_fmt; @@ -6172,14 +6172,15 @@ static void gaudi2_is_tpc_engine_idle(struct hl_device *hdev, int dcore, int ins if (idle_data->mask && !is_eng_idle) set_bit(engine_idx, idle_data->mask); - if (idle_data->s) - seq_printf(idle_data->s, idle_data->tpc_fmt, dcore, inst, + if (idle_data->e) + hl_engine_data_sprintf(idle_data->e, + idle_data->tpc_fmt, dcore, inst, is_eng_idle ? "Y" : "N", qm_glbl_sts0, qm_cgm_sts, tpc_cfg_sts); } -static bool gaudi2_is_device_idle(struct hl_device *hdev, u64 *mask_arr, - u8 mask_len, struct seq_file *s) +static bool gaudi2_is_device_idle(struct hl_device *hdev, u64 *mask_arr, u8 mask_len, + struct engines_data *e) { u32 qm_glbl_sts0, qm_glbl_sts1, qm_cgm_sts, dma_core_idle_ind_mask, mme_arch_sts, dec_swreg15, dec_enabled_bit; @@ -6197,7 +6198,7 @@ static bool gaudi2_is_device_idle(struct hl_device *hdev, u64 *mask_arr, struct gaudi2_tpc_idle_data tpc_idle_data = { .tpc_fmt = "%-6d%-5d%-9s%#-14x%#-12x%#x\n", - .s = s, + .e = e, .mask = mask, .is_idle = &is_idle, }; @@ -6209,8 +6210,8 @@ static bool gaudi2_is_device_idle(struct hl_device *hdev, u64 *mask_arr, int engine_idx, i, j; /* EDMA, Two engines per Dcore */ - if (s) - seq_puts(s, + if (e) + hl_engine_data_sprintf(e, "\nCORE EDMA is_idle QM_GLBL_STS0 DMA_CORE_IDLE_IND_MASK\n" "---- ---- ------- ------------ ----------------------\n"); @@ -6239,19 +6240,19 @@ static bool gaudi2_is_device_idle(struct hl_device *hdev, u64 *mask_arr, if (mask && !is_eng_idle) set_bit(engine_idx, mask); - if (s) - seq_printf(s, edma_fmt, i, j, - is_eng_idle ? "Y" : "N", - qm_glbl_sts0, - dma_core_idle_ind_mask); + if (e) + hl_engine_data_sprintf(e, edma_fmt, i, j, + is_eng_idle ? "Y" : "N", + qm_glbl_sts0, + dma_core_idle_ind_mask); } } /* PDMA, Two engines in Full chip */ - if (s) - seq_puts(s, - "\nPDMA is_idle QM_GLBL_STS0 DMA_CORE_IDLE_IND_MASK\n" - "---- ------- ------------ ----------------------\n"); + if (e) + hl_engine_data_sprintf(e, + "\nPDMA is_idle QM_GLBL_STS0 DMA_CORE_IDLE_IND_MASK\n" + "---- ------- ------------ ----------------------\n"); for (i = 0 ; i < NUM_OF_PDMA ; i++) { engine_idx = GAUDI2_ENGINE_ID_PDMA_0 + i; @@ -6269,16 +6270,16 @@ static bool gaudi2_is_device_idle(struct hl_device *hdev, u64 *mask_arr, if (mask && !is_eng_idle) set_bit(engine_idx, mask); - if (s) - seq_printf(s, pdma_fmt, i, is_eng_idle ? "Y" : "N", qm_glbl_sts0, - dma_core_idle_ind_mask); + if (e) + hl_engine_data_sprintf(e, pdma_fmt, i, is_eng_idle ? "Y" : "N", + qm_glbl_sts0, dma_core_idle_ind_mask); } /* NIC, twelve macros in Full chip */ - if (s && hdev->nic_ports_mask) - seq_puts(s, - "\nNIC is_idle QM_GLBL_STS0 QM_CGM_STS\n" - "--- ------- ------------ ----------\n"); + if (e && hdev->nic_ports_mask) + hl_engine_data_sprintf(e, + "\nNIC is_idle QM_GLBL_STS0 QM_CGM_STS\n" + "--- ------- ------------ ----------\n"); for (i = 0 ; i < NIC_NUMBER_OF_ENGINES ; i++) { if (!(i & 1)) @@ -6302,15 +6303,15 @@ static bool gaudi2_is_device_idle(struct hl_device *hdev, u64 *mask_arr, if (mask && !is_eng_idle) set_bit(engine_idx, mask); - if (s) - seq_printf(s, nic_fmt, i, is_eng_idle ? "Y" : "N", qm_glbl_sts0, - qm_cgm_sts); + if (e) + hl_engine_data_sprintf(e, nic_fmt, i, is_eng_idle ? "Y" : "N", + qm_glbl_sts0, qm_cgm_sts); } - if (s) - seq_puts(s, - "\nMME Stub is_idle QM_GLBL_STS0 MME_ARCH_STATUS\n" - "--- ---- ------- ------------ ---------------\n"); + if (e) + hl_engine_data_sprintf(e, + "\nMME Stub is_idle QM_GLBL_STS0 MME_ARCH_STATUS\n" + "--- ---- ------- ------------ ---------------\n"); /* MME, one per Dcore */ for (i = 0 ; i < NUM_OF_DCORES ; i++) { engine_idx = GAUDI2_DCORE0_ENGINE_ID_MME + i * GAUDI2_ENGINE_ID_DCORE_OFFSET; @@ -6327,8 +6328,8 @@ static bool gaudi2_is_device_idle(struct hl_device *hdev, u64 *mask_arr, is_eng_idle &= IS_MME_IDLE(mme_arch_sts); is_idle &= is_eng_idle; - if (s) - seq_printf(s, mme_fmt, i, "N", + if (e) + hl_engine_data_sprintf(e, mme_fmt, i, "N", is_eng_idle ? "Y" : "N", qm_glbl_sts0, mme_arch_sts); @@ -6340,16 +6341,16 @@ static bool gaudi2_is_device_idle(struct hl_device *hdev, u64 *mask_arr, /* * TPC */ - if (s && prop->tpc_enabled_mask) - seq_puts(s, + if (e && prop->tpc_enabled_mask) + hl_engine_data_sprintf(e, "\nCORE TPC is_idle QM_GLBL_STS0 QM_CGM_STS DMA_CORE_IDLE_IND_MASK\n" "---- --- -------- ------------ ---------- ----------------------\n"); gaudi2_iterate_tpcs(hdev, &tpc_iter); /* Decoders, two each Dcore and two shared PCIe decoders */ - if (s && (prop->decoder_enabled_mask & (~PCIE_DEC_EN_MASK))) - seq_puts(s, + if (e && (prop->decoder_enabled_mask & (~PCIE_DEC_EN_MASK))) + hl_engine_data_sprintf(e, "\nCORE DEC is_idle VSI_CMD_SWREG15\n" "---- --- ------- ---------------\n"); @@ -6370,13 +6371,14 @@ static bool gaudi2_is_device_idle(struct hl_device *hdev, u64 *mask_arr, if (mask && !is_eng_idle) set_bit(engine_idx, mask); - if (s) - seq_printf(s, dec_fmt, i, j, is_eng_idle ? "Y" : "N", dec_swreg15); + if (e) + hl_engine_data_sprintf(e, dec_fmt, i, j, + is_eng_idle ? "Y" : "N", dec_swreg15); } } - if (s && (prop->decoder_enabled_mask & PCIE_DEC_EN_MASK)) - seq_puts(s, + if (e && (prop->decoder_enabled_mask & PCIE_DEC_EN_MASK)) + hl_engine_data_sprintf(e, "\nPCIe DEC is_idle VSI_CMD_SWREG15\n" "-------- ------- ---------------\n"); @@ -6395,12 +6397,13 @@ static bool gaudi2_is_device_idle(struct hl_device *hdev, u64 *mask_arr, if (mask && !is_eng_idle) set_bit(engine_idx, mask); - if (s) - seq_printf(s, pcie_dec_fmt, i, is_eng_idle ? "Y" : "N", dec_swreg15); + if (e) + hl_engine_data_sprintf(e, pcie_dec_fmt, i, + is_eng_idle ? "Y" : "N", dec_swreg15); } - if (s) - seq_puts(s, + if (e) + hl_engine_data_sprintf(e, "\nCORE ROT is_idle QM_GLBL_STS0 QM_CGM_STS DMA_CORE_STS0\n" "---- ---- ------- ------------ ---------- -------------\n"); @@ -6419,8 +6422,8 @@ static bool gaudi2_is_device_idle(struct hl_device *hdev, u64 *mask_arr, if (mask && !is_eng_idle) set_bit(engine_idx, mask); - if (s) - seq_printf(s, rot_fmt, i, 0, is_eng_idle ? "Y" : "N", + if (e) + hl_engine_data_sprintf(e, rot_fmt, i, 0, is_eng_idle ? "Y" : "N", qm_glbl_sts0, qm_cgm_sts, "-"); } diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index db4487c33582..7b9f7f8b51f4 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -5137,8 +5137,8 @@ int goya_cpucp_info_get(struct hl_device *hdev) return 0; } -static bool goya_is_device_idle(struct hl_device *hdev, u64 *mask_arr, - u8 mask_len, struct seq_file *s) +static bool goya_is_device_idle(struct hl_device *hdev, u64 *mask_arr, u8 mask_len, + struct engines_data *e) { const char *fmt = "%-5d%-9s%#-14x%#-16x%#x\n"; const char *dma_fmt = "%-5d%-9s%#-14x%#x\n"; @@ -5149,9 +5149,9 @@ static bool goya_is_device_idle(struct hl_device *hdev, u64 *mask_arr, u64 offset; int i; - if (s) - seq_puts(s, "\nDMA is_idle QM_GLBL_STS0 DMA_CORE_STS0\n" - "--- ------- ------------ -------------\n"); + if (e) + hl_engine_data_sprintf(e, "\nDMA is_idle QM_GLBL_STS0 DMA_CORE_STS0\n" + "--- ------- ------------ -------------\n"); offset = mmDMA_QM_1_GLBL_STS0 - mmDMA_QM_0_GLBL_STS0; @@ -5164,13 +5164,13 @@ static bool goya_is_device_idle(struct hl_device *hdev, u64 *mask_arr, if (mask && !is_eng_idle) set_bit(GOYA_ENGINE_ID_DMA_0 + i, mask); - if (s) - seq_printf(s, dma_fmt, i, is_eng_idle ? "Y" : "N", + if (e) + hl_engine_data_sprintf(e, dma_fmt, i, is_eng_idle ? "Y" : "N", qm_glbl_sts0, dma_core_sts0); } - if (s) - seq_puts(s, + if (e) + hl_engine_data_sprintf(e, "\nTPC is_idle QM_GLBL_STS0 CMDQ_GLBL_STS0 CFG_STATUS\n" "--- ------- ------------ -------------- ----------\n"); @@ -5187,13 +5187,13 @@ static bool goya_is_device_idle(struct hl_device *hdev, u64 *mask_arr, if (mask && !is_eng_idle) set_bit(GOYA_ENGINE_ID_TPC_0 + i, mask); - if (s) - seq_printf(s, fmt, i, is_eng_idle ? "Y" : "N", + if (e) + hl_engine_data_sprintf(e, fmt, i, is_eng_idle ? "Y" : "N", qm_glbl_sts0, cmdq_glbl_sts0, tpc_cfg_sts); } - if (s) - seq_puts(s, + if (e) + hl_engine_data_sprintf(e, "\nMME is_idle QM_GLBL_STS0 CMDQ_GLBL_STS0 ARCH_STATUS\n" "--- ------- ------------ -------------- -----------\n"); @@ -5207,10 +5207,10 @@ static bool goya_is_device_idle(struct hl_device *hdev, u64 *mask_arr, if (mask && !is_eng_idle) set_bit(GOYA_ENGINE_ID_MME_0, mask); - if (s) { - seq_printf(s, fmt, 0, is_eng_idle ? "Y" : "N", qm_glbl_sts0, + if (e) { + hl_engine_data_sprintf(e, fmt, 0, is_eng_idle ? "Y" : "N", qm_glbl_sts0, cmdq_glbl_sts0, mme_arch_sts); - seq_puts(s, "\n"); + hl_engine_data_sprintf(e, "\n"); } return is_idle; -- cgit v1.2.3 From 2d4c09e3f93a8496bcb03270a104640ef1ecd39c Mon Sep 17 00:00:00 2001 From: Yang Li Date: Thu, 14 Jul 2022 09:29:08 +0800 Subject: habanalabs: Simplify bool conversion Fix the following coccicheck warning: ./drivers/misc/habanalabs/gaudi2/gaudi2.c:9727:48-53: WARNING: conversion to bool not needed here Signed-off-by: Yang Li Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi2/gaudi2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index 1140cf7db4a3..fd917e837075 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -9727,7 +9727,7 @@ static int gaudi2_get_mmu_base(struct hl_device *hdev, u64 mmu_id, u32 *mmu_base static void gaudi2_ack_mmu_error(struct hl_device *hdev, u64 mmu_id) { - bool is_pmmu = (mmu_id == HW_CAP_PMMU ? true : false); + bool is_pmmu = (mmu_id == HW_CAP_PMMU); struct gaudi2_device *gaudi2 = hdev->asic_specific; u32 mmu_base; -- cgit v1.2.3 From e4507995da974e2758621982941f9ff2ea18134b Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 14 Jul 2022 11:31:19 +0100 Subject: habanalabs: Fix spelling mistake "Scrubing" -> "Scrubbing" There is a spelling mistake in a dev_dbg message. Fix it. Signed-off-by: Colin Ian King Reviewed-by: Oded Gabbay 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 a3eca13c3fa9..a7923960fce1 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -4723,7 +4723,7 @@ static int gaudi_scrub_device_mem(struct hl_device *hdev) addr = prop->sram_user_base_address; size = hdev->pldm ? 0x10000 : prop->sram_size - SRAM_USER_BASE_OFFSET; - dev_dbg(hdev->dev, "Scrubing SRAM: 0x%09llx - 0x%09llx val: 0x%llx\n", + dev_dbg(hdev->dev, "Scrubbing SRAM: 0x%09llx - 0x%09llx val: 0x%llx\n", addr, addr + size, val); rc = gaudi_memset_device_memory(hdev, addr, size, val); if (rc) { -- cgit v1.2.3 From bc9b271e6c92b5f3bfe25f73b11e8e878f386075 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Wed, 13 Jul 2022 15:08:09 +0300 Subject: habanalabs: rename non_hard_reset to compute_reset In order to be more explicit we should use the term compute_reset for describing the reset in which only the compute engines gets reset. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/device.c | 2 +- drivers/misc/habanalabs/common/habanalabs.h | 4 ++-- drivers/misc/habanalabs/gaudi/gaudi.c | 4 ++-- drivers/misc/habanalabs/gaudi2/gaudi2.c | 4 ++-- drivers/misc/habanalabs/goya/goya.c | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index b30aeb1c657f..90e346727a7c 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -1556,7 +1556,7 @@ kill_processes: if (!hdev->asic_prop.fw_security_enabled) hl_fw_set_max_power(hdev); } else { - rc = hdev->asic_funcs->non_hard_reset_late_init(hdev); + rc = hdev->asic_funcs->compute_reset_late_init(hdev); if (rc) { if (reset_upon_device_release) dev_err(hdev->dev, diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 440e154dbe31..6d7b66cd50f1 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -1446,7 +1446,7 @@ struct engines_data { * @send_heartbeat: send is-alive packet to CPU-CP and verify response. * @debug_coresight: perform certain actions on Coresight for debugging. * @is_device_idle: return true if device is idle, false otherwise. - * @non_hard_reset_late_init: perform certain actions needed after a reset which is not hard-reset + * @compute_reset_late_init: perform certain actions needed after a compute reset * @hw_queues_lock: acquire H/W queues lock. * @hw_queues_unlock: release H/W queues lock. * @kdma_lock: acquire H/W queues lock. Relevant from GRECO ASIC @@ -1584,7 +1584,7 @@ struct hl_asic_funcs { int (*debug_coresight)(struct hl_device *hdev, struct hl_ctx *ctx, void *data); bool (*is_device_idle)(struct hl_device *hdev, u64 *mask_arr, u8 mask_len, struct engines_data *e); - int (*non_hard_reset_late_init)(struct hl_device *hdev); + int (*compute_reset_late_init)(struct hl_device *hdev); void (*hw_queues_lock)(struct hl_device *hdev); void (*hw_queues_unlock)(struct hl_device *hdev); void (*kdma_lock)(struct hl_device *hdev, int dcore_id); diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index a7923960fce1..20f62730be02 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -7427,7 +7427,7 @@ static void gaudi_print_nic_axi_irq_info(struct hl_device *hdev, u16 event_type, event_type, desc); } -static int gaudi_non_hard_reset_late_init(struct hl_device *hdev) +static int gaudi_compute_reset_late_init(struct hl_device *hdev) { /* GAUDI doesn't support any reset except hard-reset */ return -EPERM; @@ -9193,7 +9193,7 @@ static const struct hl_asic_funcs gaudi_funcs = { .send_heartbeat = gaudi_send_heartbeat, .debug_coresight = gaudi_debug_coresight, .is_device_idle = gaudi_is_device_idle, - .non_hard_reset_late_init = gaudi_non_hard_reset_late_init, + .compute_reset_late_init = gaudi_compute_reset_late_init, .hw_queues_lock = gaudi_hw_queues_lock, .hw_queues_unlock = gaudi_hw_queues_unlock, .kdma_lock = NULL, diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index fd917e837075..ab6ad06cec03 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -6129,7 +6129,7 @@ done: return ret_val; } -static int gaudi2_non_hard_reset_late_init(struct hl_device *hdev) +static int gaudi2_compute_reset_late_init(struct hl_device *hdev) { struct gaudi2_device *gaudi2 = hdev->asic_specific; size_t irq_arr_size; @@ -9930,7 +9930,7 @@ static const struct hl_asic_funcs gaudi2_funcs = { .send_heartbeat = gaudi2_send_heartbeat, .debug_coresight = gaudi2_debug_coresight, .is_device_idle = gaudi2_is_device_idle, - .non_hard_reset_late_init = gaudi2_non_hard_reset_late_init, + .compute_reset_late_init = gaudi2_compute_reset_late_init, .hw_queues_lock = gaudi2_hw_queues_lock, .hw_queues_unlock = gaudi2_hw_queues_unlock, .kdma_lock = gaudi2_kdma_lock, diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 7b9f7f8b51f4..d4459c290ea8 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -4559,7 +4559,7 @@ static int goya_unmask_irq_arr(struct hl_device *hdev, u32 *irq_arr, return rc; } -static int goya_non_hard_reset_late_init(struct hl_device *hdev) +static int goya_compute_reset_late_init(struct hl_device *hdev) { /* * Unmask all IRQs since some could have been received @@ -5478,7 +5478,7 @@ static const struct hl_asic_funcs goya_funcs = { .send_heartbeat = goya_send_heartbeat, .debug_coresight = goya_debug_coresight, .is_device_idle = goya_is_device_idle, - .non_hard_reset_late_init = goya_non_hard_reset_late_init, + .compute_reset_late_init = goya_compute_reset_late_init, .hw_queues_lock = goya_hw_queues_lock, .hw_queues_unlock = goya_hw_queues_unlock, .kdma_lock = NULL, -- cgit v1.2.3 From 28742772a0bb798f67a774e91172e06a18cd9855 Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Tue, 12 Jul 2022 18:19:11 +0300 Subject: habanalabs/gaudi2: enable all MMU SPI/SEI interrupts Currently only part of the MMU SPI/SEI interrupts are enabled, although there is no real reason to not enable all. The only exception is "burst_fifo_full" which is expected for PMMU because it has a 2 entries FIFO, and thus is it not enabled for it. Signed-off-by: Tomer Tayar Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi2/gaudi2.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index ab6ad06cec03..465d9c319c3c 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -117,6 +117,12 @@ #define MMU_RANGE_INV_ASID_EN_SHIFT 1 #define MMU_RANGE_INV_ASID_SHIFT 2 +/* The last SPI_SEI cause bit, "burst_fifo_full", is expected to be triggered in PMMU because it has + * a 2 entries FIFO, and hence it is not enabled for it. + */ +#define GAUDI2_PMMU_SPI_SEI_ENABLE_MASK GENMASK(GAUDI2_NUM_OF_MMU_SPI_SEI_CAUSE - 2, 0) +#define GAUDI2_HMMU_SPI_SEI_ENABLE_MASK GENMASK(GAUDI2_NUM_OF_MMU_SPI_SEI_CAUSE - 1, 0) + #define GAUDI2_MAX_STRING_LEN 64 #define GAUDI2_VDEC_MSIX_ENTRIES (GAUDI2_IRQ_NUM_SHARED_DEC1_ABNRM - \ @@ -4956,8 +4962,7 @@ static int gaudi2_mmu_update_hop0_addr(struct hl_device *hdev, u32 stlb_base) return 0; } -static int gaudi2_mmu_init_common(struct hl_device *hdev, u32 mmu_base, - u32 stlb_base) +static int gaudi2_mmu_init_common(struct hl_device *hdev, u32 mmu_base, u32 stlb_base) { u32 status, timeout_usec; int rc; @@ -4985,7 +4990,6 @@ static int gaudi2_mmu_init_common(struct hl_device *hdev, u32 mmu_base, return rc; WREG32(mmu_base + MMU_BYPASS_OFFSET, 0); - WREG32(mmu_base + MMU_SPI_SEI_MASK_OFFSET, 0xF); rc = hl_poll_timeout( hdev, @@ -5042,6 +5046,8 @@ static int gaudi2_pci_mmu_init(struct hl_device *hdev) DCORE0_HMMU0_MMU_STATIC_MULTI_PAGE_SIZE_CFG_8_BITS_HOP_MODE_EN_MASK); } + WREG32(mmu_base + MMU_SPI_SEI_MASK_OFFSET, GAUDI2_PMMU_SPI_SEI_ENABLE_MASK); + rc = gaudi2_mmu_init_common(hdev, mmu_base, stlb_base); if (rc) return rc; @@ -5092,6 +5098,8 @@ static int gaudi2_dcore_hmmu_init(struct hl_device *hdev, int dcore_id, RMWREG32(stlb_base + STLB_HOP_CONFIGURATION_OFFSET, 1, STLB_HOP_CONFIGURATION_ONLY_LARGE_PAGE_MASK); + WREG32(mmu_base + MMU_SPI_SEI_MASK_OFFSET, GAUDI2_HMMU_SPI_SEI_ENABLE_MASK); + rc = gaudi2_mmu_init_common(hdev, mmu_base, stlb_base); if (rc) return rc; -- cgit v1.2.3 From 913bd4179b82adfeece29243711ccaf4330772b6 Mon Sep 17 00:00:00 2001 From: Ohad Sharabi Date: Wed, 13 Jul 2022 13:47:23 +0300 Subject: habanalabs: add return code field to module iterator Up until now the module iterator called void callback functions and so caller activating callback that may fail suffered from 2 issues: 1. The need to "plant" return called in the private data. This is a drawback since the iterator itself should not be aware of the private data of the caller. 2. Due to 1 even in a failure the iterator would keep iterating instead of break upon error. To overcome this an optional rc field added to the iterator context. Signed-off-by: Ohad Sharabi Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/habanalabs.h | 7 +++++-- drivers/misc/habanalabs/gaudi2/gaudi2.c | 26 ++++++++++++++++-------- drivers/misc/habanalabs/gaudi2/gaudi2_security.c | 19 ++++++++--------- 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 6d7b66cd50f1..8c2c94fb1322 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -208,6 +208,7 @@ enum hl_protection_levels { * struct iterate_module_ctx - HW module iterator * @fn: function to apply to each HW module instance * @data: optional internal data to the function iterator + * @rc: return code for optional use of iterator/iterator-caller */ struct iterate_module_ctx { /* @@ -217,10 +218,12 @@ struct iterate_module_ctx { * @inst: HW module instance within the block * @offset: current HW module instance offset from the 1-st HW module instance * in the 1-st block - * @data: function specific data + * @ctx: the iterator context. */ - void (*fn)(struct hl_device *hdev, int block, int inst, u32 offset, void *data); + void (*fn)(struct hl_device *hdev, int block, int inst, u32 offset, + struct iterate_module_ctx *ctx); void *data; + int rc; }; struct hl_block_glbl_sec { diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index 465d9c319c3c..3531a339e742 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -1712,6 +1712,9 @@ void gaudi2_iterate_tpcs(struct hl_device *hdev, struct iterate_module_ctx *ctx) int dcore, inst, tpc_seq; u32 offset; + /* init the return code */ + ctx->rc = 0; + for (dcore = 0; dcore < NUM_OF_DCORES; dcore++) { for (inst = 0; inst < NUM_OF_TPC_PER_DCORE; inst++) { tpc_seq = dcore * NUM_OF_TPC_PER_DCORE + inst; @@ -1721,7 +1724,12 @@ void gaudi2_iterate_tpcs(struct hl_device *hdev, struct iterate_module_ctx *ctx) offset = (DCORE_OFFSET * dcore) + (DCORE_TPC_OFFSET * inst); - ctx->fn(hdev, dcore, inst, offset, ctx->data); + ctx->fn(hdev, dcore, inst, offset, ctx); + if (ctx->rc) { + dev_err(hdev->dev, "TPC iterator failed for DCORE%d TPC%d\n", + dcore, inst); + return; + } } } @@ -1730,7 +1738,9 @@ void gaudi2_iterate_tpcs(struct hl_device *hdev, struct iterate_module_ctx *ctx) /* special check for PCI TPC (DCORE0_TPC6) */ offset = DCORE_TPC_OFFSET * (NUM_DCORE0_TPC - 1); - ctx->fn(hdev, 0, NUM_DCORE0_TPC - 1, offset, ctx->data); + ctx->fn(hdev, 0, NUM_DCORE0_TPC - 1, offset, ctx); + if (ctx->rc) + dev_err(hdev->dev, "TPC iterator failed for DCORE0 TPC6\n"); } static bool gaudi2_host_phys_addr_valid(u64 addr) @@ -4507,10 +4517,10 @@ struct gaudi2_tpc_init_cfg_data { }; static void gaudi2_init_tpc_config(struct hl_device *hdev, int dcore, int inst, - u32 offset, void *data) + u32 offset, struct iterate_module_ctx *ctx) { struct gaudi2_device *gaudi2 = hdev->asic_specific; - struct gaudi2_tpc_init_cfg_data *cfg_data = data; + struct gaudi2_tpc_init_cfg_data *cfg_data = ctx->data; u32 queue_id_base; u8 seq; @@ -6155,9 +6165,9 @@ static int gaudi2_compute_reset_late_init(struct hl_device *hdev) } static void gaudi2_is_tpc_engine_idle(struct hl_device *hdev, int dcore, int inst, u32 offset, - void *data) + struct iterate_module_ctx *ctx) { - struct gaudi2_tpc_idle_data *idle_data = (struct gaudi2_tpc_idle_data *)data; + struct gaudi2_tpc_idle_data *idle_data = ctx->data; u32 tpc_cfg_sts, qm_glbl_sts0, qm_glbl_sts1, qm_cgm_sts; bool is_eng_idle; int engine_idx; @@ -6736,9 +6746,9 @@ static int gaudi2_mmu_shared_prepare(struct hl_device *hdev, u32 asid) } static void gaudi2_tpc_mmu_prepare(struct hl_device *hdev, int dcore, int inst, u32 offset, - void *data) + struct iterate_module_ctx *ctx) { - struct gaudi2_tpc_mmu_data *mmu_data = (struct gaudi2_tpc_mmu_data *)data; + struct gaudi2_tpc_mmu_data *mmu_data = ctx->data; WREG32(mmDCORE0_TPC0_CFG_AXUSER_HB_MMU_BP + offset, 0); WREG32(mmDCORE0_TPC0_CFG_AXUSER_HB_ASID + offset, mmu_data->rw_asid); diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2_security.c b/drivers/misc/habanalabs/gaudi2/gaudi2_security.c index 89a06ff5ba34..c4165db06db2 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2_security.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2_security.c @@ -2583,9 +2583,9 @@ struct gaudi2_tpc_pb_data { }; static void gaudi2_config_tpcs_glbl_sec(struct hl_device *hdev, int dcore, int inst, u32 offset, - void *data) + struct iterate_module_ctx *ctx) { - struct gaudi2_tpc_pb_data *pb_data = (struct gaudi2_tpc_pb_data *)data; + struct gaudi2_tpc_pb_data *pb_data = ctx->data; hl_config_glbl_sec(hdev, gaudi2_pb_dcr0_tpc0, pb_data->glbl_sec, offset, pb_data->block_array_size); @@ -2660,15 +2660,14 @@ static int gaudi2_init_pb_tpc(struct hl_device *hdev) struct gaudi2_tpc_arc_pb_data { u32 unsecured_regs_arr_size; u32 arc_regs_arr_size; - int rc; }; static void gaudi2_config_tpcs_pb_ranges(struct hl_device *hdev, int dcore, int inst, u32 offset, - void *data) + struct iterate_module_ctx *ctx) { - struct gaudi2_tpc_arc_pb_data *pb_data = (struct gaudi2_tpc_arc_pb_data *)data; + struct gaudi2_tpc_arc_pb_data *pb_data = ctx->data; - pb_data->rc |= hl_init_pb_ranges(hdev, HL_PB_SHARED, HL_PB_NA, 1, + ctx->rc = hl_init_pb_ranges(hdev, HL_PB_SHARED, HL_PB_NA, 1, offset, gaudi2_pb_dcr0_tpc0_arc, pb_data->arc_regs_arr_size, gaudi2_pb_dcr0_tpc0_arc_unsecured_regs, @@ -2683,12 +2682,12 @@ static int gaudi2_init_pb_tpc_arc(struct hl_device *hdev) tpc_arc_pb_data.arc_regs_arr_size = ARRAY_SIZE(gaudi2_pb_dcr0_tpc0_arc); tpc_arc_pb_data.unsecured_regs_arr_size = ARRAY_SIZE(gaudi2_pb_dcr0_tpc0_arc_unsecured_regs); - tpc_arc_pb_data.rc = 0; + tpc_iter.fn = &gaudi2_config_tpcs_pb_ranges; tpc_iter.data = &tpc_arc_pb_data; gaudi2_iterate_tpcs(hdev, &tpc_iter); - return tpc_arc_pb_data.rc; + return tpc_iter.rc; } static int gaudi2_init_pb_sm_objs(struct hl_device *hdev) @@ -3547,9 +3546,9 @@ struct gaudi2_ack_pb_tpc_data { }; static void gaudi2_ack_pb_tpc_config(struct hl_device *hdev, int dcore, int inst, u32 offset, - void *data) + struct iterate_module_ctx *ctx) { - struct gaudi2_ack_pb_tpc_data *pb_data = (struct gaudi2_ack_pb_tpc_data *)data; + struct gaudi2_ack_pb_tpc_data *pb_data = ctx->data; hl_ack_pb_single_dcore(hdev, offset, HL_PB_SINGLE_INSTANCE, HL_PB_NA, gaudi2_pb_dcr0_tpc0, pb_data->tpc_regs_array_size); -- cgit v1.2.3 From cd6b0cea89862a5b3411246a2410881a988d5b0f Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Wed, 6 Apr 2022 12:07:19 +0300 Subject: habanalabs/gaudi: increase default cs timeout to 10 minutes In order to improve scalability and reduce host overhead, it is better to increase the default TDR timeout of Gaudi1 from 30 seconds to 10 minutes. This will allow the DL Framework (e.g. PyTorch, TensorFlow) to remove the host sync they are using now and improve overall performance on scaleout training. Note that one can always set the timeout to a custom value via a kernel module parameter given during driver load. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/habanalabs_drv.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/misc/habanalabs/common/habanalabs_drv.c b/drivers/misc/habanalabs/common/habanalabs_drv.c index f733ead605e7..d59d8cdf33e6 100644 --- a/drivers/misc/habanalabs/common/habanalabs_drv.c +++ b/drivers/misc/habanalabs/common/habanalabs_drv.c @@ -27,7 +27,10 @@ static struct class *hl_class; static DEFINE_IDR(hl_devs_idr); static DEFINE_MUTEX(hl_devs_idr_lock); -static int timeout_locked = 30; +#define HL_DEFAULT_TIMEOUT_LOCKED 30 /* 30 seconds */ +#define GAUDI_DEFAULT_TIMEOUT_LOCKED 600 /* 10 minutes */ + +static int timeout_locked = HL_DEFAULT_TIMEOUT_LOCKED; static int reset_on_lockup = 1; static int memory_scrub; static ulong boot_error_status_mask = ULONG_MAX; @@ -314,12 +317,22 @@ static void copy_kernel_module_params_to_device(struct hl_device *hdev) hdev->boot_error_status_mask = boot_error_status_mask; } -static void fixup_device_params_per_asic(struct hl_device *hdev) +static void fixup_device_params_per_asic(struct hl_device *hdev, int timeout) { switch (hdev->asic_type) { - case ASIC_GOYA: case ASIC_GAUDI: case ASIC_GAUDI_SEC: + /* If user didn't request a different timeout than the default one, we have + * a different default timeout for Gaudi + */ + if (timeout == HL_DEFAULT_TIMEOUT_LOCKED) + hdev->timeout_jiffies = msecs_to_jiffies(GAUDI_DEFAULT_TIMEOUT_LOCKED * + MSEC_PER_SEC); + + hdev->reset_upon_device_release = 0; + break; + + case ASIC_GOYA: hdev->reset_upon_device_release = 0; break; @@ -339,7 +352,7 @@ static int fixup_device_params(struct hl_device *hdev) hdev->fw_comms_poll_interval_usec = HL_FW_STATUS_POLL_INTERVAL_USEC; if (tmp_timeout) - hdev->timeout_jiffies = msecs_to_jiffies(tmp_timeout * 1000); + hdev->timeout_jiffies = msecs_to_jiffies(tmp_timeout * MSEC_PER_SEC); else hdev->timeout_jiffies = MAX_SCHEDULE_TIMEOUT; @@ -360,7 +373,7 @@ static int fixup_device_params(struct hl_device *hdev) if (!hdev->cpu_queues_enable) hdev->heartbeat = 0; - fixup_device_params_per_asic(hdev); + fixup_device_params_per_asic(hdev, tmp_timeout); return 0; } -- cgit v1.2.3 From ae937492ecc7e561ace4f01c6c0c14e868744d58 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Mon, 18 Jul 2022 21:02:34 +0300 Subject: habanalabs/gaudi2: remove old interrupt mappings Interrupt enumration has changed some time ago but the old mapping was accidentally left in the driver. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi2/gaudi2P.h | 3 -- .../include/gaudi2/gaudi2_async_virt_events.h | 57 ---------------------- 2 files changed, 60 deletions(-) delete mode 100644 drivers/misc/habanalabs/include/gaudi2/gaudi2_async_virt_events.h diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2P.h b/drivers/misc/habanalabs/gaudi2/gaudi2P.h index e4bc4009f05b..5110574a650e 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2P.h +++ b/drivers/misc/habanalabs/gaudi2/gaudi2P.h @@ -15,7 +15,6 @@ #include "../include/gaudi2/gaudi2_packets.h" #include "../include/gaudi2/gaudi2_fw_if.h" #include "../include/gaudi2/gaudi2_async_events.h" -#include "../include/gaudi2/gaudi2_async_virt_events.h" #define GAUDI2_LINUX_FW_FILE "habanalabs/gaudi2/gaudi2-fit.itb" #define GAUDI2_BOOT_FIT_FILE "habanalabs/gaudi2/gaudi2-boot-fit.itb" @@ -511,8 +510,6 @@ struct dup_block_ctx { * @hbm_cfg: HBM subsystem settings * @hw_queues_lock_mutex: used by simulator instead of hw_queues_lock. * @kdma_lock_mutex: used by simulator instead of kdma_lock. - * @use_deprecated_event_mappings: use old event mappings which are about to be - * deprecated */ struct gaudi2_device { int (*cpucp_info_get)(struct hl_device *hdev); diff --git a/drivers/misc/habanalabs/include/gaudi2/gaudi2_async_virt_events.h b/drivers/misc/habanalabs/include/gaudi2/gaudi2_async_virt_events.h deleted file mode 100644 index 6d6ed7838a64..000000000000 --- a/drivers/misc/habanalabs/include/gaudi2/gaudi2_async_virt_events.h +++ /dev/null @@ -1,57 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 - * - * Copyright 2022 HabanaLabs, Ltd. - * All Rights Reserved. - * - */ - -#ifndef __GAUDI2_ASYNC_VIRT_EVENTS_H_ -#define __GAUDI2_ASYNC_VIRT_EVENTS_H_ - -enum gaudi2_async_virt_event_id { - GAUDI2_EVENT_NIC3_QM1_OLD = 1206, - GAUDI2_EVENT_NIC4_QM0_OLD = 1207, - GAUDI2_EVENT_NIC4_QM1_OLD = 1208, - GAUDI2_EVENT_NIC5_QM0_OLD = 1209, - GAUDI2_EVENT_NIC5_QM1_OLD = 1210, - GAUDI2_EVENT_NIC6_QM0_OLD = 1211, - GAUDI2_EVENT_NIC6_QM1_OLD = 1212, - GAUDI2_EVENT_NIC7_QM0_OLD = 1213, - GAUDI2_EVENT_NIC7_QM1_OLD = 1214, - GAUDI2_EVENT_NIC8_QM0_OLD = 1215, - GAUDI2_EVENT_NIC8_QM1_OLD = 1216, - GAUDI2_EVENT_NIC9_QM0_OLD = 1217, - GAUDI2_EVENT_NIC9_QM1_OLD = 1218, - GAUDI2_EVENT_NIC10_QM0_OLD = 1219, - GAUDI2_EVENT_NIC10_QM1_OLD = 1220, - GAUDI2_EVENT_NIC11_QM0_OLD = 1221, - GAUDI2_EVENT_NIC11_QM1_OLD = 1222, - GAUDI2_EVENT_CPU_PKT_SANITY_FAILED_OLD = 1223, - GAUDI2_EVENT_CPU0_STATUS_NIC0_ENG0_OLD = 1224, - GAUDI2_EVENT_CPU0_STATUS_NIC0_ENG1_OLD = 1225, - GAUDI2_EVENT_CPU1_STATUS_NIC1_ENG0_OLD = 1226, - GAUDI2_EVENT_CPU1_STATUS_NIC1_ENG1_OLD = 1227, - GAUDI2_EVENT_CPU2_STATUS_NIC2_ENG0_OLD = 1228, - GAUDI2_EVENT_CPU2_STATUS_NIC2_ENG1_OLD = 1229, - GAUDI2_EVENT_CPU3_STATUS_NIC3_ENG0_OLD = 1230, - GAUDI2_EVENT_CPU3_STATUS_NIC3_ENG1_OLD = 1231, - GAUDI2_EVENT_CPU4_STATUS_NIC4_ENG0_OLD = 1232, - GAUDI2_EVENT_CPU4_STATUS_NIC4_ENG1_OLD = 1233, - GAUDI2_EVENT_CPU5_STATUS_NIC5_ENG0_OLD = 1234, - GAUDI2_EVENT_CPU5_STATUS_NIC5_ENG1_OLD = 1235, - GAUDI2_EVENT_CPU6_STATUS_NIC6_ENG0_OLD = 1236, - GAUDI2_EVENT_CPU6_STATUS_NIC6_ENG1_OLD = 1237, - GAUDI2_EVENT_CPU7_STATUS_NIC7_ENG0_OLD = 1238, - GAUDI2_EVENT_CPU7_STATUS_NIC7_ENG1_OLD = 1239, - GAUDI2_EVENT_CPU8_STATUS_NIC8_ENG0_OLD = 1240, - GAUDI2_EVENT_CPU8_STATUS_NIC8_ENG1_OLD = 1241, - GAUDI2_EVENT_CPU9_STATUS_NIC9_ENG0_OLD = 1242, - GAUDI2_EVENT_CPU9_STATUS_NIC9_ENG1_OLD = 1243, - GAUDI2_EVENT_CPU10_STATUS_NIC10_ENG0_OLD = 1244, - GAUDI2_EVENT_CPU10_STATUS_NIC10_ENG1_OLD = 1245, - GAUDI2_EVENT_CPU11_STATUS_NIC11_ENG0_OLD = 1246, - GAUDI2_EVENT_CPU11_STATUS_NIC11_ENG1_OLD = 1247, - GAUDI2_EVENT_ARC_DCCM_FULL_OLD = 1248, -}; - -#endif /* __GAUDI2_ASYNC_VIRT_EVENTS_H_ */ -- cgit v1.2.3 From f25a72b8b9f4885ea30263a02d1083e283c9e718 Mon Sep 17 00:00:00 2001 From: Bharat Jauhari Date: Wed, 1 Jun 2022 15:18:47 +0300 Subject: habanalabs: fix spelling mistakes Cosmetic commit, no logical changes. It just fixes the spelling mistakes. Signed-off-by: Bharat Jauhari Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/firmware_if.c | 8 ++++---- drivers/misc/habanalabs/common/habanalabs.h | 27 +++++++++++++-------------- drivers/misc/habanalabs/common/memory_mgr.c | 10 +++++----- include/uapi/misc/habanalabs.h | 10 +++++----- 4 files changed, 27 insertions(+), 28 deletions(-) diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index 608ca67527a5..58c1eff16df6 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -1514,7 +1514,7 @@ int hl_fw_read_preboot_status(struct hl_device *hdev) hdev->asic_funcs->init_firmware_preload_params(hdev); /* - * In order to determine boot method (static VS dymanic) we need to + * In order to determine boot method (static VS dynamic) we need to * read the boot caps register */ rc = hl_fw_read_preboot_caps(hdev); @@ -1781,7 +1781,7 @@ int hl_fw_dynamic_send_protocol_cmd(struct hl_device *hdev, * * @return the CRC32 result * - * NOTE: kernel's CRC32 differ's from standard CRC32 calculation. + * NOTE: kernel's CRC32 differs from standard CRC32 calculation. * in order to be aligned we need to flip the bits of both the input * initial CRC and kernel's CRC32 result. * in addition both sides use initial CRC of 0, @@ -1798,7 +1798,7 @@ static u32 hl_fw_compat_crc32(u8 *data, size_t size) * * @hdev: pointer to the habanalabs device structure * @addr: device address of memory transfer - * @size: memory transter size + * @size: memory transfer size * @region: PCI memory region * * @return 0 on success, otherwise non-zero error code @@ -2547,7 +2547,7 @@ static int hl_fw_dynamic_init_cpu(struct hl_device *hdev, /* * when testing FW load (without Linux) on PLDM we don't want to * wait until boot fit is active as it may take several hours. - * instead, we load the bootfit and let it do all initializations in + * instead, we load the bootfit and let it do all initialization in * the background. */ if (hdev->pldm && !(hdev->fw_components & FW_TYPE_LINUX)) diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 8c2c94fb1322..350bc5b9f174 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -94,7 +94,7 @@ struct hl_fpriv; #define MMU_HASH_TABLE_BITS 7 /* 1 << 7 buckets */ /** - * enum hl_mmu_page_table_locaion - mmu page table location + * enum hl_mmu_page_table_location - mmu page table location * @MMU_DR_PGT: page-table is located on device DRAM. * @MMU_HR_PGT: page-table is located on host memory. * @MMU_NUM_PGT_LOCATIONS: number of page-table locations currently supported. @@ -800,7 +800,7 @@ struct hl_fence { * @lock: spinlock to protect fence. * @hdev: habanalabs device structure. * @hw_sob: the H/W SOB used in this signal/wait CS. - * @encaps_sig_hdl: encaps signals hanlder. + * @encaps_sig_hdl: encaps signals handler. * @cs_seq: command submission sequence number. * @type: type of the CS - signal/wait. * @sob_val: the SOB value that is used in this signal/wait CS. @@ -908,7 +908,7 @@ struct hl_mmap_mem_buf { * @size: holds the CB's size. * @cs_cnt: holds number of CS that this CB participates in. * @is_pool: true if CB was acquired from the pool, false otherwise. - * @is_internal: internaly allocated + * @is_internal: internally allocated * @is_mmu_mapped: true if the CB is mapped to the device's MMU. */ struct hl_cb { @@ -1116,7 +1116,7 @@ struct timestamp_reg_info { * @fence: hl fence object for interrupt completion * @cq_target_value: CQ target value * @cq_kernel_addr: CQ kernel address, to be used in the cq interrupt - * handler for taget value comparison + * handler for target value comparison */ struct hl_user_pending_interrupt { struct timestamp_reg_info ts_reg_info; @@ -1742,10 +1742,10 @@ struct hl_cs_outcome { /** * struct hl_cs_outcome_store - represents a limited store of completed CS outcomes - * @outcome_map: index of completed CS searcheable by sequence number + * @outcome_map: index of completed CS searchable by sequence number * @used_list: list of outcome objects currently in use * @free_list: list of outcome objects currently not in use - * @nodes_pool: a static pool of preallocated outcome objects + * @nodes_pool: a static pool of pre-allocated outcome objects * @db_lock: any operation on the store must take this lock */ struct hl_cs_outcome_store { @@ -1769,7 +1769,7 @@ struct hl_cs_outcome_store { * @refcount: reference counter for the context. Context is released only when * this hits 0l. It is incremented on CS and CS_WAIT. * @cs_pending: array of hl fence objects representing pending CS. - * @outcome_store: storage data structure used to remember ouitcomes of completed + * @outcome_store: storage data structure used to remember outcomes of completed * command submissions for a long time after CS id wraparound. * @va_range: holds available virtual addresses for host and dram mappings. * @mem_hash_lock: protects the mem_hash. @@ -1838,7 +1838,6 @@ struct hl_ctx_mgr { }; - /* * COMMAND SUBMISSIONS */ @@ -1904,7 +1903,7 @@ struct hl_userptr { * @tdr_active: true if TDR was activated for this CS (to prevent * double TDR activation). * @aborted: true if CS was aborted due to some device error. - * @timestamp: true if a timestmap must be captured upon completion. + * @timestamp: true if a timestamp must be captured upon completion. * @staged_last: true if this is the last staged CS and needs completion. * @staged_first: true if this is the first staged CS and we need to receive * timeout for this CS. @@ -2229,7 +2228,7 @@ struct hl_info_list { /** * struct hl_debugfs_entry - debugfs dentry wrapper. - * @info_ent: dentry realted ops. + * @info_ent: dentry related ops. * @dev_entry: ASIC specific debugfs manager. */ struct hl_debugfs_entry { @@ -2934,7 +2933,7 @@ struct razwi_info { * struct undefined_opcode_info - info about last undefined opcode error * @timestamp: timestamp of the undefined opcode error * @cb_addr_streams: CB addresses (per stream) that are currently exists in the PQ - * entiers. In case all streams array entries are + * entries. In case all streams array entries are * filled with values, it means the execution was in Lower-CP. * @cq_addr: the address of the current handled command buffer * @cq_size: the size of the current handled command buffer @@ -2975,7 +2974,7 @@ struct last_error_session_info { /** * struct hl_reset_info - holds current device reset information. * @lock: lock to protect critical reset flows. - * @compute_reset_cnt: number of compte resets since the driver was loaded. + * @compute_reset_cnt: number of compute resets since the driver was loaded. * @hard_reset_cnt: number of hard resets since the driver was loaded. * @hard_reset_schedule_flags: hard reset is scheduled to after current compute reset, * here we hold the hard reset flags. @@ -2986,7 +2985,7 @@ struct last_error_session_info { * @hard_reset_pending: is there a hard reset work pending. * @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 + * @prev_reset_trigger: saves the previous trigger which caused a reset, overridden * with a new value on next reset * @reset_trigger_repeated: set if device reset is triggered more than once with * same cause. @@ -3064,7 +3063,7 @@ struct hl_reset_info { * @hl_chip_info: ASIC's sensors information. * @device_status_description: device status description. * @hl_debugfs: device's debugfs manager. - * @cb_pool: list of preallocated CBs. + * @cb_pool: list of pre allocated CBs. * @cb_pool_lock: protects the CB pool. * @internal_cb_pool_virt_addr: internal command buffer pool virtual address. * @internal_cb_pool_dma_addr: internal command buffer pool dma address. diff --git a/drivers/misc/habanalabs/common/memory_mgr.c b/drivers/misc/habanalabs/common/memory_mgr.c index 56df962d2f3c..1936d653699e 100644 --- a/drivers/misc/habanalabs/common/memory_mgr.c +++ b/drivers/misc/habanalabs/common/memory_mgr.c @@ -11,7 +11,7 @@ * hl_mmap_mem_buf_get - increase the buffer refcount and return a pointer to * the buffer descriptor. * - * @mmg: parent unifed memory manager + * @mmg: parent unified memory manager * @handle: requested buffer handle * * Find the buffer in the store and return a pointer to its descriptor. @@ -104,7 +104,7 @@ int hl_mmap_mem_buf_put(struct hl_mmap_mem_buf *buf) * hl_mmap_mem_buf_put_handle - decrease the reference to the buffer with the * given handle. * - * @mmg: parent unifed memory manager + * @mmg: parent unified memory manager * @handle: requested buffer handle * * Decrease the reference to the buffer, and release it if it was the last one. @@ -137,7 +137,7 @@ int hl_mmap_mem_buf_put_handle(struct hl_mem_mgr *mmg, u64 handle) /** * hl_mmap_mem_buf_alloc - allocate a new mappable buffer * - * @mmg: parent unifed memory manager + * @mmg: parent unified memory manager * @behavior: behavior object describing this buffer polymorphic behavior * @gfp: gfp flags to use for the memory allocations * @args: additional args passed to behavior->alloc @@ -222,7 +222,7 @@ static const struct vm_operations_struct hl_mmap_mem_buf_vm_ops = { /** * hl_mem_mgr_mmap - map the given buffer to the user * - * @mmg: unifed memory manager + * @mmg: unified memory manager * @vma: the vma object for which mmap was closed. * @args: additional args passed to behavior->mmap * @@ -322,7 +322,7 @@ void hl_mem_mgr_init(struct device *dev, struct hl_mem_mgr *mmg) /** * hl_mem_mgr_fini - release unified memory manager * - * @mmg: parent unifed memory manager + * @mmg: parent unified memory manager * * Release the unified memory manager. Shall be called from an interrupt context. */ diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index 5d06d5c74dd1..be06b1307c44 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -876,13 +876,13 @@ struct hl_info_hw_idle { __u32 is_idle; /* * Bitmask of busy engines. - * Bits definition is according to `enum _enging_id'. + * Bits definition is according to `enum _engine_id'. */ __u32 busy_engines_mask; /* * Extended Bitmask of busy engines. - * Bits definition is according to `enum _enging_id'. + * Bits definition is according to `enum _engine_id'. */ __u64 busy_engines_mask_ext[HL_BUSY_ENGINES_MASK_EXT_SIZE]; }; @@ -1078,12 +1078,12 @@ struct hl_info_razwi_event { * struct hl_info_undefined_opcode_event - info about last undefined opcode error * @timestamp: timestamp of the undefined opcode error * @cb_addr_streams: CB addresses (per stream) that are currently exists in the PQ - * entiers. In case all streams array entries are + * entries. In case all streams array entries are * filled with values, it means the execution was in Lower-CP. * @cq_addr: the address of the current handled command buffer * @cq_size: the size of the current handled command buffer * @cb_addr_streams_len: num of streams - actual len of cb_addr_streams array. - * should be equal to 1 incase of undefined opcode + * should be equal to 1 in case of undefined opcode * in Upper-CP (specific stream) and equal to 4 incase * of undefined opcode in Lower-CP. * @engine_id: engine-id that the error occurred on @@ -1412,7 +1412,7 @@ struct hl_cs_out { /* Valid only when HL_CS_FLAGS_RESERVE_SIGNALS_ONLY is set */ struct { - /* This is the resereved signal handle id */ + /* This is the reserved signal handle id */ __u32 handle_id; /* This is the signals count */ -- cgit v1.2.3 From 0c819c9a04413facd3b167ec1e6f5928e909fcb2 Mon Sep 17 00:00:00 2001 From: Ohad Sharabi Date: Mon, 18 Jul 2022 22:02:13 +0300 Subject: habanalabs: wrap macro arg with parentheses The macro argument is cast-ed to u32 in some of the places. Because this arg can be some arithmetic computation (e.g. address + offset) the cast should be on the whole expression. Signed-off-by: Ohad Sharabi Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/habanalabs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 350bc5b9f174..d3efec600458 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -2506,7 +2506,7 @@ void hl_wreg(struct hl_device *hdev, u32 reg, u32 val); break; \ (val) = __elbi_read; \ } else {\ - (val) = RREG32((u32)addr); \ + (val) = RREG32((u32)(addr)); \ } \ if (cond) \ break; \ @@ -2517,7 +2517,7 @@ void hl_wreg(struct hl_device *hdev, u32 reg, u32 val); break; \ (val) = __elbi_read; \ } else {\ - (val) = RREG32((u32)addr); \ + (val) = RREG32((u32)(addr)); \ } \ break; \ } \ -- cgit v1.2.3 From 5f92c1e2961912e2a7a10d8e7b998b7cd9dd1d0e Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Tue, 19 Jul 2022 12:16:01 +0300 Subject: habanalabs: remove all kdma locks We don't use KDMA concurrently in the driver. The only use is through debugfs and we don't protect concurrent access through it. Reported-by: Dan Carpenter Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/habanalabs.h | 4 ---- drivers/misc/habanalabs/gaudi/gaudi.c | 2 -- drivers/misc/habanalabs/gaudi2/gaudi2.c | 23 ----------------------- drivers/misc/habanalabs/gaudi2/gaudi2P.h | 3 --- drivers/misc/habanalabs/goya/goya.c | 2 -- 5 files changed, 34 deletions(-) diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index d3efec600458..8d9e96c6092a 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -1452,8 +1452,6 @@ struct engines_data { * @compute_reset_late_init: perform certain actions needed after a compute reset * @hw_queues_lock: acquire H/W queues lock. * @hw_queues_unlock: release H/W queues lock. - * @kdma_lock: acquire H/W queues lock. Relevant from GRECO ASIC - * @kdma_unlock: release H/W queues lock. Relevant from GRECO ASIC * @get_pci_id: retrieve PCI ID. * @get_eeprom_data: retrieve EEPROM data from F/W. * @get_monitor_dump: retrieve monitor registers dump from F/W. @@ -1590,8 +1588,6 @@ struct hl_asic_funcs { int (*compute_reset_late_init)(struct hl_device *hdev); void (*hw_queues_lock)(struct hl_device *hdev); void (*hw_queues_unlock)(struct hl_device *hdev); - void (*kdma_lock)(struct hl_device *hdev, int dcore_id); - void (*kdma_unlock)(struct hl_device *hdev, int dcore_id); u32 (*get_pci_id)(struct hl_device *hdev); int (*get_eeprom_data)(struct hl_device *hdev, void *data, size_t max_size); int (*get_monitor_dump)(struct hl_device *hdev, void *data); diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 20f62730be02..4d11efed3e64 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -9196,8 +9196,6 @@ static const struct hl_asic_funcs gaudi_funcs = { .compute_reset_late_init = gaudi_compute_reset_late_init, .hw_queues_lock = gaudi_hw_queues_lock, .hw_queues_unlock = gaudi_hw_queues_unlock, - .kdma_lock = NULL, - .kdma_unlock = NULL, .get_pci_id = gaudi_get_pci_id, .get_eeprom_data = gaudi_get_eeprom_data, .get_monitor_dump = gaudi_get_monitor_dump, diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index 3531a339e742..2c43ed403509 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -3010,7 +3010,6 @@ static int gaudi2_sw_init(struct hl_device *hdev) } spin_lock_init(&gaudi2->hw_queues_lock); - spin_lock_init(&gaudi2->kdma_lock); gaudi2->scratchpad_kernel_address = hl_asic_dma_alloc_coherent(hdev, PAGE_SIZE, &gaudi2->scratchpad_bus_address, @@ -6464,22 +6463,6 @@ static void gaudi2_hw_queues_unlock(struct hl_device *hdev) spin_unlock(&gaudi2->hw_queues_lock); } -static void gaudi2_kdma_lock(struct hl_device *hdev, int dcore_id) - __acquires(&gaudi2->kdma_lock) -{ - struct gaudi2_device *gaudi2 = hdev->asic_specific; - - spin_lock(&gaudi2->kdma_lock); -} - -static void gaudi2_kdma_unlock(struct hl_device *hdev, int dcore_id) - __releases(&gaudi2->kdma_lock) -{ - struct gaudi2_device *gaudi2 = hdev->asic_specific; - - spin_unlock(&gaudi2->kdma_lock); -} - static u32 gaudi2_get_pci_id(struct hl_device *hdev) { return hdev->pdev->device; @@ -9122,8 +9105,6 @@ static int gaudi2_debugfs_read_dma(struct hl_device *hdev, u64 addr, u32 size, v goto unreserve_va; } - hdev->asic_funcs->kdma_lock(hdev, 0); - /* Enable MMU on KDMA */ gaudi2_kdma_set_mmbp_asid(hdev, false, ctx->asid); @@ -9151,8 +9132,6 @@ static int gaudi2_debugfs_read_dma(struct hl_device *hdev, u64 addr, u32 size, v gaudi2_kdma_set_mmbp_asid(hdev, true, HL_KERNEL_ASID_ID); - hdev->asic_funcs->kdma_unlock(hdev, 0); - mutex_lock(&ctx->mmu_lock); hl_mmu_unmap_contiguous(ctx, reserved_va_base, SZ_2M); hl_mmu_invalidate_cache_range(hdev, false, MMU_OP_USERPTR, @@ -9951,8 +9930,6 @@ static const struct hl_asic_funcs gaudi2_funcs = { .compute_reset_late_init = gaudi2_compute_reset_late_init, .hw_queues_lock = gaudi2_hw_queues_lock, .hw_queues_unlock = gaudi2_hw_queues_unlock, - .kdma_lock = gaudi2_kdma_lock, - .kdma_unlock = gaudi2_kdma_unlock, .get_pci_id = gaudi2_get_pci_id, .get_eeprom_data = gaudi2_get_eeprom_data, .get_monitor_dump = gaudi2_get_monitor_dump, diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2P.h b/drivers/misc/habanalabs/gaudi2/gaudi2P.h index 5110574a650e..347ea1dd78e2 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2P.h +++ b/drivers/misc/habanalabs/gaudi2/gaudi2P.h @@ -457,7 +457,6 @@ struct dup_block_ctx { * the user can map. * @lfsr_rand_seeds: array of MME ACC random seeds to set. * @hw_queues_lock: protects the H/W queues from concurrent access. - * @kdma_lock: protects the KDMA engine from concurrent access. * @scratchpad_kernel_address: general purpose PAGE_SIZE contiguous memory, * this memory region should be write-only. * currently used for HBW QMAN writes which is @@ -509,7 +508,6 @@ struct dup_block_ctx { * @flush_db_fifo: flag to force flush DB FIFO after a write. * @hbm_cfg: HBM subsystem settings * @hw_queues_lock_mutex: used by simulator instead of hw_queues_lock. - * @kdma_lock_mutex: used by simulator instead of kdma_lock. */ struct gaudi2_device { int (*cpucp_info_get)(struct hl_device *hdev); @@ -518,7 +516,6 @@ struct gaudi2_device { int lfsr_rand_seeds[MME_NUM_OF_LFSR_SEEDS]; spinlock_t hw_queues_lock; - spinlock_t kdma_lock; void *scratchpad_kernel_address; dma_addr_t scratchpad_bus_address; diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index d4459c290ea8..91429d6ea037 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -5481,8 +5481,6 @@ static const struct hl_asic_funcs goya_funcs = { .compute_reset_late_init = goya_compute_reset_late_init, .hw_queues_lock = goya_hw_queues_lock, .hw_queues_unlock = goya_hw_queues_unlock, - .kdma_lock = NULL, - .kdma_unlock = NULL, .get_pci_id = goya_get_pci_id, .get_eeprom_data = goya_get_eeprom_data, .get_monitor_dump = goya_get_monitor_dump, -- cgit v1.2.3 From f018c54e3de6619c46e33ab1c613761e9fba21d0 Mon Sep 17 00:00:00 2001 From: Dani Liberman Date: Sat, 9 Jul 2022 12:34:17 +0300 Subject: habanalabs: add uapi to retrieve engines status Currently, to get engines status, user needed to read debugfs file with root permissions. This new uapi allows user apace apps retrieve status, so for example, in case of failure, status can be retrieved immediately by the application itself which runs without root permissions. Signed-off-by: Dani Liberman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/debugfs.c | 3 +- drivers/misc/habanalabs/common/habanalabs_ioctl.c | 40 +++++++++++++++++++++++ include/uapi/misc/habanalabs.h | 9 +++++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/drivers/misc/habanalabs/common/debugfs.c b/drivers/misc/habanalabs/common/debugfs.c index 90c91c1b2c10..c297248748d3 100644 --- a/drivers/misc/habanalabs/common/debugfs.c +++ b/drivers/misc/habanalabs/common/debugfs.c @@ -17,7 +17,6 @@ #define MMU_ASID_BUF_SIZE 10 #define MMU_KBUF_SIZE (MMU_ADDR_BUF_SIZE + MMU_ASID_BUF_SIZE) #define I2C_MAX_TRANSACTION_LEN 8 -#define ENGINES_DATA_MAX_SIZE SZ_16K static struct dentry *hl_debug_root; @@ -626,7 +625,7 @@ static int engines_show(struct seq_file *s, void *data) } eng_data.actual_size = 0; - eng_data.allocated_buf_size = ENGINES_DATA_MAX_SIZE; + eng_data.allocated_buf_size = HL_ENGINES_DATA_MAX_SIZE; eng_data.buf = vmalloc(eng_data.allocated_buf_size); if (!eng_data.buf) return -ENOMEM; diff --git a/drivers/misc/habanalabs/common/habanalabs_ioctl.c b/drivers/misc/habanalabs/common/habanalabs_ioctl.c index 6a30bd98ab5e..ec55c66fedd6 100644 --- a/drivers/misc/habanalabs/common/habanalabs_ioctl.c +++ b/drivers/misc/habanalabs/common/habanalabs_ioctl.c @@ -14,6 +14,7 @@ #include #include #include +#include static u32 hl_debug_struct_size[HL_DEBUG_OP_TIMESTAMP + 1] = { [HL_DEBUG_OP_ETR] = sizeof(struct hl_debug_params_etr), @@ -697,6 +698,42 @@ static int eventfd_unregister(struct hl_fpriv *hpriv, struct hl_info_args *args) return 0; } +static int engine_status_info(struct hl_fpriv *hpriv, struct hl_info_args *args) +{ + void __user *out = (void __user *) (uintptr_t) args->return_pointer; + u32 status_buf_size = args->return_size; + struct hl_device *hdev = hpriv->hdev; + struct engines_data eng_data; + int rc; + + if ((status_buf_size < SZ_1K) || (status_buf_size > HL_ENGINES_DATA_MAX_SIZE) || (!out)) + return -EINVAL; + + eng_data.actual_size = 0; + eng_data.allocated_buf_size = status_buf_size; + eng_data.buf = vmalloc(status_buf_size); + if (!eng_data.buf) + return -ENOMEM; + + hdev->asic_funcs->is_device_idle(hdev, NULL, 0, &eng_data); + + if (eng_data.actual_size > eng_data.allocated_buf_size) { + dev_err(hdev->dev, + "Engines data size (%d Bytes) is bigger than allocated size (%u Bytes)\n", + eng_data.actual_size, status_buf_size); + vfree(eng_data.buf); + return -ENOMEM; + } + + args->user_buffer_actual_size = eng_data.actual_size; + rc = copy_to_user(out, eng_data.buf, min_t(size_t, status_buf_size, eng_data.actual_size)) ? + -EFAULT : 0; + + vfree(eng_data.buf); + + return rc; +} + static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, struct device *dev) { @@ -812,6 +849,9 @@ static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, case HL_INFO_UNREGISTER_EVENTFD: return eventfd_unregister(hpriv, args); + case HL_INFO_ENGINE_STATUS: + return engine_status_info(hpriv, args); + default: dev_err(dev, "Invalid request %d\n", args->op); rc = -EINVAL; diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index be06b1307c44..83ca6f40f4ba 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -787,10 +787,14 @@ enum hl_server_type { #define HL_INFO_UNREGISTER_EVENTFD 29 #define HL_INFO_GET_EVENTS 30 #define HL_INFO_UNDEFINED_OPCODE_EVENT 31 +#define HL_INFO_ENGINE_STATUS 32 #define HL_INFO_VERSION_MAX_LEN 128 #define HL_INFO_CARD_NAME_MAX_LEN 16 +/* Maximum buffer size for retrieving engines status */ +#define HL_ENGINES_DATA_MAX_SIZE SZ_1M + /** * struct hl_info_hw_ip_info - hardware information on various IPs in the ASIC * @sram_base_address: The first SRAM physical base address that is free to be @@ -1130,6 +1134,10 @@ enum gaudi_dcores { * resolution. Currently not in use. * @pll_index: Index as defined in hl__pll_index enumeration. * @eventfd: event file descriptor for event notifications. + * @user_buffer_actual_size: Actual data size which was copied to user allocated buffer by the + * driver. It is possible for the user to allocate buffer larger than + * needed, hence updating this variable so user will know the exact amount + * of bytes copied by the kernel to the buffer. * @pad: Padding to 64 bit. */ struct hl_info_args { @@ -1143,6 +1151,7 @@ struct hl_info_args { __u32 period_ms; __u32 pll_index; __u32 eventfd; + __u32 user_buffer_actual_size; }; __u32 pad; -- cgit v1.2.3 From 21fc79336b9587fcc251e77246b68b6e20340146 Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Wed, 20 Jul 2022 20:02:20 +0300 Subject: habanalabs/gaudi2: mark PCIE access error as fatal F/W events are enabled in a late phase of the device init, so an event for a PCIE access error during the init, can be received after the init is already done and considered as successful. A resulting device reset, which does the same H/W init, can end similarly with this event right after the reset is done and considered as successful, and a loop of this sequence can continue. To avoid it mark the PCIE access error as a fatal event, so after 2 consecutive events no more resets will be done. Signed-off-by: Tomer Tayar Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi2/gaudi2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index 2c43ed403509..68ab407fa6ba 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -8532,6 +8532,7 @@ static void gaudi2_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_ent case GAUDI2_EVENT_PCIE_ADDR_DEC_ERR: gaudi2_print_pcie_addr_dec_info(hdev, le64_to_cpu(eq_entry->intr_cause.intr_cause_data)); + reset_flags |= HL_DRV_RESET_FW_FATAL_ERR; break; case GAUDI2_EVENT_HMMU0_PAGE_FAULT_OR_WR_PERM ... GAUDI2_EVENT_HMMU12_SECURITY_ERROR: -- cgit v1.2.3 From d6501ecfb6233197b5b7dbd6aa5256636f3931f3 Mon Sep 17 00:00:00 2001 From: Ohad Sharabi Date: Sun, 24 Jul 2022 08:45:34 +0300 Subject: habanalabs/gaudi: fix print format for div_sel Print format was for int (%d) while variable is u32. Signed-off-by: Ohad Sharabi Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi/gaudi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 4d11efed3e64..866dc4b891d6 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -939,9 +939,7 @@ static int gaudi_fetch_psoc_frequency(struct hl_device *hdev) else freq = pll_clk / (div_fctr + 1); } else { - dev_warn(hdev->dev, - "Received invalid div select value: %d", - div_sel); + dev_warn(hdev->dev, "Received invalid div select value: %#x", div_sel); freq = 0; } } -- cgit v1.2.3 From 68c82ba9a96f47dac9963c9f22b8c6bf4af00e02 Mon Sep 17 00:00:00 2001 From: Ohad Sharabi Date: Sun, 24 Jul 2022 10:23:05 +0300 Subject: habanalabs/gaudi: read div_sel value from firmware Even when running with unsecured f/w, we should read the PLL div_sel value from the f/w as this register is always privileged. Signed-off-by: Ohad Sharabi Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi/gaudi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 866dc4b891d6..7f52935dc603 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -899,12 +899,13 @@ static int gaudi_early_fini(struct hl_device *hdev) */ static int gaudi_fetch_psoc_frequency(struct hl_device *hdev) { - struct asic_fixed_properties *prop = &hdev->asic_prop; u32 nr = 0, nf = 0, od = 0, div_fctr = 0, pll_clk, div_sel; + struct asic_fixed_properties *prop = &hdev->asic_prop; u16 pll_freq_arr[HL_PLL_NUM_OUTPUTS], freq; int rc; - if (hdev->asic_prop.fw_security_enabled) { + if ((hdev->fw_components & FW_TYPE_LINUX) && + (prop->fw_app_cpu_boot_dev_sts0 & CPU_BOOT_DEV_STS0_PLL_INFO_EN)) { struct gaudi_device *gaudi = hdev->asic_specific; if (!(gaudi->hw_cap_initialized & HW_CAP_CPU_Q)) -- cgit v1.2.3 From 0c876b47a54a5ae1331a99da9cc115f8f5c90990 Mon Sep 17 00:00:00 2001 From: Tal Cohen Date: Sun, 24 Jul 2022 17:40:23 +0300 Subject: habanalabs: fix command submission sanity check When a CS is submitted, the ioctl handler checks the CS flags and performs a sanity check, according to its value. As new CS flags are added, the sanity check needs to be updated according to the new flags. Signed-off-by: Tal Cohen Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_submission.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index 90a4574cbe2d..304e4f3b0e7e 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -12,7 +12,9 @@ #include #define HL_CS_FLAGS_TYPE_MASK (HL_CS_FLAGS_SIGNAL | HL_CS_FLAGS_WAIT | \ - HL_CS_FLAGS_COLLECTIVE_WAIT) + HL_CS_FLAGS_COLLECTIVE_WAIT | HL_CS_FLAGS_RESERVE_SIGNALS_ONLY | \ + HL_CS_FLAGS_UNRESERVE_SIGNALS_ONLY) + #define MAX_TS_ITER_NUM 10 @@ -1253,6 +1255,7 @@ static int hl_cs_sanity_checks(struct hl_fpriv *hpriv, union hl_cs_args *args) u32 cs_type_flags, num_chunks; enum hl_device_status status; enum hl_cs_type cs_type; + bool is_sync_stream; if (!hl_device_operational(hdev, &status)) { return -EBUSY; @@ -1276,9 +1279,10 @@ static int hl_cs_sanity_checks(struct hl_fpriv *hpriv, union hl_cs_args *args) cs_type = hl_cs_get_cs_type(cs_type_flags); num_chunks = args->in.num_chunks_execute; - if (unlikely((cs_type == CS_TYPE_SIGNAL || cs_type == CS_TYPE_WAIT || - cs_type == CS_TYPE_COLLECTIVE_WAIT) && - !hdev->supports_sync_stream)) { + is_sync_stream = (cs_type == CS_TYPE_SIGNAL || cs_type == CS_TYPE_WAIT || + cs_type == CS_TYPE_COLLECTIVE_WAIT); + + if (unlikely(is_sync_stream && !hdev->supports_sync_stream)) { dev_err(hdev->dev, "Sync stream CS is not supported\n"); return -EINVAL; } @@ -1288,7 +1292,7 @@ static int hl_cs_sanity_checks(struct hl_fpriv *hpriv, union hl_cs_args *args) dev_err(hdev->dev, "Got execute CS with 0 chunks, context %d\n", ctx->asid); return -EINVAL; } - } else if (num_chunks != 1) { + } else if (is_sync_stream && num_chunks != 1) { dev_err(hdev->dev, "Sync stream CS mandates one chunk only, context %d\n", ctx->asid); -- cgit v1.2.3 From 7fa6c0fe8b2154f84162b8aacbe581df722a6f0c Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Wed, 27 Jul 2022 09:04:13 +0300 Subject: habanalabs: avoid returning a valid handle if map_block() fails map_block() sets the block id handle even if get_hw_block_id() fails, and in this case it uses block id 0 which might be a valid id. Modify it to set the handle only if get_hw_block_id() succeeds. Signed-off-by: Tomer Tayar Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/memory.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c index 61bc1bfe984a..0698c3c363bd 100644 --- a/drivers/misc/habanalabs/common/memory.c +++ b/drivers/misc/habanalabs/common/memory.c @@ -1418,18 +1418,23 @@ vm_type_err: return rc; } -static int map_block(struct hl_device *hdev, u64 address, u64 *handle, - u32 *size) +static int map_block(struct hl_device *hdev, u64 address, u64 *handle, u32 *size) { - u32 block_id = 0; + u32 block_id; int rc; + *handle = 0; + if (size) + *size = 0; + rc = hdev->asic_funcs->get_hw_block_id(hdev, address, size, &block_id); + if (rc) + return rc; *handle = block_id | HL_MMAP_TYPE_BLOCK; *handle <<= PAGE_SHIFT; - return rc; + return 0; } static void hw_block_vm_close(struct vm_area_struct *vma) -- cgit v1.2.3 From 5f46217221dfdda94244d88ac6c1354293fc681b Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Fri, 29 Jul 2022 16:30:48 +0300 Subject: habanalabs: fix vma fields assignments order in hl_hw_block_mmap() In hl_hw_block_mmap(), the vma's 'vm_private_data' and 'vm_ops' fields are assigned before filling the content of the private data. In between there is a call to the ASIC hw_block_mmap() function, and if it fails, the vma close function will be called with a bad private data value. Fix the order of assignments to avoid this issue. In hl_hw_block_mmap() the vma's 'vm_private_data and vm_ops are assigned before setting the Signed-off-by: Tomer Tayar Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/memory.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c index 0698c3c363bd..a027fa88889b 100644 --- a/drivers/misc/habanalabs/common/memory.c +++ b/drivers/misc/habanalabs/common/memory.c @@ -1492,23 +1492,22 @@ int hl_hw_block_mmap(struct hl_fpriv *hpriv, struct vm_area_struct *vma) if (!lnode) return -ENOMEM; - vma->vm_ops = &hw_block_vm_ops; - vma->vm_private_data = lnode; - - hl_ctx_get(ctx); - rc = hdev->asic_funcs->hw_block_mmap(hdev, vma, block_id, block_size); if (rc) { - hl_ctx_put(ctx); kfree(lnode); return rc; } + hl_ctx_get(ctx); + lnode->ctx = ctx; lnode->vaddr = vma->vm_start; lnode->size = block_size; lnode->id = block_id; + vma->vm_private_data = lnode; + vma->vm_ops = &hw_block_vm_ops; + mutex_lock(&ctx->hw_block_list_lock); list_add_tail(&lnode->node, &ctx->hw_block_mem_list); mutex_unlock(&ctx->hw_block_list_lock); -- cgit v1.2.3 From f0d4944c20819edf4de2c5c17963491d23e213da Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Tue, 19 Jul 2022 09:01:53 +0300 Subject: habanalabs: add a missing lock for in_reset indication Add a missing lock in hl_device_resume() when it assigns a value to the 'in_reset' indication. Signed-off-by: Tomer Tayar Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/device.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index 90e346727a7c..6a98aae90f49 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -1091,7 +1091,9 @@ int hl_device_resume(struct hl_device *hdev) /* 'in_reset' was set to true during suspend, now we must clear it in order * for hard reset to be performed */ + spin_lock(&hdev->reset_info.lock); hdev->reset_info.in_reset = 0; + spin_unlock(&hdev->reset_info.lock); rc = hl_device_reset(hdev, HL_DRV_RESET_HARD); if (rc) { -- cgit v1.2.3 From 7ca9022bd776d5a1c694ec0973e3d2e8671013c2 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Sun, 31 Jul 2022 08:27:36 +0300 Subject: habanalabs/uapi: move defines to better place inside file Cosmetic change to move the eventfd events defines to a better location in the file, closer to other INFO IOCTL defines. Signed-off-by: Oded Gabbay --- include/uapi/misc/habanalabs.h | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index 83ca6f40f4ba..0da8894ab94a 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -707,6 +707,21 @@ enum hl_server_type { HL_SERVER_GAUDI2_HLS2 = 5 }; +/* + * Notifier event values - for the notification mechanism and the HL_INFO_GET_EVENTS command + * + * HL_NOTIFIER_EVENT_TPC_ASSERT - Indicates TPC assert event + * HL_NOTIFIER_EVENT_UNDEFINED_OPCODE - Indicates undefined operation code + * HL_NOTIFIER_EVENT_DEVICE_RESET - Indicates device requires a reset + * HL_NOTIFIER_EVENT_CS_TIMEOUT - Indicates CS timeout error + * HL_NOTIFIER_EVENT_DEVICE_UNAVAILABLE - Indicates device is unavailable + */ +#define HL_NOTIFIER_EVENT_TPC_ASSERT (1ULL << 0) +#define HL_NOTIFIER_EVENT_UNDEFINED_OPCODE (1ULL << 1) +#define HL_NOTIFIER_EVENT_DEVICE_RESET (1ULL << 2) +#define HL_NOTIFIER_EVENT_CS_TIMEOUT (1ULL << 3) +#define HL_NOTIFIER_EVENT_DEVICE_UNAVAILABLE (1ULL << 4) + /* Opcode for management ioctl * * HW_IP_INFO - Receive information about different IP blocks in the @@ -1883,21 +1898,6 @@ struct hl_debug_args { __u32 ctx_id; }; -/* - * Notifier event values - for the notification mechanism and the HL_INFO_GET_EVENTS command - * - * HL_NOTIFIER_EVENT_TPC_ASSERT - Indicates TPC assert event - * HL_NOTIFIER_EVENT_UNDEFINED_OPCODE - Indicates undefined operation code - * HL_NOTIFIER_EVENT_DEVICE_RESET - Indicates device requires a reset - * HL_NOTIFIER_EVENT_CS_TIMEOUT - Indicates CS timeout error - * HL_NOTIFIER_EVENT_DEVICE_UNAVAILABLE - Indicates device is unavailable - */ -#define HL_NOTIFIER_EVENT_TPC_ASSERT (1ULL << 0) -#define HL_NOTIFIER_EVENT_UNDEFINED_OPCODE (1ULL << 1) -#define HL_NOTIFIER_EVENT_DEVICE_RESET (1ULL << 2) -#define HL_NOTIFIER_EVENT_CS_TIMEOUT (1ULL << 3) -#define HL_NOTIFIER_EVENT_DEVICE_UNAVAILABLE (1ULL << 4) - /* * Various information operations such as: * - H/W IP information -- cgit v1.2.3 From ab6c08f0d597408ce7ab4a0f92088cf7cefd2915 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Sun, 31 Jul 2022 09:10:24 +0300 Subject: habanalabs: move common function out of debugfs.c A common function that is called from multiple places can't be located in degugfs.c because that file is only compiled if debugfs is enabled in the kernel config file. This can lead to undefined symbol compilation error. Reported-by: kernel test robot Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/debugfs.c | 25 ------------------------- drivers/misc/habanalabs/common/device.c | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/drivers/misc/habanalabs/common/debugfs.c b/drivers/misc/habanalabs/common/debugfs.c index c297248748d3..69fd3ed7680a 100644 --- a/drivers/misc/habanalabs/common/debugfs.c +++ b/drivers/misc/habanalabs/common/debugfs.c @@ -586,31 +586,6 @@ err: return -EINVAL; } -void hl_engine_data_sprintf(struct engines_data *e, const char *fmt, ...) -{ - va_list args; - int str_size; - - va_start(args, fmt); - /* Calculate formatted string length. Assuming each string is null terminated, hence - * increment result by 1 - */ - str_size = vsnprintf(NULL, 0, fmt, args) + 1; - va_end(args); - - if ((e->actual_size + str_size) < e->allocated_buf_size) { - va_start(args, fmt); - vsnprintf(e->buf + e->actual_size, str_size, fmt, args); - va_end(args); - } - - /* Need to update the size even when not updating destination buffer to get the exact size - * of all input strings - */ - e->actual_size += str_size; - -} - static int engines_show(struct seq_file *s, void *data) { struct hl_debugfs_entry *entry = s->private; diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index 6a98aae90f49..ab2497b6d164 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -267,6 +267,30 @@ int hl_access_dev_mem(struct hl_device *hdev, enum pci_region region_type, return 0; } +void hl_engine_data_sprintf(struct engines_data *e, const char *fmt, ...) +{ + va_list args; + int str_size; + + va_start(args, fmt); + /* Calculate formatted string length. Assuming each string is null terminated, hence + * increment result by 1 + */ + str_size = vsnprintf(NULL, 0, fmt, args) + 1; + va_end(args); + + if ((e->actual_size + str_size) < e->allocated_buf_size) { + va_start(args, fmt); + vsnprintf(e->buf + e->actual_size, str_size, fmt, args); + va_end(args); + } + + /* Need to update the size even when not updating destination buffer to get the exact size + * of all input strings + */ + e->actual_size += str_size; +} + enum hl_device_status hl_device_status(struct hl_device *hdev) { enum hl_device_status status; -- cgit v1.2.3 From 6419b5232efacb59b227c7088d1c00b98bdb82de Mon Sep 17 00:00:00 2001 From: farah kassabri Date: Wed, 20 Jul 2022 13:53:37 +0300 Subject: habanalabs/gaudi2: change device f/w security check On Gaudi2 the f/w always configures the PCIe iATU and allows access to scratchpad registers. Therefore, we can know if the f/w is secured by reading a status bit from the f/w registers. Signed-off-by: farah kassabri Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/firmware_if.c | 2 ++ drivers/misc/habanalabs/gaudi2/gaudi2.c | 21 ++++++--------------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index 58c1eff16df6..cbcb9442bdca 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -1476,6 +1476,8 @@ static void hl_fw_preboot_update_state(struct hl_device *hdev) */ prop->hard_reset_done_by_fw = !!(cpu_boot_dev_sts0 & CPU_BOOT_DEV_STS0_FW_HARD_RST_EN); + prop->fw_security_enabled = !!(cpu_boot_dev_sts0 & CPU_BOOT_DEV_STS0_SECURITY_EN); + dev_dbg(hdev->dev, "Firmware preboot boot device status0 %#x\n", cpu_boot_dev_sts0); diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index 68ab407fa6ba..9ccde0258eca 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -2493,7 +2493,6 @@ static int gaudi2_early_init(struct hl_device *hdev) struct asic_fixed_properties *prop = &hdev->asic_prop; struct pci_dev *pdev = hdev->pdev; resource_size_t pci_bar_size; - u32 fw_boot_status; int rc; rc = gaudi2_set_fixed_properties(hdev); @@ -2521,22 +2520,14 @@ static int gaudi2_early_init(struct hl_device *hdev) prop->dram_pci_bar_size = pci_resource_len(pdev, DRAM_BAR_ID); hdev->dram_pci_bar_start = pci_resource_start(pdev, DRAM_BAR_ID); - /* If FW security is enabled at this point it means no access to ELBI */ - if (hdev->asic_prop.fw_security_enabled) { - hdev->asic_prop.iatu_done_by_fw = true; - goto pci_init; - } - - rc = hl_pci_elbi_read(hdev, CFG_BASE + mmCPU_BOOT_DEV_STS0, &fw_boot_status); - if (rc) - goto free_queue_props; - - /* Check whether FW is configuring iATU */ - if ((fw_boot_status & CPU_BOOT_DEV_STS0_ENABLED) && - (fw_boot_status & CPU_BOOT_DEV_STS0_FW_IATU_CONF_EN)) + /* + * Only in pldm driver config iATU + */ + if (hdev->pldm) + hdev->asic_prop.iatu_done_by_fw = false; + else hdev->asic_prop.iatu_done_by_fw = true; -pci_init: rc = hl_pci_init(hdev); if (rc) goto free_queue_props; -- cgit v1.2.3 From 07056f58e43319902cd1072c00df2846b31e14b8 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Sun, 7 Aug 2022 16:36:30 +0300 Subject: habanalabs: remove left-over code from bring-up There is some left-over code from the gaudi2 bring-up that wasn't removed so far. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/firmware_if.c | 20 ---------------- .../misc/habanalabs/include/common/hl_boot_if.h | 28 ++++++++++++++++------ 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index cbcb9442bdca..12d0f18c1f6c 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -15,14 +15,6 @@ #define FW_FILE_MAX_SIZE 0x1400000 /* maximum size of 20MB */ -struct fw_binning_conf { - u64 tpc_binning; - u32 dec_binning; - u32 hbm_binning; - u32 edma_binning; - u32 mme_redundancy; -}; - static char *extract_fw_ver_from_str(const char *fw_str) { char *str, *fw_ver, *whitespace; @@ -2424,18 +2416,6 @@ static int hl_fw_dynamic_send_msg(struct hl_device *hdev, msg.reset_cause = *(__u8 *) data; break; - case HL_COMMS_BINNING_CONF_TYPE: - { - struct fw_binning_conf *binning_conf = (struct fw_binning_conf *) data; - - msg.tpc_binning_conf = cpu_to_le64(binning_conf->tpc_binning); - msg.dec_binning_conf = cpu_to_le32(binning_conf->dec_binning); - msg.hbm_binning_conf = cpu_to_le32(binning_conf->hbm_binning); - msg.edma_binning_conf = cpu_to_le32(binning_conf->edma_binning); - msg.mme_redundancy_conf = cpu_to_le32(binning_conf->mme_redundancy); - break; - } - default: dev_err(hdev->dev, "Send COMMS message - invalid message type %u\n", diff --git a/drivers/misc/habanalabs/include/common/hl_boot_if.h b/drivers/misc/habanalabs/include/common/hl_boot_if.h index a3594119bc51..f2f6488de625 100644 --- a/drivers/misc/habanalabs/include/common/hl_boot_if.h +++ b/drivers/misc/habanalabs/include/common/hl_boot_if.h @@ -465,6 +465,26 @@ enum comms_msg_type { HL_COMMS_BINNING_CONF_TYPE = 3, }; +/* + * Binning information shared between LKD and FW + * @tpc_mask - TPC binning information + * @dec_mask - Decoder binning information + * @hbm_mask - HBM binning information + * @edma_mask - EDMA binning information + * @mme_mask_l - MME binning information lower 32 + * @mme_mask_h - MME binning information upper 32 + * @reserved - reserved field for 64 bit alignment + */ +struct lkd_fw_binning_info { + __le64 tpc_mask; + __le32 dec_mask; + __le32 hbm_mask; + __le32 edma_mask; + __le32 mme_mask_l; + __le32 mme_mask_h; + __le32 reserved; +}; + /* TODO: remove this struct after the code is updated to use message */ /* this is the comms descriptor header - meta data */ struct comms_desc_header { @@ -525,13 +545,7 @@ struct lkd_fw_comms_msg { struct { __u8 fw_cfg_skip; /* 1 - skip, 0 - don't skip */ }; - struct { - __le64 tpc_binning_conf; - __le32 dec_binning_conf; - __le32 hbm_binning_conf; - __le32 edma_binning_conf; - __le32 mme_redundancy_conf; /* use MME_REDUNDANT_COLUMN */ - }; + struct lkd_fw_binning_info binning_info; }; }; -- cgit v1.2.3 From 194e515c79462f1ad09ebcc9e01a3acb84a98d82 Mon Sep 17 00:00:00 2001 From: Tal Cohen Date: Thu, 7 Jul 2022 18:42:47 +0300 Subject: habanalabs/gaudi2: new API to control engine cores running mode The current flow of halting the engine cores is implemented by command buffers built by the user space and sent towards the Driver. This current flow is broken since the user space does not know when the cores actually halt as sending a workload is async op. Therefore the application can not free the memory that is mapped to the engine cores. This new API allows the user space to control the running mode. The API call is sync (returns after the cores are set to the requested mode). Signed-off-by: Tal Cohen Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- .../misc/habanalabs/common/command_submission.c | 43 ++++++++++++- drivers/misc/habanalabs/common/habanalabs.h | 8 ++- drivers/misc/habanalabs/gaudi2/gaudi2.c | 72 ++++++++++++++++++++-- .../include/gaudi2/asic_reg/gaudi2_regs.h | 1 + include/uapi/misc/habanalabs.h | 38 ++++++++++-- 5 files changed, 151 insertions(+), 11 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index 304e4f3b0e7e..cf4118515678 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -13,7 +13,7 @@ #define HL_CS_FLAGS_TYPE_MASK (HL_CS_FLAGS_SIGNAL | HL_CS_FLAGS_WAIT | \ HL_CS_FLAGS_COLLECTIVE_WAIT | HL_CS_FLAGS_RESERVE_SIGNALS_ONLY | \ - HL_CS_FLAGS_UNRESERVE_SIGNALS_ONLY) + HL_CS_FLAGS_UNRESERVE_SIGNALS_ONLY | HL_CS_FLAGS_ENGINE_CORE_COMMAND) #define MAX_TS_ITER_NUM 10 @@ -1244,6 +1244,8 @@ static enum hl_cs_type hl_cs_get_cs_type(u32 cs_type_flags) return CS_RESERVE_SIGNALS; else if (cs_type_flags & HL_CS_FLAGS_UNRESERVE_SIGNALS_ONLY) return CS_UNRESERVE_SIGNALS; + else if (cs_type_flags & HL_CS_FLAGS_ENGINE_CORE_COMMAND) + return CS_TYPE_ENGINE_CORE; else return CS_TYPE_DEFAULT; } @@ -2355,6 +2357,41 @@ out: return rc; } +static int cs_ioctl_engine_cores(struct hl_fpriv *hpriv, u64 engine_cores, + u32 num_engine_cores, u32 core_command) +{ + int rc; + struct hl_device *hdev = hpriv->hdev; + void __user *engine_cores_arr; + u32 *cores; + + if (!num_engine_cores || num_engine_cores > hdev->asic_prop.num_engine_cores) { + dev_err(hdev->dev, "Number of engine cores %d is invalid\n", num_engine_cores); + return -EINVAL; + } + + if (core_command != HL_ENGINE_CORE_RUN && core_command != HL_ENGINE_CORE_HALT) { + dev_err(hdev->dev, "Engine core command is invalid\n"); + return -EINVAL; + } + + engine_cores_arr = (void __user *) (uintptr_t) engine_cores; + cores = kmalloc_array(num_engine_cores, sizeof(u32), GFP_KERNEL); + if (!cores) + return -ENOMEM; + + if (copy_from_user(cores, engine_cores_arr, num_engine_cores * sizeof(u32))) { + dev_err(hdev->dev, "Failed to copy core-ids array from user\n"); + kfree(cores); + return -EFAULT; + } + + rc = hdev->asic_funcs->set_engine_cores(hdev, cores, num_engine_cores, core_command); + kfree(cores); + + return rc; +} + int hl_cs_ioctl(struct hl_fpriv *hpriv, void *data) { union hl_cs_args *args = data; @@ -2407,6 +2444,10 @@ int hl_cs_ioctl(struct hl_fpriv *hpriv, void *data) rc = cs_ioctl_unreserve_signals(hpriv, args->in.encaps_sig_handle_id); break; + case CS_TYPE_ENGINE_CORE: + rc = cs_ioctl_engine_cores(hpriv, args->in.engine_cores, + args->in.num_engine_cores, args->in.core_command); + break; default: rc = cs_ioctl_default(hpriv, chunks, num_chunks, &cs_seq, args->in.cs_flags, diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 8d9e96c6092a..ae3f5832fe58 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -345,7 +345,8 @@ enum hl_cs_type { CS_TYPE_WAIT, CS_TYPE_COLLECTIVE_WAIT, CS_RESERVE_SIGNALS, - CS_UNRESERVE_SIGNALS + CS_UNRESERVE_SIGNALS, + CS_TYPE_ENGINE_CORE }; /* @@ -617,6 +618,7 @@ struct hl_hints_range { * which the property supports_user_set_page_size is true * (i.e. the DRAM supports multiple page sizes), otherwise * it will shall be equal to dram_page_size. + * @num_engine_cores: number of engine cpu cores * @collective_first_sob: first sync object available for collective use * @collective_first_mon: first monitor available for collective use * @sync_stream_first_sob: first sync object available for sync stream use @@ -737,6 +739,7 @@ struct asic_fixed_properties { u32 faulty_dram_cluster_map; u32 xbar_edge_enabled_mask; u32 device_mem_alloc_default_page_size; + u32 num_engine_cores; u16 collective_first_sob; u16 collective_first_mon; u16 sync_stream_first_sob; @@ -1511,6 +1514,7 @@ struct engines_data { * @check_if_razwi_happened: check if there was a razwi due to RR violation. * @access_dev_mem: access device memory * @set_dram_bar_base: set the base of the DRAM BAR + * @set_engine_cores: set a config command to enigne cores */ struct hl_asic_funcs { int (*early_init)(struct hl_device *hdev); @@ -1645,6 +1649,8 @@ struct hl_asic_funcs { int (*access_dev_mem)(struct hl_device *hdev, enum pci_region region_type, u64 addr, u64 *val, enum debugfs_access_type acc_type); u64 (*set_dram_bar_base)(struct hl_device *hdev, u64 addr); + int (*set_engine_cores)(struct hl_device *hdev, u32 *core_ids, + u32 num_cores, u32 core_command); }; diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index 9ccde0258eca..676419961f86 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -1989,6 +1989,7 @@ static int gaudi2_set_fixed_properties(struct hl_device *hdev) prop->pmmu_huge.end_addr = VA_HOST_SPACE_HPAGE_END; } + prop->num_engine_cores = CPU_ID_MAX; prop->cfg_size = CFG_SIZE; prop->max_asid = MAX_ASID; prop->num_of_events = GAUDI2_EVENT_SIZE; @@ -3751,14 +3752,16 @@ static void gaudi2_stop_dec(struct hl_device *hdev) gaudi2_stop_pcie_dec(hdev); } -static void gaudi2_halt_arc(struct hl_device *hdev, u32 cpu_id) +static void gaudi2_set_arc_running_mode(struct hl_device *hdev, u32 cpu_id, u32 run_mode) { u32 reg_base, reg_val; reg_base = gaudi2_arc_blocks_bases[cpu_id]; + if (run_mode == HL_ENGINE_CORE_RUN) + reg_val = FIELD_PREP(ARC_FARM_ARC0_AUX_RUN_HALT_REQ_RUN_REQ_MASK, 1); + else + reg_val = FIELD_PREP(ARC_FARM_ARC0_AUX_RUN_HALT_REQ_HALT_REQ_MASK, 1); - /* Halt ARC */ - reg_val = FIELD_PREP(ARC_FARM_ARC0_AUX_RUN_HALT_REQ_HALT_REQ_MASK, 1); WREG32(reg_base + ARC_HALT_REQ_OFFSET, reg_val); } @@ -3768,10 +3771,37 @@ static void gaudi2_halt_arcs(struct hl_device *hdev) for (arc_id = CPU_ID_SCHED_ARC0; arc_id < CPU_ID_MAX; arc_id++) { if (gaudi2_is_arc_enabled(hdev, arc_id)) - gaudi2_halt_arc(hdev, arc_id); + gaudi2_set_arc_running_mode(hdev, arc_id, HL_ENGINE_CORE_HALT); } } +static int gaudi2_verify_arc_running_mode(struct hl_device *hdev, u32 cpu_id, u32 run_mode) +{ + int rc; + u32 reg_base, val, ack_mask, timeout_usec = 100000; + + if (hdev->pldm) + timeout_usec *= 100; + + reg_base = gaudi2_arc_blocks_bases[cpu_id]; + if (run_mode == HL_ENGINE_CORE_RUN) + ack_mask = ARC_FARM_ARC0_AUX_RUN_HALT_ACK_RUN_ACK_MASK; + else + ack_mask = ARC_FARM_ARC0_AUX_RUN_HALT_ACK_HALT_ACK_MASK; + + rc = hl_poll_timeout(hdev, reg_base + ARC_HALT_ACK_OFFSET, + val, ((val & ack_mask) == ack_mask), + 1000, timeout_usec); + + if (!rc) { + /* Clear */ + val = FIELD_PREP(ARC_FARM_ARC0_AUX_RUN_HALT_REQ_RUN_REQ_MASK, 0); + WREG32(reg_base + ARC_HALT_REQ_OFFSET, val); + } + + return rc; +} + static void gaudi2_reset_arcs(struct hl_device *hdev) { struct gaudi2_device *gaudi2 = hdev->asic_specific; @@ -3796,8 +3826,39 @@ static void gaudi2_nic_qmans_manual_flush(struct hl_device *hdev) queue_id = GAUDI2_QUEUE_ID_NIC_0_0; - for (i = 0 ; i < NIC_NUMBER_OF_ENGINES ; i++, queue_id += NUM_OF_PQ_PER_QMAN) + for (i = 0 ; i < NIC_NUMBER_OF_ENGINES ; i++, queue_id += NUM_OF_PQ_PER_QMAN) { + if (!(hdev->nic_ports_mask & BIT(i))) + continue; + gaudi2_qman_manual_flush_common(hdev, queue_id); + } +} + +static int gaudi2_set_engine_cores(struct hl_device *hdev, u32 *core_ids, + u32 num_cores, u32 core_command) +{ + int i, rc; + + + for (i = 0 ; i < num_cores ; i++) { + if (gaudi2_is_arc_enabled(hdev, core_ids[i])) + gaudi2_set_arc_running_mode(hdev, core_ids[i], core_command); + } + + for (i = 0 ; i < num_cores ; i++) { + if (gaudi2_is_arc_enabled(hdev, core_ids[i])) { + rc = gaudi2_verify_arc_running_mode(hdev, core_ids[i], core_command); + + if (rc) { + dev_err(hdev->dev, "failed to %s arc: %d\n", + (core_command == HL_ENGINE_CORE_HALT) ? + "HALT" : "RUN", core_ids[i]); + return -1; + } + } + } + + return 0; } static void gaudi2_halt_engines(struct hl_device *hdev, bool hard_reset, bool fw_reset) @@ -9968,6 +10029,7 @@ static const struct hl_asic_funcs gaudi2_funcs = { .mmu_get_real_page_size = gaudi2_mmu_get_real_page_size, .access_dev_mem = hl_access_dev_mem, .set_dram_bar_base = gaudi2_set_hbm_bar_base, + .set_engine_cores = gaudi2_set_engine_cores, }; void gaudi2_set_asic_funcs(struct hl_device *hdev) diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/gaudi2_regs.h b/drivers/misc/habanalabs/include/gaudi2/asic_reg/gaudi2_regs.h index d0e2c68a639f..bfda4223bdc8 100644 --- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/gaudi2_regs.h +++ b/drivers/misc/habanalabs/include/gaudi2/asic_reg/gaudi2_regs.h @@ -239,6 +239,7 @@ #define SFT_IF_RTR_OFFSET (mmSFT0_HBW_RTR_IF1_RTR_H3_BASE - mmSFT0_HBW_RTR_IF0_RTR_H3_BASE) #define ARC_HALT_REQ_OFFSET (mmARC_FARM_ARC0_AUX_RUN_HALT_REQ - mmARC_FARM_ARC0_AUX_BASE) +#define ARC_HALT_ACK_OFFSET (mmARC_FARM_ARC0_AUX_RUN_HALT_ACK - mmARC_FARM_ARC0_AUX_BASE) #define ARC_REGION_CFG_OFFSET(region) \ (mmARC_FARM_ARC0_AUX_ARC_REGION_CFG_0 + (region * 4) - mmARC_FARM_ARC0_AUX_BASE) diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index 0da8894ab94a..f51c6ae4f94d 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -1361,17 +1361,47 @@ struct hl_cs_chunk { #define HL_CS_FLAGS_RESERVE_SIGNALS_ONLY 0x1000 #define HL_CS_FLAGS_UNRESERVE_SIGNALS_ONLY 0x2000 +/* + * The engine cores CS is merged into the existing CS ioctls. + * Use it to control the engine cores mode. + */ +#define HL_CS_FLAGS_ENGINE_CORE_COMMAND 0x4000 + #define HL_CS_STATUS_SUCCESS 0 #define HL_MAX_JOBS_PER_CS 512 +/* HL_ENGINE_CORE_ values + * + * HL_ENGINE_CORE_HALT: engine core halt + * HL_ENGINE_CORE_RUN: engine core run + */ +#define HL_ENGINE_CORE_HALT (1 << 0) +#define HL_ENGINE_CORE_RUN (1 << 1) + struct hl_cs_in { - /* this holds address of array of hl_cs_chunk for restore phase */ - __u64 chunks_restore; + union { + struct { + /* this holds address of array of hl_cs_chunk for restore phase */ + __u64 chunks_restore; - /* holds address of array of hl_cs_chunk for execution phase */ - __u64 chunks_execute; + /* holds address of array of hl_cs_chunk for execution phase */ + __u64 chunks_execute; + }; + + /* Valid only when HL_CS_FLAGS_ENGINE_CORE_COMMAND is set */ + struct { + /* this holds address of array of uint32 for engine_cores */ + __u64 engine_cores; + + /* number of engine cores in engine_cores array */ + __u32 num_engine_cores; + + /* the core command to be sent towards engine cores */ + __u32 core_command; + }; + }; union { /* -- cgit v1.2.3 From 07ecaa0d85decb73a2907a4b419cfa7739517d5e Mon Sep 17 00:00:00 2001 From: Dani Liberman Date: Mon, 27 Jun 2022 22:06:51 +0300 Subject: habanalabs: unify hwmon resources clean up Since hwmon fini code is common for all asics, unified it to common function. Signed-off-by: Dani Liberman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/habanalabs.h | 1 + drivers/misc/habanalabs/common/hwmon.c | 21 +++++++++++++++++++++ drivers/misc/habanalabs/gaudi/gaudi.c | 18 +----------------- drivers/misc/habanalabs/gaudi2/gaudi2.c | 18 +----------------- drivers/misc/habanalabs/goya/goya.c | 17 +---------------- 5 files changed, 25 insertions(+), 50 deletions(-) diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index ae3f5832fe58..f495a4b82f73 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -3529,6 +3529,7 @@ void hl_sysfs_fini(struct hl_device *hdev); int hl_hwmon_init(struct hl_device *hdev); void hl_hwmon_fini(struct hl_device *hdev); +void hl_hwmon_release_resources(struct hl_device *hdev); int hl_cb_create(struct hl_device *hdev, struct hl_mem_mgr *mmg, struct hl_ctx *ctx, u32 cb_size, bool internal_cb, diff --git a/drivers/misc/habanalabs/common/hwmon.c b/drivers/misc/habanalabs/common/hwmon.c index 57f5d2c48330..8c262aeb425e 100644 --- a/drivers/misc/habanalabs/common/hwmon.c +++ b/drivers/misc/habanalabs/common/hwmon.c @@ -910,3 +910,24 @@ void hl_hwmon_fini(struct hl_device *hdev) hwmon_device_unregister(hdev->hwmon_dev); } + +void hl_hwmon_release_resources(struct hl_device *hdev) +{ + const struct hwmon_channel_info **channel_info_arr; + int i = 0; + + if (!hdev->hl_chip_info->info) + return; + + channel_info_arr = hdev->hl_chip_info->info; + + while (channel_info_arr[i]) { + kfree(channel_info_arr[i]->config); + kfree(channel_info_arr[i]); + i++; + } + + kfree(channel_info_arr); + + hdev->hl_chip_info->info = NULL; +} diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 7f52935dc603..96020693ac29 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -1682,23 +1682,7 @@ disable_pci_access: static void gaudi_late_fini(struct hl_device *hdev) { - const struct hwmon_channel_info **channel_info_arr; - int i = 0; - - if (!hdev->hl_chip_info->info) - return; - - channel_info_arr = hdev->hl_chip_info->info; - - while (channel_info_arr[i]) { - kfree(channel_info_arr[i]->config); - kfree(channel_info_arr[i]); - i++; - } - - kfree(channel_info_arr); - - hdev->hl_chip_info->info = NULL; + hl_hwmon_release_resources(hdev); } static int gaudi_alloc_cpu_accessible_dma_mem(struct hl_device *hdev) diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index 676419961f86..fa806e5b6680 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -2711,23 +2711,7 @@ disable_pci_access: static void gaudi2_late_fini(struct hl_device *hdev) { - const struct hwmon_channel_info **channel_info_arr; - int i = 0; - - if (!hdev->hl_chip_info->info) - return; - - channel_info_arr = hdev->hl_chip_info->info; - - while (channel_info_arr[i]) { - kfree(channel_info_arr[i]->config); - kfree(channel_info_arr[i]); - i++; - } - - kfree(channel_info_arr); - - hdev->hl_chip_info->info = NULL; + hl_hwmon_release_resources(hdev); } static void gaudi2_user_mapped_dec_init(struct gaudi2_device *gaudi2, u32 start_idx) diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 91429d6ea037..87465a28af0d 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -916,26 +916,11 @@ int goya_late_init(struct hl_device *hdev) */ void goya_late_fini(struct hl_device *hdev) { - const struct hwmon_channel_info **channel_info_arr; struct goya_device *goya = hdev->asic_specific; - int i = 0; cancel_delayed_work_sync(&goya->goya_work->work_freq); - if (!hdev->hl_chip_info->info) - return; - - channel_info_arr = hdev->hl_chip_info->info; - - while (channel_info_arr[i]) { - kfree(channel_info_arr[i]->config); - kfree(channel_info_arr[i]); - i++; - } - - kfree(channel_info_arr); - - hdev->hl_chip_info->info = NULL; + hl_hwmon_release_resources(hdev); } static void goya_set_pci_memory_regions(struct hl_device *hdev) -- cgit v1.2.3 From 65d3c635137e24625740801dc21d885f66193299 Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Wed, 3 Aug 2022 16:36:02 +0300 Subject: habanalabs: fix H/W block handling for partial unmappings Several munmap() calls can be done or a mapped H/W block that has a larger size than a page size. Releasing the object should be done only when all mapped range is unmapped. Signed-off-by: Tomer Tayar Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/debugfs.c | 14 ++++++++------ drivers/misc/habanalabs/common/habanalabs.h | 6 ++++-- drivers/misc/habanalabs/common/memory.c | 10 +++++++++- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/drivers/misc/habanalabs/common/debugfs.c b/drivers/misc/habanalabs/common/debugfs.c index 69fd3ed7680a..48d3ec8b5c82 100644 --- a/drivers/misc/habanalabs/common/debugfs.c +++ b/drivers/misc/habanalabs/common/debugfs.c @@ -291,14 +291,16 @@ static int vm_show(struct seq_file *s, void *data) if (ctx->asid != HL_KERNEL_ASID_ID && !list_empty(&ctx->hw_block_mem_list)) { seq_puts(s, "\nhw_block mappings:\n\n"); - seq_puts(s, " virtual address size HW block id\n"); - seq_puts(s, "-------------------------------------------\n"); + seq_puts(s, + " virtual address block size mapped size HW block id\n"); + seq_puts(s, + "---------------------------------------------------------------\n"); mutex_lock(&ctx->hw_block_list_lock); - list_for_each_entry(lnode, &ctx->hw_block_mem_list, - node) { + list_for_each_entry(lnode, &ctx->hw_block_mem_list, node) { seq_printf(s, - " 0x%-14lx %-6u %-9u\n", - lnode->vaddr, lnode->size, lnode->id); + " 0x%-14lx %-6u %-6u %-9u\n", + lnode->vaddr, lnode->block_size, lnode->mapped_size, + lnode->id); } mutex_unlock(&ctx->hw_block_list_lock); } diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index f495a4b82f73..237a887b3a43 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -2063,14 +2063,16 @@ struct hl_vm_hash_node { * @node: node to hang on the list in context object. * @ctx: the context this node belongs to. * @vaddr: virtual address of the HW block. - * @size: size of the block. + * @block_size: size of the block. + * @mapped_size: size of the block which is mapped. May change if partial un-mappings are done. * @id: HW block id (handle). */ struct hl_vm_hw_block_list_node { struct list_head node; struct hl_ctx *ctx; unsigned long vaddr; - u32 size; + u32 block_size; + u32 mapped_size; u32 id; }; diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c index a027fa88889b..5bc704da889d 100644 --- a/drivers/misc/habanalabs/common/memory.c +++ b/drivers/misc/habanalabs/common/memory.c @@ -1442,6 +1442,13 @@ static void hw_block_vm_close(struct vm_area_struct *vma) struct hl_vm_hw_block_list_node *lnode = (struct hl_vm_hw_block_list_node *) vma->vm_private_data; struct hl_ctx *ctx = lnode->ctx; + long new_mmap_size; + + new_mmap_size = lnode->mapped_size - (vma->vm_end - vma->vm_start); + if (new_mmap_size > 0) { + lnode->mapped_size = new_mmap_size; + return; + } mutex_lock(&ctx->hw_block_list_lock); list_del(&lnode->node); @@ -1502,7 +1509,8 @@ int hl_hw_block_mmap(struct hl_fpriv *hpriv, struct vm_area_struct *vma) lnode->ctx = ctx; lnode->vaddr = vma->vm_start; - lnode->size = block_size; + lnode->block_size = block_size; + lnode->mapped_size = lnode->block_size; lnode->id = block_id; vma->vm_private_data = lnode; -- cgit v1.2.3 From 107a5bcc0b34c0f4f6c8b771321bf3a4e095335d Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Wed, 3 Aug 2022 11:59:19 +0300 Subject: habanalabs: remove secured PCI IDs Secured PCI ID will not be supported in new asics because the security status can always be read from the f/w. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/habanalabs_drv.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/misc/habanalabs/common/habanalabs_drv.c b/drivers/misc/habanalabs/common/habanalabs_drv.c index d59d8cdf33e6..8026793d9083 100644 --- a/drivers/misc/habanalabs/common/habanalabs_drv.c +++ b/drivers/misc/habanalabs/common/habanalabs_drv.c @@ -58,14 +58,12 @@ MODULE_PARM_DESC(boot_error_status_mask, #define PCI_IDS_GAUDI_SEC 0x1010 #define PCI_IDS_GAUDI2 0x1020 -#define PCI_IDS_GAUDI2_SEC 0x1030 static const struct pci_device_id ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_HABANALABS, PCI_IDS_GOYA), }, { PCI_DEVICE(PCI_VENDOR_ID_HABANALABS, PCI_IDS_GAUDI), }, { PCI_DEVICE(PCI_VENDOR_ID_HABANALABS, PCI_IDS_GAUDI_SEC), }, { PCI_DEVICE(PCI_VENDOR_ID_HABANALABS, PCI_IDS_GAUDI2), }, - { PCI_DEVICE(PCI_VENDOR_ID_HABANALABS, PCI_IDS_GAUDI2_SEC), }, { 0, } }; MODULE_DEVICE_TABLE(pci, ids); @@ -95,9 +93,6 @@ static enum hl_asic_type get_asic_type(u16 device) case PCI_IDS_GAUDI2: asic_type = ASIC_GAUDI2; break; - case PCI_IDS_GAUDI2_SEC: - asic_type = ASIC_GAUDI2_SEC; - break; default: asic_type = ASIC_INVALID; break; @@ -110,7 +105,6 @@ static bool is_asic_secured(enum hl_asic_type asic_type) { switch (asic_type) { case ASIC_GAUDI_SEC: - case ASIC_GAUDI2_SEC: return true; default: return false; -- cgit v1.2.3 From 6457271f64a2b6674b11aebb2888427eb4558c22 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Wed, 3 Aug 2022 17:16:56 +0300 Subject: habanalabs: expose device security status through sysfs In order for the user to know if he is running on a secured device or not, a sysfs node is added. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- Documentation/ABI/testing/sysfs-driver-habanalabs | 6 ++++++ drivers/misc/habanalabs/common/sysfs.c | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-driver-habanalabs b/Documentation/ABI/testing/sysfs-driver-habanalabs index 96646fb2e7a1..6963640a2615 100644 --- a/Documentation/ABI/testing/sysfs-driver-habanalabs +++ b/Documentation/ABI/testing/sysfs-driver-habanalabs @@ -176,6 +176,12 @@ KernelVersion: 5.1 Contact: ogabbay@kernel.org Description: Version of the device's preboot F/W code +What: /sys/class/habanalabs/hl/security_enabled +Date: Oct 2022 +KernelVersion: 6.1 +Contact: obitton@habana.ai +Description: Displays the device's security status + What: /sys/class/habanalabs/hl/soft_reset Date: Jan 2019 KernelVersion: 5.1 diff --git a/drivers/misc/habanalabs/common/sysfs.c b/drivers/misc/habanalabs/common/sysfs.c index 6c5271f01160..36e9814139d1 100644 --- a/drivers/misc/habanalabs/common/sysfs.c +++ b/drivers/misc/habanalabs/common/sysfs.c @@ -375,6 +375,14 @@ out: return max_size; } +static ssize_t security_enabled_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", hdev->asic_prop.fw_security_enabled); +} + static DEVICE_ATTR_RO(armcp_kernel_ver); static DEVICE_ATTR_RO(armcp_ver); static DEVICE_ATTR_RO(cpld_ver); @@ -393,6 +401,7 @@ static DEVICE_ATTR_RO(status); static DEVICE_ATTR_RO(thermal_ver); static DEVICE_ATTR_RO(uboot_ver); static DEVICE_ATTR_RO(fw_os_ver); +static DEVICE_ATTR_RO(security_enabled); static struct bin_attribute bin_attr_eeprom = { .attr = {.name = "eeprom", .mode = (0444)}, @@ -417,6 +426,7 @@ static struct attribute *hl_dev_attrs[] = { &dev_attr_thermal_ver.attr, &dev_attr_uboot_ver.attr, &dev_attr_fw_os_ver.attr, + &dev_attr_security_enabled.attr, NULL, }; -- cgit v1.2.3 From 38a4358009456bfd7e4893c4f98ee401efce26eb Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Wed, 3 Aug 2022 17:25:33 +0300 Subject: habanalabs: expose device security status using info ioctl In order for the user to know if he is running on a secured device or not, we add it also to the hw_ip info ioctl. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/habanalabs_ioctl.c | 1 + include/uapi/misc/habanalabs.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/common/habanalabs_ioctl.c b/drivers/misc/habanalabs/common/habanalabs_ioctl.c index ec55c66fedd6..c7bd000750c8 100644 --- a/drivers/misc/habanalabs/common/habanalabs_ioctl.c +++ b/drivers/misc/habanalabs/common/habanalabs_ioctl.c @@ -104,6 +104,7 @@ static int hw_ip_info(struct hl_device *hdev, struct hl_info_args *args) hw_ip.edma_enabled_mask = prop->edma_enabled_mask; hw_ip.server_type = prop->server_type; + hw_ip.security_enabled = prop->fw_security_enabled; return copy_to_user(out, &hw_ip, min((size_t) size, sizeof(hw_ip))) ? -EFAULT : 0; diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index f51c6ae4f94d..3005cc04d4b1 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -840,6 +840,7 @@ enum hl_server_type { * @tpc_enabled_mask: Bit-mask that represents which TPCs are enabled. Relevant * for Goya/Gaudi only. * @dram_enabled: Whether the DRAM is enabled. + * @security_enabled: Whether security is enabled on device. * @mme_master_slave_mode: Indicate whether the MME is working in master/slave * configuration. Relevant for Greco and later. * @cpucp_version: The CPUCP f/w version. @@ -871,7 +872,7 @@ struct hl_info_hw_ip_info { __u32 psoc_pci_pll_div_factor; __u8 tpc_enabled_mask; __u8 dram_enabled; - __u8 reserved; + __u8 security_enabled; __u8 mme_master_slave_mode; __u8 cpucp_version[HL_INFO_VERSION_MAX_LEN]; __u8 card_name[HL_INFO_CARD_NAME_MAX_LEN]; -- cgit v1.2.3 From 75bc3986fc768912715d4898d5f4aac3fadbc155 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 8 Aug 2022 14:03:44 +0300 Subject: habanalabs: fix bug when setting va block size the size of a block is always 'block->end - block->start + 1' Signed-off-by: Dafna Hirschfeld Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/memory.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c index 5bc704da889d..e3b40dbf154c 100644 --- a/drivers/misc/habanalabs/common/memory.c +++ b/drivers/misc/habanalabs/common/memory.c @@ -457,7 +457,7 @@ static void merge_va_blocks_locked(struct hl_device *hdev, prev = list_prev_entry(va_block, node); if (&prev->node != va_list && prev->end + 1 == va_block->start) { prev->end = va_block->end; - prev->size = prev->end - prev->start; + prev->size = prev->end - prev->start + 1; list_del(&va_block->node); kfree(va_block); va_block = prev; @@ -466,7 +466,7 @@ static void merge_va_blocks_locked(struct hl_device *hdev, next = list_next_entry(va_block, node); if (&next->node != va_list && va_block->end + 1 == next->start) { next->start = va_block->start; - next->size = next->end - next->start; + next->size = next->end - next->start + 1; list_del(&va_block->node); kfree(va_block); } -- cgit v1.2.3 From 78da23cb103336be27a5fa5f3d16ff7b08f7b4b9 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Wed, 10 Aug 2022 15:39:20 +0300 Subject: habanalabs: fix missing info in sysfs documentation The kernel version field wasn't updated when a few entries were upstreamed. Signed-off-by: Oded Gabbay --- Documentation/ABI/testing/sysfs-driver-habanalabs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-driver-habanalabs b/Documentation/ABI/testing/sysfs-driver-habanalabs index 6963640a2615..13b5b2ec3be7 100644 --- a/Documentation/ABI/testing/sysfs-driver-habanalabs +++ b/Documentation/ABI/testing/sysfs-driver-habanalabs @@ -16,7 +16,7 @@ Description: Version of the application running on the device's CPU What: /sys/class/habanalabs/hl/clk_max_freq_mhz Date: Jun 2019 -KernelVersion: not yet upstreamed +KernelVersion: 5.7 Contact: ogabbay@kernel.org Description: Allows the user to set the maximum clock frequency, in MHz. The device clock might be set to lower value than the maximum. @@ -26,7 +26,7 @@ Description: Allows the user to set the maximum clock frequency, in MHz. What: /sys/class/habanalabs/hl/clk_cur_freq_mhz Date: Jun 2019 -KernelVersion: not yet upstreamed +KernelVersion: 5.7 Contact: ogabbay@kernel.org Description: Displays the current frequency, in MHz, of the device clock. This property is valid only for the Gaudi ASIC family @@ -236,6 +236,6 @@ Description: Version of the u-boot running on the device's CPU What: /sys/class/habanalabs/hl/vrm_ver Date: Jan 2022 -KernelVersion: not yet upstreamed +KernelVersion: 5.17 Contact: ogabbay@kernel.org Description: Version of the Device's Voltage Regulator Monitor F/W code. N/A to GOYA and GAUDI -- cgit v1.2.3 From 273190d4204ef44b14b97e571ed7b4e42504189f Mon Sep 17 00:00:00 2001 From: Omer Shpigelman Date: Thu, 4 Aug 2022 17:32:30 +0300 Subject: habanalabs: add cdev index data member Instead of recalculating the cdev index, store it in a dedicated data member. This data member is intended to be passed to other drivers using the auxiliary bus infra and hence this new data member is necessary in case that the calculation is changed in the future. Signed-off-by: Omer Shpigelman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/device.c | 10 ++++++---- drivers/misc/habanalabs/common/habanalabs.h | 4 +++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index ab2497b6d164..b662d40f18e8 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -1730,7 +1730,9 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass) char *name; bool add_cdev_sysfs_on_err = false; - name = kasprintf(GFP_KERNEL, "hl%d", hdev->id / 2); + hdev->cdev_idx = hdev->id / 2; + + name = kasprintf(GFP_KERNEL, "hl%d", hdev->cdev_idx); if (!name) { rc = -ENOMEM; goto out_disabled; @@ -1745,7 +1747,7 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass) if (rc) goto out_disabled; - name = kasprintf(GFP_KERNEL, "hl_controlD%d", hdev->id / 2); + name = kasprintf(GFP_KERNEL, "hl_controlD%d", hdev->cdev_idx); if (!name) { rc = -ENOMEM; goto free_dev; @@ -2023,10 +2025,10 @@ out_disabled: if (hdev->pdev) dev_err(&hdev->pdev->dev, "Failed to initialize hl%d. Device is NOT usable !\n", - hdev->id / 2); + hdev->cdev_idx); else pr_err("Failed to initialize hl%d. Device is NOT usable !\n", - hdev->id / 2); + hdev->cdev_idx); return rc; } diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 237a887b3a43..a3c516c31b54 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -3129,7 +3129,8 @@ struct hl_reset_info { * @edma_binning: contains mask of edma engines that is received from the f/w which * indicates which edma engines are binned-out * @id: device minor. - * @id_control: minor of the control device + * @id_control: minor of the control device. + * @cdev_idx: char device index. Used for setting its name. * @cpu_pci_msb_addr: 50-bit extension bits for the device CPU's 40-bit * addresses. * @is_in_dram_scrub: true if dram scrub operation is on going. @@ -3289,6 +3290,7 @@ struct hl_device { u32 edma_binning; u16 id; u16 id_control; + u16 cdev_idx; u16 cpu_pci_msb_addr; u8 is_in_dram_scrub; u8 disabled; -- cgit v1.2.3 From 6173572f29a4f9f27b9655666e55cee20b0b8cf5 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Mon, 15 Aug 2022 13:59:14 +0300 Subject: habanalabs: select FW_LOADER in Kconfig The driver is loading firmware to the device and we use the firmware loading functions from the FW_LOADER module. 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 861c81006c6d..bd01d0d940c0 100644 --- a/drivers/misc/habanalabs/Kconfig +++ b/drivers/misc/habanalabs/Kconfig @@ -10,6 +10,7 @@ config HABANA_AI select HWMON select DMA_SHARED_BUFFER select CRC32 + select FW_LOADER 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 From 46e49f434fcaafe3c62232aaa0358f03b462141d Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 15 Aug 2022 11:40:55 +0300 Subject: habanalabs: if map page fails don't try to unmap it The original code tried to unmap a page that was not mapped as part of the map page error path. Signed-off-by: Dafna Hirschfeld Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/mmu/mmu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/misc/habanalabs/common/mmu/mmu.c b/drivers/misc/habanalabs/common/mmu/mmu.c index 60740de47b34..4153aec55594 100644 --- a/drivers/misc/habanalabs/common/mmu/mmu.c +++ b/drivers/misc/habanalabs/common/mmu/mmu.c @@ -403,6 +403,8 @@ int hl_mmu_map_contiguous(struct hl_ctx *ctx, u64 virt_addr, dev_err(hdev->dev, "Map failed for va 0x%llx to pa 0x%llx\n", curr_va, curr_pa); + /* last mapping failed so don't try to unmap it - reduce off by page_size */ + off -= page_size; goto unmap; } } -- cgit v1.2.3 From fb855768d33fe7ec1c0e5b9ed21a72478a81f77e Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Mon, 15 Aug 2022 20:13:30 +0300 Subject: habanalabs: fix calculation of DRAM base address in PCIe BAR The calculation of the device DRAM base address before setting the relevant PCIe BAR to point at it, has an assumption that this BAR is used to access only the DRAM, and thus the covered DRAM size is a power of 2. In future ASICs it is not necessarily true, so need to update the calculation to support also a non-power-of-2 size. Signed-off-by: Tomer Tayar Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/device.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index b662d40f18e8..0b3097802b00 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -42,7 +42,11 @@ static uint64_t hl_set_dram_bar(struct hl_device *hdev, u64 addr) struct asic_fixed_properties *prop = &hdev->asic_prop; u64 bar_base_addr; - bar_base_addr = addr & ~(prop->dram_pci_bar_size - 0x1ull); + if (is_power_of_2(prop->dram_pci_bar_size)) + bar_base_addr = addr & ~(prop->dram_pci_bar_size - 0x1ull); + else + bar_base_addr = DIV_ROUND_DOWN_ULL(addr, prop->dram_pci_bar_size) * + prop->dram_pci_bar_size; return hdev->asic_funcs->set_dram_bar_base(hdev, bar_base_addr); } -- cgit v1.2.3 From 7b5d13c9cae72b9baac88009401d5518cd86bb0d Mon Sep 17 00:00:00 2001 From: Rajarama Manjukody Bhat Date: Fri, 12 Aug 2022 09:28:20 +0300 Subject: habanalabs/gaudi2: assigning PQFs for ARC f/w in PDMA Assigning 3 PQFs in PDMA1 and 2 PQFs in PDMA0 for ARC firmware usage. Signed-off-by: Rajarama Manjukody Bhat Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi2/gaudi2.c | 13 +++++++++---- drivers/misc/habanalabs/gaudi2/gaudi2_masks.h | 8 +++++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index fa806e5b6680..c907e0fbf182 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -4175,11 +4175,15 @@ static void gaudi2_init_qman_common(struct hl_device *hdev, u32 reg_base, WREG32(reg_base + QM_GLBL_CFG2_OFFSET, 0); /* Enable the QMAN channel. - * PDMA1 QMAN configuration is different, as we do not allow user to - * access CP2/3, it is reserved for the ARC usage. + * PDMA QMAN configuration is different, as we do not allow user to + * access some of the CPs. + * PDMA0: CP2/3 are reserved for the ARC usage. + * PDMA1: CP1/2/3 are reserved for the ARC usage. */ if (reg_base == gaudi2_qm_blocks_bases[GAUDI2_QUEUE_ID_PDMA_1_0]) WREG32(reg_base + QM_GLBL_CFG0_OFFSET, PDMA1_QMAN_ENABLE); + else if (reg_base == gaudi2_qm_blocks_bases[GAUDI2_QUEUE_ID_PDMA_0_0]) + WREG32(reg_base + QM_GLBL_CFG0_OFFSET, PDMA0_QMAN_ENABLE); else WREG32(reg_base + QM_GLBL_CFG0_OFFSET, QMAN_ENABLE); } @@ -5580,10 +5584,11 @@ static bool gaudi2_is_queue_enabled(struct hl_device *hdev, u32 hw_queue_id) u64 hw_test_cap_bit = 0; switch (hw_queue_id) { - case GAUDI2_QUEUE_ID_PDMA_0_0 ... GAUDI2_QUEUE_ID_PDMA_1_1: + case GAUDI2_QUEUE_ID_PDMA_0_0: + case GAUDI2_QUEUE_ID_PDMA_0_1: + case GAUDI2_QUEUE_ID_PDMA_1_0: hw_cap_mask = HW_CAP_PDMA_MASK; break; - case GAUDI2_QUEUE_ID_DCORE0_EDMA_0_0...GAUDI2_QUEUE_ID_DCORE0_EDMA_1_3: hw_test_cap_bit = HW_CAP_EDMA_SHIFT + ((hw_queue_id - GAUDI2_QUEUE_ID_DCORE0_EDMA_0_0) >> 2); diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2_masks.h b/drivers/misc/habanalabs/gaudi2/gaudi2_masks.h index eed16d642a5a..0239d118abc5 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2_masks.h +++ b/drivers/misc/habanalabs/gaudi2/gaudi2_masks.h @@ -51,12 +51,18 @@ (0x1F << PDMA0_QM_GLBL_CFG0_CP_EN_SHIFT) | \ (0x1 << PDMA0_QM_GLBL_CFG0_ARC_CQF_EN_SHIFT)) -#define PDMA1_QMAN_ENABLE \ +#define PDMA0_QMAN_ENABLE \ ((0x3 << PDMA0_QM_GLBL_CFG0_PQF_EN_SHIFT) | \ (0x1F << PDMA0_QM_GLBL_CFG0_CQF_EN_SHIFT) | \ (0x1F << PDMA0_QM_GLBL_CFG0_CP_EN_SHIFT) | \ (0x1 << PDMA0_QM_GLBL_CFG0_ARC_CQF_EN_SHIFT)) +#define PDMA1_QMAN_ENABLE \ + ((0x1 << PDMA0_QM_GLBL_CFG0_PQF_EN_SHIFT) | \ + (0x1F << PDMA0_QM_GLBL_CFG0_CQF_EN_SHIFT) | \ + (0x1F << PDMA0_QM_GLBL_CFG0_CP_EN_SHIFT) | \ + (0x1 << PDMA0_QM_GLBL_CFG0_ARC_CQF_EN_SHIFT)) + /* QM_IDLE_MASK is valid for all engines QM idle check */ #define QM_IDLE_MASK (DCORE0_EDMA0_QM_GLBL_STS0_PQF_IDLE_MASK | \ DCORE0_EDMA0_QM_GLBL_STS0_CQF_IDLE_MASK | \ -- cgit v1.2.3 From 191a4443c39b278fbb0898590530bef122b26b7e Mon Sep 17 00:00:00 2001 From: Ohad Sharabi Date: Wed, 8 Jun 2022 09:58:59 +0300 Subject: habanalabs: define trace events This patch adds trace events for habanalabs driver to gain all the benefits such an infrastructure can supply. The following events were added: - MMU map/unmap: to be able to track driver's memory allocations - DMA alloc/free: to track our DMA allocation the above trace points in conjunction will help us map the device memory usage as well as to be able to track memory violations. Signed-off-by: Ohad Sharabi Acked-by: Oded Gabbay Reviewed-by: Steven Rostedt (Google) Signed-off-by: Oded Gabbay --- MAINTAINERS | 1 + drivers/misc/habanalabs/common/habanalabs_drv.c | 3 + include/trace/events/habanalabs.h | 90 +++++++++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 include/trace/events/habanalabs.h diff --git a/MAINTAINERS b/MAINTAINERS index da6d4fd517b0..20ead1fd0e19 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8886,6 +8886,7 @@ T: git https://git.kernel.org/pub/scm/linux/kernel/git/ogabbay/linux.git F: Documentation/ABI/testing/debugfs-driver-habanalabs F: Documentation/ABI/testing/sysfs-driver-habanalabs F: drivers/misc/habanalabs/ +F: include/trace/events/habanalabs.h F: include/uapi/misc/habanalabs.h HACKRF MEDIA DRIVER diff --git a/drivers/misc/habanalabs/common/habanalabs_drv.c b/drivers/misc/habanalabs/common/habanalabs_drv.c index 8026793d9083..e12148428731 100644 --- a/drivers/misc/habanalabs/common/habanalabs_drv.c +++ b/drivers/misc/habanalabs/common/habanalabs_drv.c @@ -14,6 +14,9 @@ #include #include +#define CREATE_TRACE_POINTS +#include + #define HL_DRIVER_AUTHOR "HabanaLabs Kernel Driver Team" #define HL_DRIVER_DESC "Driver for HabanaLabs's AI Accelerators" diff --git a/include/trace/events/habanalabs.h b/include/trace/events/habanalabs.h new file mode 100644 index 000000000000..09ca516e1624 --- /dev/null +++ b/include/trace/events/habanalabs.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright 2016-2021 HabanaLabs, Ltd. + * All Rights Reserved. + * + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM habanalabs + +#if !defined(_TRACE_HABANALABS_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_HABANALABS_H + +#include + +DECLARE_EVENT_CLASS(habanalabs_mmu_template, + TP_PROTO(struct device *dev, u64 virt_addr, u64 phys_addr, u32 page_size, bool flush_pte), + + TP_ARGS(dev, virt_addr, phys_addr, page_size, flush_pte), + + TP_STRUCT__entry( + __string(dname, dev_name(dev)) + __field(u64, virt_addr) + __field(u64, phys_addr) + __field(u32, page_size) + __field(u8, flush_pte) + ), + + TP_fast_assign( + __assign_str(dname, dev_name(dev)); + __entry->virt_addr = virt_addr; + __entry->phys_addr = phys_addr; + __entry->page_size = page_size; + __entry->flush_pte = flush_pte; + ), + + TP_printk("%s: vaddr: %#llx, paddr: %#llx, psize: %#x, flush: %s", + __get_str(dname), + __entry->virt_addr, + __entry->phys_addr, + __entry->page_size, + __entry->flush_pte ? "true" : "false") +); + +DEFINE_EVENT(habanalabs_mmu_template, habanalabs_mmu_map, + TP_PROTO(struct device *dev, u64 virt_addr, u64 phys_addr, u32 page_size, bool flush_pte), + TP_ARGS(dev, virt_addr, phys_addr, page_size, flush_pte)); + +DEFINE_EVENT(habanalabs_mmu_template, habanalabs_mmu_unmap, + TP_PROTO(struct device *dev, u64 virt_addr, u64 phys_addr, u32 page_size, bool flush_pte), + TP_ARGS(dev, virt_addr, phys_addr, page_size, flush_pte)); + +DECLARE_EVENT_CLASS(habanalabs_dma_alloc_template, + TP_PROTO(struct device *dev, u64 cpu_addr, u64 dma_addr, size_t size), + + TP_ARGS(dev, cpu_addr, dma_addr, size), + + TP_STRUCT__entry( + __string(dname, dev_name(dev)) + __field(u64, cpu_addr) + __field(u64, dma_addr) + __field(u32, size) + ), + + TP_fast_assign( + __assign_str(dname, dev_name(dev)); + __entry->cpu_addr = cpu_addr; + __entry->dma_addr = dma_addr; + __entry->size = size; + ), + + TP_printk("%s: cpu_addr: %#llx, dma_addr: %#llx, size: %#x", + __get_str(dname), + __entry->cpu_addr, + __entry->dma_addr, + __entry->size) +); + +DEFINE_EVENT(habanalabs_dma_alloc_template, habanalabs_dma_alloc, + TP_PROTO(struct device *dev, u64 cpu_addr, u64 dma_addr, size_t size), + TP_ARGS(dev, cpu_addr, dma_addr, size)); + +DEFINE_EVENT(habanalabs_dma_alloc_template, habanalabs_dma_free, + TP_PROTO(struct device *dev, u64 cpu_addr, u64 dma_addr, size_t size), + TP_ARGS(dev, cpu_addr, dma_addr, size)); + +#endif /* if !defined(_TRACE_HABANALABS_H) || defined(TRACE_HEADER_MULTI_READ) */ + +/* This part must be outside protection */ +#include -- cgit v1.2.3 From 4eb87df3d04aa725e752fe2df0df3e83f204d247 Mon Sep 17 00:00:00 2001 From: Ohad Sharabi Date: Wed, 8 Jun 2022 10:27:59 +0300 Subject: habanalabs: trace MMU map/unmap page This patch utilize the defined tracepoint to trace the MMU's pages map/unmap operations. Signed-off-by: Ohad Sharabi Reviewed-by: Oded Gabbay Reviewed-by: Steven Rostedt (Google) Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/mmu/mmu.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/misc/habanalabs/common/mmu/mmu.c b/drivers/misc/habanalabs/common/mmu/mmu.c index 4153aec55594..264f3b9edc88 100644 --- a/drivers/misc/habanalabs/common/mmu/mmu.c +++ b/drivers/misc/habanalabs/common/mmu/mmu.c @@ -9,6 +9,8 @@ #include "../habanalabs.h" +#include + /** * hl_mmu_get_funcs() - get MMU functions structure * @hdev: habanalabs device structure. @@ -259,6 +261,9 @@ int hl_mmu_unmap_page(struct hl_ctx *ctx, u64 virt_addr, u32 page_size, bool flu if (flush_pte) mmu_funcs->flush(ctx); + if (trace_habanalabs_mmu_unmap_enabled() && !rc) + trace_habanalabs_mmu_unmap(hdev->dev, virt_addr, 0, page_size, flush_pte); + return rc; } @@ -344,6 +349,8 @@ int hl_mmu_map_page(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, u32 page_s if (flush_pte) mmu_funcs->flush(ctx); + trace_habanalabs_mmu_map(hdev->dev, virt_addr, phys_addr, page_size, flush_pte); + return 0; err: -- cgit v1.2.3 From 0263256791094180ab8749b224ef7bfe0bfd67bb Mon Sep 17 00:00:00 2001 From: Ohad Sharabi Date: Sun, 12 Jun 2022 15:00:29 +0300 Subject: habanalabs: trace DMA allocations This patch add tracepoints in the code for DMA allocation. The main purpose is to be able to cross data with the map operations and determine whether memory violation occurred, for example free DMA allocation before unmapping it from device memory. To achieve this the DMA alloc/free code flows were refactored so that a single DMA tracepoint will catch many flows. To get better understanding of what happened in the DMA allocations the real allocating function is added to the trace as well. Signed-off-by: Ohad Sharabi Reviewed-by: Oded Gabbay Reviewed-by: Steven Rostedt (Google) Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/device.c | 49 ++++++++++++++++++----------- drivers/misc/habanalabs/common/habanalabs.h | 40 +++++++++++++++++------ include/trace/events/habanalabs.h | 19 ++++++----- 3 files changed, 73 insertions(+), 35 deletions(-) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index 0b3097802b00..230b7eeef962 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -13,6 +13,8 @@ #include #include +#include + #define HL_RESET_DELAY_USEC 10000 /* 10ms */ enum dma_alloc_type { @@ -101,9 +103,10 @@ static int hl_access_sram_dram_region(struct hl_device *hdev, u64 addr, u64 *val } static void *hl_dma_alloc_common(struct hl_device *hdev, size_t size, dma_addr_t *dma_handle, - gfp_t flag, enum dma_alloc_type alloc_type) + gfp_t flag, enum dma_alloc_type alloc_type, + const char *caller) { - void *ptr; + void *ptr = NULL; switch (alloc_type) { case DMA_ALLOC_COHERENT: @@ -117,11 +120,16 @@ static void *hl_dma_alloc_common(struct hl_device *hdev, size_t size, dma_addr_t break; } + if (trace_habanalabs_dma_alloc_enabled() && !ZERO_OR_NULL_PTR(ptr)) + trace_habanalabs_dma_alloc(hdev->dev, (u64) (uintptr_t) ptr, *dma_handle, size, + caller); + return ptr; } static void hl_asic_dma_free_common(struct hl_device *hdev, size_t size, void *cpu_addr, - dma_addr_t dma_handle, enum dma_alloc_type alloc_type) + dma_addr_t dma_handle, enum dma_alloc_type alloc_type, + const char *caller) { switch (alloc_type) { case DMA_ALLOC_COHERENT: @@ -134,39 +142,44 @@ static void hl_asic_dma_free_common(struct hl_device *hdev, size_t size, void *c hdev->asic_funcs->asic_dma_pool_free(hdev, cpu_addr, dma_handle); break; } + + trace_habanalabs_dma_free(hdev->dev, (u64) (uintptr_t) cpu_addr, dma_handle, size, caller); } -void *hl_asic_dma_alloc_coherent(struct hl_device *hdev, size_t size, dma_addr_t *dma_handle, - gfp_t flag) +void *hl_asic_dma_alloc_coherent_caller(struct hl_device *hdev, size_t size, dma_addr_t *dma_handle, + gfp_t flag, const char *caller) { - return hl_dma_alloc_common(hdev, size, dma_handle, flag, DMA_ALLOC_COHERENT); + return hl_dma_alloc_common(hdev, size, dma_handle, flag, DMA_ALLOC_COHERENT, caller); } -void hl_asic_dma_free_coherent(struct hl_device *hdev, size_t size, void *cpu_addr, - dma_addr_t dma_handle) +void hl_asic_dma_free_coherent_caller(struct hl_device *hdev, size_t size, void *cpu_addr, + dma_addr_t dma_handle, const char *caller) { - hl_asic_dma_free_common(hdev, size, cpu_addr, dma_handle, DMA_ALLOC_COHERENT); + hl_asic_dma_free_common(hdev, size, cpu_addr, dma_handle, DMA_ALLOC_COHERENT, caller); } -void *hl_cpu_accessible_dma_pool_alloc(struct hl_device *hdev, size_t size, dma_addr_t *dma_handle) +void *hl_cpu_accessible_dma_pool_alloc_caller(struct hl_device *hdev, size_t size, + dma_addr_t *dma_handle, const char *caller) { - return hl_dma_alloc_common(hdev, size, dma_handle, 0, DMA_ALLOC_CPU_ACCESSIBLE); + return hl_dma_alloc_common(hdev, size, dma_handle, 0, DMA_ALLOC_CPU_ACCESSIBLE, caller); } -void hl_cpu_accessible_dma_pool_free(struct hl_device *hdev, size_t size, void *vaddr) +void hl_cpu_accessible_dma_pool_free_caller(struct hl_device *hdev, size_t size, void *vaddr, + const char *caller) { - hl_asic_dma_free_common(hdev, size, vaddr, 0, DMA_ALLOC_CPU_ACCESSIBLE); + hl_asic_dma_free_common(hdev, size, vaddr, 0, DMA_ALLOC_CPU_ACCESSIBLE, caller); } -void *hl_asic_dma_pool_zalloc(struct hl_device *hdev, size_t size, gfp_t mem_flags, - dma_addr_t *dma_handle) +void *hl_asic_dma_pool_zalloc_caller(struct hl_device *hdev, size_t size, gfp_t mem_flags, + dma_addr_t *dma_handle, const char *caller) { - return hl_dma_alloc_common(hdev, size, dma_handle, mem_flags, DMA_ALLOC_POOL); + return hl_dma_alloc_common(hdev, size, dma_handle, mem_flags, DMA_ALLOC_POOL, caller); } -void hl_asic_dma_pool_free(struct hl_device *hdev, void *vaddr, dma_addr_t dma_addr) +void hl_asic_dma_pool_free_caller(struct hl_device *hdev, void *vaddr, dma_addr_t dma_addr, + const char *caller) { - hl_asic_dma_free_common(hdev, 0, vaddr, dma_addr, DMA_ALLOC_POOL); + hl_asic_dma_free_common(hdev, 0, vaddr, dma_addr, DMA_ALLOC_POOL, caller); } int hl_dma_map_sgtable(struct hl_device *hdev, struct sg_table *sgt, enum dma_data_direction dir) diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index a3c516c31b54..43b9427d9c97 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -143,6 +143,25 @@ enum hl_mmu_enablement { #define HL_MAX_DCORES 8 +/* DMA alloc/free wrappers */ +#define hl_asic_dma_alloc_coherent(hdev, size, dma_handle, flags) \ + hl_asic_dma_alloc_coherent_caller(hdev, size, dma_handle, flags, __func__) + +#define hl_cpu_accessible_dma_pool_alloc(hdev, size, dma_handle) \ + hl_cpu_accessible_dma_pool_alloc_caller(hdev, size, dma_handle, __func__) + +#define hl_asic_dma_pool_zalloc(hdev, size, mem_flags, dma_handle) \ + hl_asic_dma_pool_zalloc_caller(hdev, size, mem_flags, dma_handle, __func__) + +#define hl_asic_dma_free_coherent(hdev, size, cpu_addr, dma_handle) \ + hl_asic_dma_free_coherent_caller(hdev, size, cpu_addr, dma_handle, __func__) + +#define hl_cpu_accessible_dma_pool_free(hdev, size, vaddr) \ + hl_cpu_accessible_dma_pool_free_caller(hdev, size, vaddr, __func__) + +#define hl_asic_dma_pool_free(hdev, vaddr, dma_addr) \ + hl_asic_dma_pool_free_caller(hdev, vaddr, dma_addr, __func__) + /* * Reset Flags * @@ -3446,15 +3465,18 @@ static inline bool hl_mem_area_crosses_range(u64 address, u32 size, } uint64_t hl_set_dram_bar_default(struct hl_device *hdev, u64 addr); -void *hl_asic_dma_alloc_coherent(struct hl_device *hdev, size_t size, dma_addr_t *dma_handle, - gfp_t flag); -void hl_asic_dma_free_coherent(struct hl_device *hdev, size_t size, void *cpu_addr, - dma_addr_t dma_handle); -void *hl_cpu_accessible_dma_pool_alloc(struct hl_device *hdev, size_t size, dma_addr_t *dma_handle); -void hl_cpu_accessible_dma_pool_free(struct hl_device *hdev, size_t size, void *vaddr); -void *hl_asic_dma_pool_zalloc(struct hl_device *hdev, size_t size, gfp_t mem_flags, - dma_addr_t *dma_handle); -void hl_asic_dma_pool_free(struct hl_device *hdev, void *vaddr, dma_addr_t dma_addr); +void *hl_asic_dma_alloc_coherent_caller(struct hl_device *hdev, size_t size, dma_addr_t *dma_handle, + gfp_t flag, const char *caller); +void hl_asic_dma_free_coherent_caller(struct hl_device *hdev, size_t size, void *cpu_addr, + dma_addr_t dma_handle, const char *caller); +void *hl_cpu_accessible_dma_pool_alloc_caller(struct hl_device *hdev, size_t size, + dma_addr_t *dma_handle, const char *caller); +void hl_cpu_accessible_dma_pool_free_caller(struct hl_device *hdev, size_t size, void *vaddr, + const char *caller); +void *hl_asic_dma_pool_zalloc_caller(struct hl_device *hdev, size_t size, gfp_t mem_flags, + dma_addr_t *dma_handle, const char *caller); +void hl_asic_dma_pool_free_caller(struct hl_device *hdev, void *vaddr, dma_addr_t dma_addr, + const char *caller); int hl_dma_map_sgtable(struct hl_device *hdev, struct sg_table *sgt, enum dma_data_direction dir); void hl_dma_unmap_sgtable(struct hl_device *hdev, struct sg_table *sgt, enum dma_data_direction dir); diff --git a/include/trace/events/habanalabs.h b/include/trace/events/habanalabs.h index 09ca516e1624..f05c5fa668a2 100644 --- a/include/trace/events/habanalabs.h +++ b/include/trace/events/habanalabs.h @@ -51,15 +51,16 @@ DEFINE_EVENT(habanalabs_mmu_template, habanalabs_mmu_unmap, TP_ARGS(dev, virt_addr, phys_addr, page_size, flush_pte)); DECLARE_EVENT_CLASS(habanalabs_dma_alloc_template, - TP_PROTO(struct device *dev, u64 cpu_addr, u64 dma_addr, size_t size), + TP_PROTO(struct device *dev, u64 cpu_addr, u64 dma_addr, size_t size, const char *caller), - TP_ARGS(dev, cpu_addr, dma_addr, size), + TP_ARGS(dev, cpu_addr, dma_addr, size, caller), TP_STRUCT__entry( __string(dname, dev_name(dev)) __field(u64, cpu_addr) __field(u64, dma_addr) __field(u32, size) + __field(const char *, caller) ), TP_fast_assign( @@ -67,22 +68,24 @@ DECLARE_EVENT_CLASS(habanalabs_dma_alloc_template, __entry->cpu_addr = cpu_addr; __entry->dma_addr = dma_addr; __entry->size = size; + __entry->caller = caller; ), - TP_printk("%s: cpu_addr: %#llx, dma_addr: %#llx, size: %#x", + TP_printk("%s: cpu_addr: %#llx, dma_addr: %#llx, size: %#x, caller: %s", __get_str(dname), __entry->cpu_addr, __entry->dma_addr, - __entry->size) + __entry->size, + __entry->caller) ); DEFINE_EVENT(habanalabs_dma_alloc_template, habanalabs_dma_alloc, - TP_PROTO(struct device *dev, u64 cpu_addr, u64 dma_addr, size_t size), - TP_ARGS(dev, cpu_addr, dma_addr, size)); + TP_PROTO(struct device *dev, u64 cpu_addr, u64 dma_addr, size_t size, const char *caller), + TP_ARGS(dev, cpu_addr, dma_addr, size, caller)); DEFINE_EVENT(habanalabs_dma_alloc_template, habanalabs_dma_free, - TP_PROTO(struct device *dev, u64 cpu_addr, u64 dma_addr, size_t size), - TP_ARGS(dev, cpu_addr, dma_addr, size)); + TP_PROTO(struct device *dev, u64 cpu_addr, u64 dma_addr, size_t size, const char *caller), + TP_ARGS(dev, cpu_addr, dma_addr, size, caller)); #endif /* if !defined(_TRACE_HABANALABS_H) || defined(TRACE_HEADER_MULTI_READ) */ -- cgit v1.2.3 From 262042af1397099f88386830152770bcfd0de122 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 23 May 2022 08:59:19 +0300 Subject: habanalabs: set command buffer host VA dynamically Set the addresses for userspace command buffer dynamically instead of hard-coded. There is no reason for it to be hard-coded. Signed-off-by: Dafna Hirschfeld Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_buffer.c | 18 ++++++++++++++---- drivers/misc/habanalabs/common/habanalabs.h | 10 +++------- drivers/misc/habanalabs/common/memory.c | 2 +- drivers/misc/habanalabs/gaudi2/gaudi2.c | 3 --- drivers/misc/habanalabs/gaudi2/gaudi2P.h | 3 --- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_buffer.c b/drivers/misc/habanalabs/common/command_buffer.c index b027f66f8bd4..c3e2568542a1 100644 --- a/drivers/misc/habanalabs/common/command_buffer.c +++ b/drivers/misc/habanalabs/common/command_buffer.c @@ -12,6 +12,8 @@ #include #include +#define CB_VA_POOL_SIZE (4UL * SZ_1G) + static int cb_map_mem(struct hl_ctx *ctx, struct hl_cb *cb) { struct hl_device *hdev = ctx->hdev; @@ -25,7 +27,7 @@ static int cb_map_mem(struct hl_ctx *ctx, struct hl_cb *cb) if (!hdev->supports_cb_mapping) { dev_err_ratelimited(hdev->dev, - "Cannot map CB because no VA range is allocated for CB mapping\n"); + "Mapping a CB to the device's MMU is not supported\n"); return -EINVAL; } @@ -566,16 +568,23 @@ int hl_cb_va_pool_init(struct hl_ctx *ctx) return -ENOMEM; } - rc = gen_pool_add(ctx->cb_va_pool, prop->cb_va_start_addr, - prop->cb_va_end_addr - prop->cb_va_start_addr, -1); + ctx->cb_va_pool_base = hl_reserve_va_block(hdev, ctx, HL_VA_RANGE_TYPE_HOST, + CB_VA_POOL_SIZE, HL_MMU_VA_ALIGNMENT_NOT_NEEDED); + if (!ctx->cb_va_pool_base) { + rc = -ENOMEM; + goto err_pool_destroy; + } + rc = gen_pool_add(ctx->cb_va_pool, ctx->cb_va_pool_base, CB_VA_POOL_SIZE, -1); if (rc) { dev_err(hdev->dev, "Failed to add memory to VA gen pool for CB mapping\n"); - goto err_pool_destroy; + goto err_unreserve_va_block; } return 0; +err_unreserve_va_block: + hl_unreserve_va_block(hdev, ctx, ctx->cb_va_pool_base, CB_VA_POOL_SIZE); err_pool_destroy: gen_pool_destroy(ctx->cb_va_pool); @@ -590,4 +599,5 @@ void hl_cb_va_pool_fini(struct hl_ctx *ctx) return; gen_pool_destroy(ctx->cb_va_pool); + hl_unreserve_va_block(hdev, ctx, ctx->cb_va_pool_base, CB_VA_POOL_SIZE); } diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 43b9427d9c97..d7fd4f57abf3 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -567,10 +567,6 @@ struct hl_hints_range { * @tpc_binning_mask: which TPCs are binned. 0 means usable and 1 means binned. * @dram_enabled_mask: which DRAMs are enabled. * @dram_binning_mask: which DRAMs are binned. 0 means usable, 1 means binned. - * @cb_va_start_addr: virtual start address of command buffers which are mapped - * to the device's MMU. - * @cb_va_end_addr: virtual end address of command buffers which are mapped to - * the device's MMU. * @dram_hints_align_mask: dram va hint addresses alignment mask which is used * for hints validity check. * @cfg_base_address: config space base address. @@ -713,8 +709,6 @@ struct asic_fixed_properties { u64 tpc_binning_mask; u64 dram_enabled_mask; u64 dram_binning_mask; - u64 cb_va_start_addr; - u64 cb_va_end_addr; u64 dram_hints_align_mask; u64 cfg_base_address; u64 mmu_cache_mng_addr; @@ -1803,6 +1797,7 @@ struct hl_cs_outcome_store { * @cb_va_pool: device VA pool for command buffers which are mapped to the * device's MMU. * @sig_mgr: encaps signals handle manager. + * @cb_va_pool_base: the base address for the device VA pool * @cs_sequence: sequence number for CS. Value is assigned to a CS and passed * to user so user could inquire about CS. It is used as * index to cs_pending array. @@ -1838,6 +1833,7 @@ struct hl_ctx { struct hl_cs_counters_atomic cs_counters; struct gen_pool *cb_va_pool; struct hl_encaps_signals_mgr sig_mgr; + u64 cb_va_pool_base; u64 cs_sequence; u64 *dram_default_hops; spinlock_t cs_lock; @@ -3600,7 +3596,7 @@ void hl_hw_block_mem_init(struct hl_ctx *ctx); void hl_hw_block_mem_fini(struct hl_ctx *ctx); u64 hl_reserve_va_block(struct hl_device *hdev, struct hl_ctx *ctx, - enum hl_va_range_type type, u32 size, u32 alignment); + enum hl_va_range_type type, u64 size, u32 alignment); int hl_unreserve_va_block(struct hl_device *hdev, struct hl_ctx *ctx, u64 start_addr, u64 size); int hl_pin_host_memory(struct hl_device *hdev, u64 addr, u64 size, diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c index e3b40dbf154c..0a653fff08d4 100644 --- a/drivers/misc/habanalabs/common/memory.c +++ b/drivers/misc/habanalabs/common/memory.c @@ -755,7 +755,7 @@ out: * - Return the start address of the virtual block. */ u64 hl_reserve_va_block(struct hl_device *hdev, struct hl_ctx *ctx, - enum hl_va_range_type type, u32 size, u32 alignment) + enum hl_va_range_type type, u64 size, u32 alignment) { return get_va_block(hdev, ctx->va_range[type], size, 0, max(alignment, ctx->va_range[type]->page_size), diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index c907e0fbf182..ff0f9e9db1b5 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -2022,9 +2022,6 @@ static int gaudi2_set_fixed_properties(struct hl_device *hdev) prop->server_type = HL_SERVER_TYPE_UNKNOWN; - prop->cb_va_start_addr = VA_HOST_SPACE_USER_MAPPED_CB_START; - prop->cb_va_end_addr = VA_HOST_SPACE_USER_MAPPED_CB_END; - prop->max_dec = NUMBER_OF_DEC; prop->clk_pll_index = HL_GAUDI2_MME_PLL; diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2P.h b/drivers/misc/habanalabs/gaudi2/gaudi2P.h index 347ea1dd78e2..9094a702678d 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2P.h +++ b/drivers/misc/habanalabs/gaudi2/gaudi2P.h @@ -139,9 +139,6 @@ #define VA_HOST_SPACE_HPAGE_START 0xFFF0800000000000ull #define VA_HOST_SPACE_HPAGE_END 0xFFF1000000000000ull /* 140TB */ -#define VA_HOST_SPACE_USER_MAPPED_CB_START 0xFFF1000000000000ull -#define VA_HOST_SPACE_USER_MAPPED_CB_END 0xFFF1000100000000ull /* 4GB */ - /* 140TB */ #define VA_HOST_SPACE_PAGE_SIZE (VA_HOST_SPACE_PAGE_END - VA_HOST_SPACE_PAGE_START) -- cgit v1.2.3 From c38f72370b615d48c7eb44389b229105f07a70e2 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Mon, 22 Aug 2022 10:59:34 +0300 Subject: habanalabs: perform context switch flow only if needed Except Goya, none of our ASICs require context switch flow, hence we enable this flow only where it is needed. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_submission.c | 10 ++++++---- drivers/misc/habanalabs/common/habanalabs.h | 2 ++ drivers/misc/habanalabs/goya/goya.c | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index cf4118515678..746b688d34cf 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -1590,13 +1590,14 @@ static int hl_cs_ctx_switch(struct hl_fpriv *hpriv, union hl_cs_args *args, struct hl_device *hdev = hpriv->hdev; struct hl_ctx *ctx = hpriv->ctx; bool need_soft_reset = false; - int rc = 0, do_ctx_switch; + int rc = 0, do_ctx_switch = 0; void __user *chunks; u32 num_chunks, tmp; u16 sob_count; int ret; - do_ctx_switch = atomic_cmpxchg(&ctx->thread_ctx_switch_token, 1, 0); + if (hdev->supports_ctx_switch) + do_ctx_switch = atomic_cmpxchg(&ctx->thread_ctx_switch_token, 1, 0); if (do_ctx_switch || (args->in.cs_flags & HL_CS_FLAGS_FORCE_RESTORE)) { mutex_lock(&hpriv->restore_phase_mutex); @@ -1667,9 +1668,10 @@ wait_again: } } - ctx->thread_ctx_switch_wait_token = 1; + if (hdev->supports_ctx_switch) + ctx->thread_ctx_switch_wait_token = 1; - } else if (!ctx->thread_ctx_switch_wait_token) { + } else if (hdev->supports_ctx_switch && !ctx->thread_ctx_switch_wait_token) { rc = hl_poll_timeout_memory(hdev, &ctx->thread_ctx_switch_wait_token, tmp, (tmp == 1), 100, jiffies_to_usecs(hdev->timeout_jiffies), false); diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index d7fd4f57abf3..33c6476b60a9 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -3199,6 +3199,7 @@ struct hl_reset_info { * Used only for testing. * @heartbeat: Controls if we want to enable the heartbeat mechanism vs. the f/w, which verifies * that the f/w is always alive. Used only for testing. + * @supports_ctx_switch: true if a ctx switch is required upon first submission. */ struct hl_device { struct pci_dev *pdev; @@ -3335,6 +3336,7 @@ struct hl_device { u8 compute_ctx_in_release; u8 supports_mmu_prefetch; u8 reset_upon_device_release; + u8 supports_ctx_switch; /* Parameters for bring-up */ u64 nic_ports_mask; diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 87465a28af0d..d8fb91d257b9 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -1025,6 +1025,7 @@ static int goya_sw_init(struct hl_device *hdev) hdev->asic_prop.supports_compute_reset = true; hdev->asic_prop.allow_inference_soft_reset = true; hdev->supports_wait_for_multi_cs = false; + hdev->supports_ctx_switch = true; hdev->asic_funcs->set_pci_memory_regions(hdev); -- cgit v1.2.3 From d155df4f628a5312a485235aa8cc5ba78e11ea65 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Tue, 23 Aug 2022 16:23:56 +0300 Subject: habanalabs: ignore EEPROM errors during boot EEPROM errors reported by firmware are basically warnings and should not fail the boot process. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/firmware_if.c | 9 +++++++++ drivers/misc/habanalabs/include/common/hl_boot_if.h | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index 12d0f18c1f6c..4ede4bb03e8e 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -573,6 +573,15 @@ static bool fw_report_boot_dev0(struct hl_device *hdev, u32 err_val, dev_dbg(hdev->dev, "Device status0 %#x\n", sts_val); /* All warnings should go here in order not to reach the unknown error validation */ + if (err_val & CPU_BOOT_ERR0_EEPROM_FAIL) { + dev_warn(hdev->dev, + "Device boot warning - EEPROM failure detected, default settings applied\n"); + /* This is a warning so we don't want it to disable the + * device + */ + err_val &= ~CPU_BOOT_ERR0_EEPROM_FAIL; + } + if (err_val & CPU_BOOT_ERR0_DRAM_SKIPPED) { dev_warn(hdev->dev, "Device boot warning - Skipped DRAM initialization\n"); diff --git a/drivers/misc/habanalabs/include/common/hl_boot_if.h b/drivers/misc/habanalabs/include/common/hl_boot_if.h index f2f6488de625..2e45be5de4fe 100644 --- a/drivers/misc/habanalabs/include/common/hl_boot_if.h +++ b/drivers/misc/habanalabs/include/common/hl_boot_if.h @@ -34,6 +34,7 @@ enum cpu_boot_err { CPU_BOOT_ERR_BINNING_FAIL = 19, CPU_BOOT_ERR_TPM_FAIL = 20, CPU_BOOT_ERR_TMP_THRESH_INIT_FAIL = 21, + CPU_BOOT_ERR_EEPROM_FAIL = 22, CPU_BOOT_ERR_ENABLED = 31, CPU_BOOT_ERR_SCND_EN = 63, CPU_BOOT_ERR_LAST = 64 /* we have 2 registers of 32 bits */ @@ -115,6 +116,9 @@ enum cpu_boot_err { * CPU_BOOT_ERR0_TMP_THRESH_INIT_FAIL Failed to set threshold for tmperature * sensor. * + * CPU_BOOT_ERR_EEPROM_FAIL Failed reading EEPROM data. Defaults + * are used. + * * CPU_BOOT_ERR0_ENABLED Error registers enabled. * This is a main indication that the * running FW populates the error @@ -139,6 +143,7 @@ enum cpu_boot_err { #define CPU_BOOT_ERR0_BINNING_FAIL (1 << CPU_BOOT_ERR_BINNING_FAIL) #define CPU_BOOT_ERR0_TPM_FAIL (1 << CPU_BOOT_ERR_TPM_FAIL) #define CPU_BOOT_ERR0_TMP_THRESH_INIT_FAIL (1 << CPU_BOOT_ERR_TMP_THRESH_INIT_FAIL) +#define CPU_BOOT_ERR0_EEPROM_FAIL (1 << CPU_BOOT_ERR_EEPROM_FAIL) #define CPU_BOOT_ERR0_ENABLED (1 << CPU_BOOT_ERR_ENABLED) #define CPU_BOOT_ERR1_ENABLED (1 << CPU_BOOT_ERR_ENABLED) -- cgit v1.2.3 From 988262ef2fb9b43719ce40af1efe9bfbc62b461c Mon Sep 17 00:00:00 2001 From: farah kassabri Date: Tue, 23 Aug 2022 17:41:52 +0300 Subject: habanalabs/gaudi2: log critical events with no rate limit When we have a storm of errors of HBM ECC SERR we can reach a situation where driver start hard reset flow without logging the error cause that caused the hard reset due to logs rate limiting. Signed-off-by: farah kassabri Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi2/gaudi2.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index ff0f9e9db1b5..6bebd5eb0294 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -8200,10 +8200,17 @@ static bool gaudi2_handle_hbm_mc_sei_err(struct hl_device *hdev, u16 event_type, return true; } - dev_err_ratelimited(hdev->dev, - "System Error Interrupt - HBM(%u) MC(%u) MC_CH(%u) MC_PC(%u). Critical(%u). Error cause: %s\n", - hbm_id, mc_id, sei_data->hdr.mc_channel, sei_data->hdr.mc_pseudo_channel, - sei_data->hdr.is_critical, hbm_mc_sei_cause[cause_idx]); + if (sei_data->hdr.is_critical) + dev_err(hdev->dev, + "System Critical Error Interrupt - HBM(%u) MC(%u) MC_CH(%u) MC_PC(%u). Error cause: %s\n", + hbm_id, mc_id, sei_data->hdr.mc_channel, sei_data->hdr.mc_pseudo_channel, + hbm_mc_sei_cause[cause_idx]); + + else + dev_err_ratelimited(hdev->dev, + "System Non-Critical Error Interrupt - HBM(%u) MC(%u) MC_CH(%u) MC_PC(%u). Error cause: %s\n", + hbm_id, mc_id, sei_data->hdr.mc_channel, sei_data->hdr.mc_pseudo_channel, + hbm_mc_sei_cause[cause_idx]); /* Print error-specific info */ switch (cause_idx) { -- cgit v1.2.3 From 0855bf8b17374fef702844664af70454fa6951ef Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Sun, 21 Aug 2022 13:50:51 +0300 Subject: habanalabs/gaudi2: dump detailed information upon RAZWI In order to improve debuggability, we add all available information when a RAZWI event occur. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi2/gaudi2.c | 206 ++++++++++++++++++++++++-------- 1 file changed, 155 insertions(+), 51 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index 6bebd5eb0294..4696da7a57c1 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -1531,17 +1531,57 @@ static const u32 rtr_coordinates_to_rtr_id[NUM_OF_RTR_PER_DCORE * NUM_OF_DCORES] RTR_ID_X_Y(17, 11) }; +enum rtr_id { + DCORE0_RTR0, + DCORE0_RTR1, + DCORE0_RTR2, + DCORE0_RTR3, + DCORE0_RTR4, + DCORE0_RTR5, + DCORE0_RTR6, + DCORE0_RTR7, + DCORE1_RTR0, + DCORE1_RTR1, + DCORE1_RTR2, + DCORE1_RTR3, + DCORE1_RTR4, + DCORE1_RTR5, + DCORE1_RTR6, + DCORE1_RTR7, + DCORE2_RTR0, + DCORE2_RTR1, + DCORE2_RTR2, + DCORE2_RTR3, + DCORE2_RTR4, + DCORE2_RTR5, + DCORE2_RTR6, + DCORE2_RTR7, + DCORE3_RTR0, + DCORE3_RTR1, + DCORE3_RTR2, + DCORE3_RTR3, + DCORE3_RTR4, + DCORE3_RTR5, + DCORE3_RTR6, + DCORE3_RTR7, +}; + static const u32 gaudi2_tpc_initiator_rtr_id[NUM_OF_TPC_PER_DCORE * NUM_OF_DCORES + 1] = { - 1, 1, 2, 2, 3, 3, 14, 14, 13, 13, 12, 12, 19, 19, 18, 18, 17, - 17, 28, 28, 29, 29, 30, 30, 0 + DCORE0_RTR1, DCORE0_RTR1, DCORE0_RTR2, DCORE0_RTR2, DCORE0_RTR3, DCORE0_RTR3, + DCORE1_RTR6, DCORE1_RTR6, DCORE1_RTR5, DCORE1_RTR5, DCORE1_RTR4, DCORE1_RTR4, + DCORE2_RTR3, DCORE2_RTR3, DCORE2_RTR2, DCORE2_RTR2, DCORE2_RTR1, DCORE2_RTR1, + DCORE3_RTR4, DCORE3_RTR4, DCORE3_RTR5, DCORE3_RTR5, DCORE3_RTR6, DCORE3_RTR6, + DCORE0_RTR0 }; static const u32 gaudi2_dec_initiator_rtr_id[NUMBER_OF_DEC] = { - 0, 0, 15, 15, 16, 16, 31, 31, 0, 0 + DCORE0_RTR0, DCORE0_RTR0, DCORE1_RTR7, DCORE1_RTR7, DCORE2_RTR0, DCORE2_RTR0, + DCORE3_RTR7, DCORE3_RTR7, DCORE0_RTR0, DCORE0_RTR0 }; static const u32 gaudi2_nic_initiator_rtr_id[NIC_NUMBER_OF_MACROS] = { - 15, 15, 15, 15, 15, 16, 16, 16, 16, 31, 31, 31 + DCORE1_RTR7, DCORE1_RTR7, DCORE1_RTR7, DCORE1_RTR7, DCORE1_RTR7, DCORE2_RTR0, + DCORE2_RTR0, DCORE2_RTR0, DCORE2_RTR0, DCORE3_RTR7, DCORE3_RTR7, DCORE3_RTR7 }; struct sft_info { @@ -1554,11 +1594,11 @@ static const struct sft_info gaudi2_edma_initiator_sft_id[NUM_OF_EDMA_PER_DCORE }; static const u32 gaudi2_pdma_initiator_rtr_id[NUM_OF_PDMA] = { - 0, 0 + DCORE0_RTR0, DCORE0_RTR0 }; static const u32 gaudi2_rot_initiator_rtr_id[NUM_OF_ROT] = { - 16, 31 + DCORE2_RTR0, DCORE3_RTR7 }; struct mme_initiators_rtr_id { @@ -7062,10 +7102,6 @@ static void gaudi2_razwi_rr_hbw_shared_printf_info(struct hl_device *hdev, razwi_lo = le32_to_cpu(razwi_info->hbw.rr_aw_razwi_lo_reg); razwi_xy = le32_to_cpu(razwi_info->hbw.rr_aw_razwi_id_reg); } - - dev_err_ratelimited(hdev->dev, - "%s-RAZWI SHARED RR HBW WR error, captured address HI 0x%x LO 0x%x, Initiator coordinates 0x%x\n", - name, razwi_hi, razwi_lo, razwi_xy); } else { if (read_razwi_regs) { razwi_hi = RREG32(rtr_mstr_if_base_addr + RR_SHRD_HBW_AR_RAZWI_HI); @@ -7076,11 +7112,11 @@ static void gaudi2_razwi_rr_hbw_shared_printf_info(struct hl_device *hdev, razwi_lo = le32_to_cpu(razwi_info->hbw.rr_ar_razwi_lo_reg); razwi_xy = le32_to_cpu(razwi_info->hbw.rr_ar_razwi_id_reg); } - - dev_err_ratelimited(hdev->dev, - "%s-RAZWI SHARED RR HBW AR error, captured address HI 0x%x LO 0x%x, Initiator coordinates 0x%x\n", - name, razwi_hi, razwi_lo, razwi_xy); } + + dev_err_ratelimited(hdev->dev, + "%s-RAZWI SHARED RR HBW %s error, address %#llx, Initiator coordinates 0x%x\n", + name, is_write ? "WR" : "RD", (u64)razwi_hi << 32 | razwi_lo, razwi_xy); } static void gaudi2_razwi_rr_lbw_shared_printf_info(struct hl_device *hdev, @@ -7338,7 +7374,79 @@ static void gaudi2_check_if_razwi_happened(struct hl_device *hdev) gaudi2_ack_module_razwi_event_handler(hdev, RAZWI_ROT, mod_idx, 0, NULL); } -static void gaudi2_razwi_unmapped_addr_hbw_printf_info(struct hl_device *hdev, +static const char *gaudi2_get_initiators_name(u32 rtr_id) +{ + switch (rtr_id) { + case DCORE0_RTR0: + return "DEC0/1/8/9, TPC24, PDMA0/1, PMMU, PCIE_IF, EDMA0/2, HMMU0/2/4/6, CPU"; + case DCORE0_RTR1: + return "TPC0/1"; + case DCORE0_RTR2: + return "TPC2/3"; + case DCORE0_RTR3: + return "TPC4/5"; + case DCORE0_RTR4: + return "MME0_SBTE0/1"; + case DCORE0_RTR5: + return "MME0_WAP0/SBTE2"; + case DCORE0_RTR6: + return "MME0_CTRL_WR/SBTE3"; + case DCORE0_RTR7: + return "MME0_WAP1/CTRL_RD/SBTE4"; + case DCORE1_RTR0: + return "MME1_WAP1/CTRL_RD/SBTE4"; + case DCORE1_RTR1: + return "MME1_CTRL_WR/SBTE3"; + case DCORE1_RTR2: + return "MME1_WAP0/SBTE2"; + case DCORE1_RTR3: + return "MME1_SBTE0/1"; + case DCORE1_RTR4: + return "TPC10/11"; + case DCORE1_RTR5: + return "TPC8/9"; + case DCORE1_RTR6: + return "TPC6/7"; + case DCORE1_RTR7: + return "DEC2/3, NIC0/1/2/3/4, ARC_FARM, KDMA, EDMA1/3, HMMU1/3/5/7"; + case DCORE2_RTR0: + return "DEC4/5, NIC5/6/7/8, EDMA4/6, HMMU8/10/12/14, ROT0"; + case DCORE2_RTR1: + return "TPC16/17"; + case DCORE2_RTR2: + return "TPC14/15"; + case DCORE2_RTR3: + return "TPC12/13"; + case DCORE2_RTR4: + return "MME2_SBTE0/1"; + case DCORE2_RTR5: + return "MME2_WAP0/SBTE2"; + case DCORE2_RTR6: + return "MME2_CTRL_WR/SBTE3"; + case DCORE2_RTR7: + return "MME2_WAP1/CTRL_RD/SBTE4"; + case DCORE3_RTR0: + return "MME3_WAP1/CTRL_RD/SBTE4"; + case DCORE3_RTR1: + return "MME3_CTRL_WR/SBTE3"; + case DCORE3_RTR2: + return "MME3_WAP0/SBTE2"; + case DCORE3_RTR3: + return "MME3_SBTE0/1"; + case DCORE3_RTR4: + return "TPC18/19"; + case DCORE3_RTR5: + return "TPC20/21"; + case DCORE3_RTR6: + return "TPC22/23"; + case DCORE3_RTR7: + return "DEC6/7, NIC9/10/11, EDMA5/7, HMMU9/11/13/15, ROT1, PSOC"; + default: + return "N/A"; + } +} + +static void gaudi2_razwi_unmapped_addr_hbw_printf_info(struct hl_device *hdev, u32 rtr_id, u64 rtr_ctrl_base_addr, bool is_write) { u32 razwi_hi, razwi_lo; @@ -7347,50 +7455,47 @@ static void gaudi2_razwi_unmapped_addr_hbw_printf_info(struct hl_device *hdev, razwi_hi = RREG32(rtr_ctrl_base_addr + DEC_RAZWI_HBW_AW_ADDR_HI); razwi_lo = RREG32(rtr_ctrl_base_addr + DEC_RAZWI_HBW_AW_ADDR_LO); - dev_err_ratelimited(hdev->dev, - "RAZWI PSOC unmapped HBW WR error, ctr_base 0x%llx, captured address HI 0x%x, LO 0x%x\n", - rtr_ctrl_base_addr, razwi_hi, razwi_lo); - /* Clear set indication */ WREG32(rtr_ctrl_base_addr + DEC_RAZWI_HBW_AW_SET, 0x1); } else { razwi_hi = RREG32(rtr_ctrl_base_addr + DEC_RAZWI_HBW_AR_ADDR_HI); - razwi_lo = RREG32(rtr_ctrl_base_addr + DEC_RAZWI_HBW_AR_ADDR_LO); - dev_err_ratelimited(hdev->dev, - "RAZWI PSOC unmapped HBW AR error, ctr_base 0x%llx, captured address HI 0x%x, LO 0x%x\n", - rtr_ctrl_base_addr, razwi_hi, razwi_lo); - /* Clear set indication */ WREG32(rtr_ctrl_base_addr + DEC_RAZWI_HBW_AR_SET, 0x1); } + + dev_err_ratelimited(hdev->dev, + "RAZWI PSOC unmapped HBW %s error, rtr id %u, address %#llx\n", + is_write ? "WR" : "RD", rtr_id, (u64)razwi_hi << 32 | razwi_lo); + + dev_err_ratelimited(hdev->dev, + "Initiators: %s\n", gaudi2_get_initiators_name(rtr_id)); } -static void gaudi2_razwi_unmapped_addr_lbw_printf_info(struct hl_device *hdev, - u64 rtr_ctrl_base_addr, bool is_write) +static void gaudi2_razwi_unmapped_addr_lbw_printf_info(struct hl_device *hdev, u32 rtr_id, + u64 rtr_ctrl_base_addr, bool is_write) { u32 razwi_addr; if (is_write) { razwi_addr = RREG32(rtr_ctrl_base_addr + DEC_RAZWI_LBW_AW_ADDR); - dev_err_ratelimited(hdev->dev, - "RAZWI PSOC unmapped LBW WR error, ctr_base 0x%llx, captured address 0x%x\n", - rtr_ctrl_base_addr, razwi_addr); - /* Clear set indication */ WREG32(rtr_ctrl_base_addr + DEC_RAZWI_LBW_AW_SET, 0x1); } else { razwi_addr = RREG32(rtr_ctrl_base_addr + DEC_RAZWI_LBW_AR_ADDR); - dev_err_ratelimited(hdev->dev, - "RAZWI PSOC unmapped LBW AR error, ctr_base 0x%llx, captured address 0x%x\n", - rtr_ctrl_base_addr, razwi_addr); - /* Clear set indication */ WREG32(rtr_ctrl_base_addr + DEC_RAZWI_LBW_AR_SET, 0x1); } + + dev_err_ratelimited(hdev->dev, + "RAZWI PSOC unmapped LBW %s error, rtr id %u, address %#x\n", + is_write ? "WR" : "RD", rtr_id, razwi_addr); + + dev_err_ratelimited(hdev->dev, + "Initiators: %s\n", gaudi2_get_initiators_name(rtr_id)); } /* PSOC RAZWI interrupt occurs only when trying to access a bad address */ @@ -7408,21 +7513,16 @@ static void gaudi2_ack_psoc_razwi_event_handler(struct hl_device *hdev) } razwi_mask_info = RREG32(mmPSOC_GLOBAL_CONF_RAZWI_MASK_INFO); - - xy = (razwi_mask_info & PSOC_GLOBAL_CONF_RAZWI_MASK_INFO_AXUSER_L_MASK) - >> PSOC_GLOBAL_CONF_RAZWI_MASK_INFO_AXUSER_L_SHIFT; + xy = FIELD_GET(PSOC_GLOBAL_CONF_RAZWI_MASK_INFO_AXUSER_L_MASK, razwi_mask_info); dev_err_ratelimited(hdev->dev, - "PSOC RAZWI interrupt: Mask %d, WAS_AR %d, WAS_AW %d, AXUSER_L 0x%x AXUSER_H 0x%x\n", - (razwi_mask_info & PSOC_GLOBAL_CONF_RAZWI_MASK_INFO_MASK_MASK) - >> PSOC_GLOBAL_CONF_RAZWI_MASK_INFO_MASK_SHIFT, - (razwi_mask_info & PSOC_GLOBAL_CONF_RAZWI_MASK_INFO_WAS_AR_MASK) - >> PSOC_GLOBAL_CONF_RAZWI_MASK_INFO_WAS_AR_SHIFT, - (razwi_mask_info & PSOC_GLOBAL_CONF_RAZWI_MASK_INFO_WAS_AW_MASK) - >> PSOC_GLOBAL_CONF_RAZWI_MASK_INFO_WAS_AW_SHIFT, xy, - (razwi_mask_info & - PSOC_GLOBAL_CONF_RAZWI_MASK_INFO_AXUSER_H_MASK) - >> PSOC_GLOBAL_CONF_RAZWI_MASK_INFO_AXUSER_H_SHIFT); + "PSOC RAZWI interrupt: Mask %d, AR %d, AW %d, AXUSER_L 0x%x AXUSER_H 0x%x\n", + FIELD_GET(PSOC_GLOBAL_CONF_RAZWI_MASK_INFO_MASK_MASK, razwi_mask_info), + FIELD_GET(PSOC_GLOBAL_CONF_RAZWI_MASK_INFO_WAS_AR_MASK, razwi_mask_info), + FIELD_GET(PSOC_GLOBAL_CONF_RAZWI_MASK_INFO_WAS_AW_MASK, razwi_mask_info), + xy, + FIELD_GET(PSOC_GLOBAL_CONF_RAZWI_MASK_INFO_AXUSER_H_MASK, razwi_mask_info)); + if (xy == 0) { dev_err_ratelimited(hdev->dev, "PSOC RAZWI interrupt: received event from 0 rtr coordinates\n"); @@ -7452,16 +7552,20 @@ static void gaudi2_ack_psoc_razwi_event_handler(struct hl_device *hdev) lbw_ar_set = RREG32(rtr_ctrl_base_addr + DEC_RAZWI_LBW_AR_SET); if (hbw_aw_set) - gaudi2_razwi_unmapped_addr_hbw_printf_info(hdev, rtr_ctrl_base_addr, true); + gaudi2_razwi_unmapped_addr_hbw_printf_info(hdev, rtr_id, + rtr_ctrl_base_addr, true); if (hbw_ar_set) - gaudi2_razwi_unmapped_addr_hbw_printf_info(hdev, rtr_ctrl_base_addr, false); + gaudi2_razwi_unmapped_addr_hbw_printf_info(hdev, rtr_id, + rtr_ctrl_base_addr, false); if (lbw_aw_set) - gaudi2_razwi_unmapped_addr_lbw_printf_info(hdev, rtr_ctrl_base_addr, true); + gaudi2_razwi_unmapped_addr_lbw_printf_info(hdev, rtr_id, + rtr_ctrl_base_addr, true); if (lbw_ar_set) - gaudi2_razwi_unmapped_addr_lbw_printf_info(hdev, rtr_ctrl_base_addr, false); + gaudi2_razwi_unmapped_addr_lbw_printf_info(hdev, rtr_id, + rtr_ctrl_base_addr, false); clear: /* Clear Interrupts only on pldm or if f/w doesn't handle interrupts */ -- cgit v1.2.3 From 4745b2f0d0d4b291ec69619c815f53fd8a968d9a Mon Sep 17 00:00:00 2001 From: farah kassabri Date: Wed, 17 Aug 2022 17:43:43 +0300 Subject: habanalabs: send device active message to f/w As part of the RAS that is done by the f/w, we should send a message to the f/w when a user either acquires or releases the device. Signed-off-by: farah kassabri Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/device.c | 2 ++ drivers/misc/habanalabs/common/firmware_if.c | 15 +++++++++++++++ drivers/misc/habanalabs/common/habanalabs.h | 3 +++ drivers/misc/habanalabs/common/habanalabs_drv.c | 2 ++ drivers/misc/habanalabs/gaudi/gaudi.c | 6 ++++++ drivers/misc/habanalabs/gaudi2/gaudi2.c | 12 ++++++++++++ drivers/misc/habanalabs/gaudi2/gaudi2P.h | 1 + drivers/misc/habanalabs/goya/goya.c | 6 ++++++ drivers/misc/habanalabs/include/common/cpucp_if.h | 11 +++++++++++ 9 files changed, 58 insertions(+) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index 230b7eeef962..d6df0bd55e9f 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -470,6 +470,8 @@ static int hl_device_release(struct inode *inode, struct file *filp) hdev->last_open_session_duration_jif = jiffies - hdev->last_successful_open_jif; + hdev->asic_funcs->send_device_activity(hdev, false); + return 0; } diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index 4ede4bb03e8e..cd2eb7e73be5 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -454,6 +454,21 @@ void hl_fw_cpu_accessible_dma_pool_free(struct hl_device *hdev, size_t size, size); } +int hl_fw_send_device_activity(struct hl_device *hdev, bool open) +{ + struct cpucp_packet pkt; + int rc; + + memset(&pkt, 0, sizeof(pkt)); + pkt.ctl = cpu_to_le32(CPUCP_PACKET_ACTIVE_STATUS_SET << CPUCP_PKT_CTL_OPCODE_SHIFT); + pkt.value = cpu_to_le64(open); + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL); + if (rc) + dev_err(hdev->dev, "failed to send device activity msg(%u)\n", open); + + return rc; +} + int hl_fw_send_heartbeat(struct hl_device *hdev) { struct cpucp_packet hb_pkt; diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 33c6476b60a9..c1bd82d4a83c 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -1528,6 +1528,7 @@ struct engines_data { * @access_dev_mem: access device memory * @set_dram_bar_base: set the base of the DRAM BAR * @set_engine_cores: set a config command to enigne cores + * @send_device_activity: indication to FW about device availability */ struct hl_asic_funcs { int (*early_init)(struct hl_device *hdev); @@ -1664,6 +1665,7 @@ struct hl_asic_funcs { u64 (*set_dram_bar_base)(struct hl_device *hdev, u64 addr); int (*set_engine_cores)(struct hl_device *hdev, u32 *core_ids, u32 num_cores, u32 core_command); + int (*send_device_activity)(struct hl_device *hdev, bool open); }; @@ -3715,6 +3717,7 @@ int hl_fw_dram_replaced_row_get(struct hl_device *hdev, struct cpucp_hbm_row_info *info); int hl_fw_dram_pending_row_get(struct hl_device *hdev, u32 *pend_rows_num); int hl_fw_cpucp_engine_core_asid_set(struct hl_device *hdev, u32 asid); +int hl_fw_send_device_activity(struct hl_device *hdev, bool open); int hl_pci_bars_map(struct hl_device *hdev, const char * const name[3], bool is_wc[3]); int hl_pci_elbi_read(struct hl_device *hdev, u64 addr, u32 *data); diff --git a/drivers/misc/habanalabs/common/habanalabs_drv.c b/drivers/misc/habanalabs/common/habanalabs_drv.c index e12148428731..849e54fe78a6 100644 --- a/drivers/misc/habanalabs/common/habanalabs_drv.c +++ b/drivers/misc/habanalabs/common/habanalabs_drv.c @@ -204,6 +204,8 @@ int hl_device_open(struct inode *inode, struct file *filp) goto out_err; } + rc = hdev->asic_funcs->send_device_activity(hdev, true); + list_add(&hpriv->dev_node, &hdev->fpriv_list); mutex_unlock(&hdev->fpriv_list_lock); diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 96020693ac29..87dbdbb220da 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -9132,6 +9132,11 @@ static void gaudi_add_device_attr(struct hl_device *hdev, struct attribute_group dev_vrm_attr_grp->attrs = gaudi_vrm_dev_attrs; } +static int gaudi_send_device_activity(struct hl_device *hdev, bool open) +{ + return 0; +} + static const struct hl_asic_funcs gaudi_funcs = { .early_init = gaudi_early_init, .early_fini = gaudi_early_fini, @@ -9224,6 +9229,7 @@ static const struct hl_asic_funcs gaudi_funcs = { .mmu_get_real_page_size = hl_mmu_get_real_page_size, .access_dev_mem = hl_access_dev_mem, .set_dram_bar_base = gaudi_set_hbm_bar_base, + .send_device_activity = gaudi_send_device_activity, }; /** diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index 4696da7a57c1..330869cb4c0b 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -10031,6 +10031,17 @@ static int gaudi2_get_monitor_dump(struct hl_device *hdev, void *data) return -EOPNOTSUPP; } +int gaudi2_send_device_activity(struct hl_device *hdev, bool open) +{ + struct gaudi2_device *gaudi2 = hdev->asic_specific; + + if (!(gaudi2->hw_cap_initialized & HW_CAP_CPU_Q) || hdev->fw_major_version < 37) + return 0; + + /* TODO: add check for FW version using minor ver once it's known */ + return hl_fw_send_device_activity(hdev, open); +} + static const struct hl_asic_funcs gaudi2_funcs = { .early_init = gaudi2_early_init, .early_fini = gaudi2_early_fini, @@ -10127,6 +10138,7 @@ static const struct hl_asic_funcs gaudi2_funcs = { .access_dev_mem = hl_access_dev_mem, .set_dram_bar_base = gaudi2_set_hbm_bar_base, .set_engine_cores = gaudi2_set_engine_cores, + .send_device_activity = gaudi2_send_device_activity, }; void gaudi2_set_asic_funcs(struct hl_device *hdev) diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2P.h b/drivers/misc/habanalabs/gaudi2/gaudi2P.h index 9094a702678d..a99c348bbf39 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2P.h +++ b/drivers/misc/habanalabs/gaudi2/gaudi2P.h @@ -553,5 +553,6 @@ void gaudi2_pb_print_security_errors(struct hl_device *hdev, u32 block_addr, u32 u32 offended_addr); int gaudi2_init_security(struct hl_device *hdev); void gaudi2_ack_protection_bits_errors(struct hl_device *hdev); +int gaudi2_send_device_activity(struct hl_device *hdev, bool open); #endif /* GAUDI2P_H_ */ diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index d8fb91d257b9..5ef9e3ca97a6 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -5420,6 +5420,11 @@ static int goya_scrub_device_dram(struct hl_device *hdev, u64 val) return -EOPNOTSUPP; } +static int goya_send_device_activity(struct hl_device *hdev, bool open) +{ + return 0; +} + static const struct hl_asic_funcs goya_funcs = { .early_init = goya_early_init, .early_fini = goya_early_fini, @@ -5512,6 +5517,7 @@ static const struct hl_asic_funcs goya_funcs = { .mmu_get_real_page_size = hl_mmu_get_real_page_size, .access_dev_mem = hl_access_dev_mem, .set_dram_bar_base = goya_set_ddr_bar_base, + .send_device_activity = goya_send_device_activity, }; /* diff --git a/drivers/misc/habanalabs/include/common/cpucp_if.h b/drivers/misc/habanalabs/include/common/cpucp_if.h index abf40e1c4965..b837bb1f4cd3 100644 --- a/drivers/misc/habanalabs/include/common/cpucp_if.h +++ b/drivers/misc/habanalabs/include/common/cpucp_if.h @@ -636,6 +636,10 @@ enum pq_init_status { * passes the max size it allows the CpuCP to write to the structure, to prevent * data corruption in case of mismatched driver/FW versions. * Relevant only to Gaudi. + * + * CPUCP_PACKET_ACTIVE_STATUS_SET - + * LKD sends FW indication whether device is free or in use, this indication is reported + * also to the BMC. */ enum cpucp_packet_id { @@ -691,6 +695,13 @@ enum cpucp_packet_id { CPUCP_PACKET_RESERVED4, /* not used */ CPUCP_PACKET_RESERVED5, /* not used */ CPUCP_PACKET_MONITOR_DUMP_GET, /* debugfs */ + CPUCP_PACKET_RESERVED6, /* not used */ + CPUCP_PACKET_RESERVED7, /* not used */ + CPUCP_PACKET_RESERVED8, /* not used */ + CPUCP_PACKET_RESERVED9, /* not used */ + CPUCP_PACKET_RESERVED10, /* not used */ + CPUCP_PACKET_ACTIVE_STATUS_SET, /* internal */ + CPUCP_PACKET_ID_MAX /* must be last */ }; #define CPUCP_PACKET_FENCE_VAL 0xFE8CE7A5 -- cgit v1.2.3 From f5ec364c9ecd1113492e15ceaafd4447f5836528 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Sun, 28 Aug 2022 12:46:27 +0300 Subject: habanalabs: send device activity in a proper context 'Device activity open packet' should be sent outside of mutex as there is no real necessity for a lock. In addition 'device activity close packet' should be sent upon an actual release of the device. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/device.c | 4 ++-- drivers/misc/habanalabs/common/habanalabs_drv.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index d6df0bd55e9f..5f6407ed3b04 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -363,6 +363,8 @@ static void hpriv_release(struct kref *ref) hdev = hpriv->hdev; + hdev->asic_funcs->send_device_activity(hdev, false); + put_pid(hpriv->taskpid); hl_debugfs_remove_file(hpriv); @@ -470,8 +472,6 @@ static int hl_device_release(struct inode *inode, struct file *filp) hdev->last_open_session_duration_jif = jiffies - hdev->last_successful_open_jif; - hdev->asic_funcs->send_device_activity(hdev, false); - return 0; } diff --git a/drivers/misc/habanalabs/common/habanalabs_drv.c b/drivers/misc/habanalabs/common/habanalabs_drv.c index 849e54fe78a6..fd9c8680f954 100644 --- a/drivers/misc/habanalabs/common/habanalabs_drv.c +++ b/drivers/misc/habanalabs/common/habanalabs_drv.c @@ -204,11 +204,11 @@ int hl_device_open(struct inode *inode, struct file *filp) goto out_err; } - rc = hdev->asic_funcs->send_device_activity(hdev, true); - list_add(&hpriv->dev_node, &hdev->fpriv_list); mutex_unlock(&hdev->fpriv_list_lock); + hdev->asic_funcs->send_device_activity(hdev, true); + hl_debugfs_add_file(hpriv); atomic_set(&hdev->last_error.cs_timeout.write_enable, 1); -- cgit v1.2.3 From 62adba0a55a7dc522f179b8ff8e0b3b7250c440f Mon Sep 17 00:00:00 2001 From: farah kassabri Date: Tue, 23 Aug 2022 14:32:42 +0300 Subject: habanalabs: fix possible hole in device va cb_map_mem() uses gen_pool_alloc() to get virtual address for mapping a CB. The mapping is done in chunks of page size, so if the CB size is larger, it is possible that the allocated virtual addresses won't be consecutive. User retrieves this device VA which returns the virtual address in the first va_block. If there is a "hole" in the virtual addresses, user can configure a HW block with a bad device VA. Signed-off-by: farah kassabri Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_buffer.c | 99 ++++--------------------- drivers/misc/habanalabs/common/habanalabs.h | 7 +- 2 files changed, 20 insertions(+), 86 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_buffer.c b/drivers/misc/habanalabs/common/command_buffer.c index c3e2568542a1..d16de18863ba 100644 --- a/drivers/misc/habanalabs/common/command_buffer.c +++ b/drivers/misc/habanalabs/common/command_buffer.c @@ -18,11 +18,7 @@ static int cb_map_mem(struct hl_ctx *ctx, struct hl_cb *cb) { struct hl_device *hdev = ctx->hdev; struct asic_fixed_properties *prop = &hdev->asic_prop; - struct hl_vm_va_block *va_block, *tmp; - dma_addr_t bus_addr; - u64 virt_addr; u32 page_size = prop->pmmu.page_size; - s32 offset; int rc; if (!hdev->supports_cb_mapping) { @@ -37,106 +33,45 @@ static int cb_map_mem(struct hl_ctx *ctx, struct hl_cb *cb) return -EINVAL; } - INIT_LIST_HEAD(&cb->va_block_list); - - for (bus_addr = cb->bus_address; - bus_addr < cb->bus_address + cb->size; - bus_addr += page_size) { - - virt_addr = (u64) gen_pool_alloc(ctx->cb_va_pool, page_size); - if (!virt_addr) { - dev_err(hdev->dev, - "Failed to allocate device virtual address for CB\n"); - rc = -ENOMEM; - goto err_va_pool_free; - } + if (cb->is_mmu_mapped) + return 0; - va_block = kzalloc(sizeof(*va_block), GFP_KERNEL); - if (!va_block) { - rc = -ENOMEM; - gen_pool_free(ctx->cb_va_pool, virt_addr, page_size); - goto err_va_pool_free; - } + cb->roundup_size = roundup(cb->size, page_size); - va_block->start = virt_addr; - va_block->end = virt_addr + page_size - 1; - va_block->size = page_size; - list_add_tail(&va_block->node, &cb->va_block_list); + cb->virtual_addr = (u64) gen_pool_alloc(ctx->cb_va_pool, cb->roundup_size); + if (!cb->virtual_addr) { + dev_err(hdev->dev, "Failed to allocate device virtual address for CB\n"); + return -ENOMEM; } mutex_lock(&ctx->mmu_lock); - - bus_addr = cb->bus_address; - offset = 0; - list_for_each_entry(va_block, &cb->va_block_list, node) { - rc = hl_mmu_map_page(ctx, va_block->start, bus_addr, - va_block->size, list_is_last(&va_block->node, - &cb->va_block_list)); - if (rc) { - dev_err(hdev->dev, "Failed to map VA %#llx to CB\n", - va_block->start); - goto err_va_umap; - } - - bus_addr += va_block->size; - offset += va_block->size; + rc = hl_mmu_map_contiguous(ctx, cb->virtual_addr, cb->bus_address, cb->roundup_size); + if (rc) { + dev_err(hdev->dev, "Failed to map VA %#llx to CB\n", cb->virtual_addr); + goto err_va_umap; } - rc = hl_mmu_invalidate_cache(hdev, false, MMU_OP_USERPTR | MMU_OP_SKIP_LOW_CACHE_INV); - mutex_unlock(&ctx->mmu_lock); cb->is_mmu_mapped = true; - return rc; err_va_umap: - list_for_each_entry(va_block, &cb->va_block_list, node) { - if (offset <= 0) - break; - hl_mmu_unmap_page(ctx, va_block->start, va_block->size, - offset <= va_block->size); - offset -= va_block->size; - } - - rc = hl_mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR); - mutex_unlock(&ctx->mmu_lock); - -err_va_pool_free: - list_for_each_entry_safe(va_block, tmp, &cb->va_block_list, node) { - gen_pool_free(ctx->cb_va_pool, va_block->start, va_block->size); - list_del(&va_block->node); - kfree(va_block); - } - + gen_pool_free(ctx->cb_va_pool, cb->virtual_addr, cb->roundup_size); return rc; } static void cb_unmap_mem(struct hl_ctx *ctx, struct hl_cb *cb) { struct hl_device *hdev = ctx->hdev; - struct hl_vm_va_block *va_block, *tmp; mutex_lock(&ctx->mmu_lock); - - list_for_each_entry(va_block, &cb->va_block_list, node) - if (hl_mmu_unmap_page(ctx, va_block->start, va_block->size, - list_is_last(&va_block->node, - &cb->va_block_list))) - dev_warn_ratelimited(hdev->dev, - "Failed to unmap CB's va 0x%llx\n", - va_block->start); - + hl_mmu_unmap_contiguous(ctx, cb->virtual_addr, cb->roundup_size); hl_mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR); - mutex_unlock(&ctx->mmu_lock); - list_for_each_entry_safe(va_block, tmp, &cb->va_block_list, node) { - gen_pool_free(ctx->cb_va_pool, va_block->start, va_block->size); - list_del(&va_block->node); - kfree(va_block); - } + gen_pool_free(ctx->cb_va_pool, cb->virtual_addr, cb->roundup_size); } static void cb_fini(struct hl_device *hdev, struct hl_cb *cb) @@ -378,7 +313,6 @@ int hl_cb_destroy(struct hl_mem_mgr *mmg, u64 cb_handle) static int hl_cb_info(struct hl_mem_mgr *mmg, u64 handle, u32 flags, u32 *usage_cnt, u64 *device_va) { - struct hl_vm_va_block *va_block; struct hl_cb *cb; int rc = 0; @@ -390,9 +324,8 @@ static int hl_cb_info(struct hl_mem_mgr *mmg, } if (flags & HL_CB_FLAGS_GET_DEVICE_VA) { - va_block = list_first_entry(&cb->va_block_list, struct hl_vm_va_block, node); - if (va_block) { - *device_va = va_block->start; + if (cb->is_mmu_mapped) { + *device_va = cb->virtual_addr; } else { dev_err(mmg->dev, "CB is not mapped to the device's MMU\n"); rc = -EINVAL; diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index c1bd82d4a83c..b7e01651d429 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -917,11 +917,11 @@ struct hl_mmap_mem_buf { * @buf: back pointer to the parent mappable memory buffer * @debugfs_list: node in debugfs list of command buffers. * @pool_list: node in pool list of command buffers. - * @va_block_list: list of virtual addresses blocks of the CB if it is mapped to - * the device's MMU. * @kernel_address: Holds the CB's kernel virtual address. + * @virtual_addr: Holds the CB's virtual address. * @bus_address: Holds the CB's DMA address. * @size: holds the CB's size. + * @roundup_size: holds the cb size after roundup to page size. * @cs_cnt: holds number of CS that this CB participates in. * @is_pool: true if CB was acquired from the pool, false otherwise. * @is_internal: internally allocated @@ -933,10 +933,11 @@ struct hl_cb { struct hl_mmap_mem_buf *buf; struct list_head debugfs_list; struct list_head pool_list; - struct list_head va_block_list; void *kernel_address; + u64 virtual_addr; dma_addr_t bus_address; u32 size; + u32 roundup_size; atomic_t cs_cnt; u8 is_pool; u8 is_internal; -- cgit v1.2.3 From aee3fd74fe579b6de5d8661dac559df91ab36b12 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Tue, 30 Aug 2022 15:07:51 +0300 Subject: habanalabs/gaudi: rename mme cfg error response print Current description is misleading hence we rename it to a more suitable error description. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi2/gaudi2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index 330869cb4c0b..a0b15b2f2ea4 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -616,7 +616,7 @@ static const char * const guadi2_mme_error_cause[GAUDI2_NUM_OF_MME_ERR_CAUSE] = "qman_axi_err", "wap sei (wbc axi err)", "arc sei", - "mme_cfg_unalign_addr", + "cfg access error", "qm_sw_err", "sbte_dbg_intr_0", "sbte_dbg_intr_1", -- cgit v1.2.3 From a0fc8688c003172455f99b6b7e185b167ed964a0 Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Thu, 1 Sep 2022 14:12:56 +0300 Subject: habanalabs/gaudi2: read F/W security indication after hard reset F/W security status might change after every reset. Add the reading of the preboot status to the hard reset sequence, which among others reads this security indication. As this preboot status reading includes the waiting for the preboot to be ready, it can be removed from the CPU init which is done in a later stage. Signed-off-by: Tomer Tayar Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/device.c | 7 +++++++ drivers/misc/habanalabs/common/firmware_if.c | 7 ------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index 5f6407ed3b04..cc392d062f0d 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -1563,6 +1563,13 @@ kill_processes: */ hdev->disabled = false; + /* F/W security enabled indication might be updated after hard-reset */ + if (hard_reset) { + rc = hl_fw_read_preboot_status(hdev); + if (rc) + goto out_err; + } + rc = hdev->asic_funcs->hw_init(hdev); if (rc) { dev_err(hdev->dev, "failed to initialize the H/W after reset\n"); diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index cd2eb7e73be5..8bfb459a8282 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -2509,13 +2509,6 @@ static int hl_fw_dynamic_init_cpu(struct hl_device *hdev, */ dyn_regs = &fw_loader->dynamic_loader.comm_desc.cpu_dyn_regs; - /* if no preboot loaded indication- wait for preboot */ - if (!(hdev->fw_loader.fw_comp_loaded & FW_TYPE_PREBOOT_CPU)) { - rc = hl_fw_wait_preboot_ready(hdev); - if (rc) - return -EIO; - } - rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, COMMS_RST_STATE, 0, true, fw_loader->cpu_timeout); -- cgit v1.2.3 From 0626fa1a4d311b55b5f20a90380915f1bc135607 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Tue, 23 Aug 2022 16:58:38 +0300 Subject: habanalabs: add support for new cpucp return codes Firmware now responds with a more detailed cpucp return codes. Driver can now distinguish between error and debug return codes. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/firmware_if.c | 34 +++++++++++++++++++++-- drivers/misc/habanalabs/common/habanalabs.h | 2 ++ drivers/misc/habanalabs/gaudi2/gaudi2.c | 2 ++ drivers/misc/habanalabs/include/common/cpucp_if.h | 17 +++++++++++- 4 files changed, 51 insertions(+), 4 deletions(-) diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index 8bfb459a8282..c2375917fc02 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -252,7 +252,7 @@ int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg, struct cpucp_packet *pkt; dma_addr_t pkt_dma_addr; struct hl_bd *sent_bd; - u32 tmp, expected_ack_val, pi; + u32 tmp, expected_ack_val, pi, opcode; int rc; pkt = hl_cpu_accessible_dma_pool_alloc(hdev, len, &pkt_dma_addr); @@ -319,8 +319,35 @@ int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg, rc = (tmp & CPUCP_PKT_CTL_RC_MASK) >> CPUCP_PKT_CTL_RC_SHIFT; if (rc) { - dev_dbg(hdev->dev, "F/W ERROR %d for CPU packet %d\n", - rc, (tmp & CPUCP_PKT_CTL_OPCODE_MASK) >> CPUCP_PKT_CTL_OPCODE_SHIFT); + opcode = (tmp & CPUCP_PKT_CTL_OPCODE_MASK) >> CPUCP_PKT_CTL_OPCODE_SHIFT; + + if (!prop->supports_advanced_cpucp_rc) { + dev_dbg(hdev->dev, "F/W ERROR %d for CPU packet %d\n", rc, opcode); + goto scrub_descriptor; + } + + switch (rc) { + case cpucp_packet_invalid: + dev_err(hdev->dev, + "CPU packet %d is not supported by F/W\n", opcode); + break; + case cpucp_packet_fault: + dev_err(hdev->dev, + "F/W failed processing CPU packet %d\n", opcode); + break; + case cpucp_packet_invalid_pkt: + dev_dbg(hdev->dev, + "CPU packet %d is not supported by F/W\n", opcode); + break; + case cpucp_packet_invalid_params: + dev_err(hdev->dev, + "F/W reports invalid parameters for CPU packet %d\n", opcode); + break; + + default: + dev_err(hdev->dev, + "Unknown F/W ERROR %d for CPU packet %d\n", rc, opcode); + } /* propagate the return code from the f/w to the callers who want to check it */ if (result) @@ -332,6 +359,7 @@ int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg, *result = le64_to_cpu(pkt->result); } +scrub_descriptor: /* Scrub previous buffer descriptor 'ctl' field which contains the * previous PI value written during packet submission. * We must do this or else F/W can read an old value upon queue wraparound. diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index b7e01651d429..959e3616cc2f 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -678,6 +678,7 @@ struct hl_hints_range { * @set_max_power_on_device_init: true if need to set max power in F/W on device init. * @supports_user_set_page_size: true if user can set the allocation page size. * @dma_mask: the dma mask to be set for this device + * @supports_advanced_cpucp_rc: true if new cpucp opcodes are supported. */ struct asic_fixed_properties { struct hw_queue_properties *hw_queues_props; @@ -785,6 +786,7 @@ struct asic_fixed_properties { u8 set_max_power_on_device_init; u8 supports_user_set_page_size; u8 dma_mask; + u8 supports_advanced_cpucp_rc; }; /** diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index a0b15b2f2ea4..db18e066509c 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -2721,6 +2721,8 @@ static int gaudi2_late_init(struct hl_device *hdev) struct gaudi2_device *gaudi2 = hdev->asic_specific; int rc; + hdev->asic_prop.supports_advanced_cpucp_rc = true; + rc = hl_fw_send_pci_access_msg(hdev, CPUCP_PACKET_ENABLE_PCI_ACCESS, gaudi2->virt_msix_db_dma_addr); if (rc) { diff --git a/drivers/misc/habanalabs/include/common/cpucp_if.h b/drivers/misc/habanalabs/include/common/cpucp_if.h index b837bb1f4cd3..9593d1a26945 100644 --- a/drivers/misc/habanalabs/include/common/cpucp_if.h +++ b/drivers/misc/habanalabs/include/common/cpucp_if.h @@ -824,10 +824,25 @@ enum cpucp_led_index { CPUCP_LED2_INDEX }; +/* + * enum cpucp_packet_rc - Error return code + * @cpucp_packet_success -> in case of success. + * @cpucp_packet_invalid -> this is to support Goya and Gaudi platform. + * @cpucp_packet_fault -> in case of processing error like failing to + * get device binding or semaphore etc. + * @cpucp_packet_invalid_pkt -> when cpucp packet is un-supported. This is + * supported Greco onwards. + * @cpucp_packet_invalid_params -> when checking parameter like length of buffer + * or attribute value etc. Supported Greco onwards. + * @cpucp_packet_rc_max -> It indicates size of enum so should be at last. + */ enum cpucp_packet_rc { cpucp_packet_success, cpucp_packet_invalid, - cpucp_packet_fault + cpucp_packet_fault, + cpucp_packet_invalid_pkt, + cpucp_packet_invalid_params, + cpucp_packet_rc_max }; /* -- cgit v1.2.3 From 76925f55c9ba46faaf4054a8192dc5814bc2e0ab Mon Sep 17 00:00:00 2001 From: Ohad Sharabi Date: Thu, 1 Sep 2022 16:37:08 +0300 Subject: habanalabs: fix resetting the DRAM BAR Current code does not takes into account the new DRAM region base and so calculated address is wrong and can lead to crush. Signed-off-by: Ohad Sharabi Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/device.c | 41 ++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index cc392d062f0d..c6a00bb259fb 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -28,8 +28,9 @@ enum dma_alloc_type { /* * hl_set_dram_bar- sets the bar to allow later access to address * - * @hdev: pointer to habanalabs device structure + * @hdev: pointer to habanalabs device structure. * @addr: the address the caller wants to access. + * @region: the PCI region. * * @return: the old BAR base address on success, U64_MAX for failure. * The caller should set it back to the old address after use. @@ -39,10 +40,10 @@ enum dma_alloc_type { * This function can be called also if the bar doesn't need to be set, * in that case it just won't change the base. */ -static uint64_t hl_set_dram_bar(struct hl_device *hdev, u64 addr) +static u64 hl_set_dram_bar(struct hl_device *hdev, u64 addr, struct pci_mem_region *region) { struct asic_fixed_properties *prop = &hdev->asic_prop; - u64 bar_base_addr; + u64 bar_base_addr, old_base; if (is_power_of_2(prop->dram_pci_bar_size)) bar_base_addr = addr & ~(prop->dram_pci_bar_size - 0x1ull); @@ -50,51 +51,53 @@ static uint64_t hl_set_dram_bar(struct hl_device *hdev, u64 addr) bar_base_addr = DIV_ROUND_DOWN_ULL(addr, prop->dram_pci_bar_size) * prop->dram_pci_bar_size; - return hdev->asic_funcs->set_dram_bar_base(hdev, bar_base_addr); -} + old_base = hdev->asic_funcs->set_dram_bar_base(hdev, bar_base_addr); + /* in case of success we need to update the new BAR base */ + if (old_base != U64_MAX) + region->region_base = bar_base_addr; + + return old_base; +} static int hl_access_sram_dram_region(struct hl_device *hdev, u64 addr, u64 *val, enum debugfs_access_type acc_type, enum pci_region region_type) { struct pci_mem_region *region = &hdev->pci_mem_region[region_type]; + void __iomem *acc_addr; u64 old_base = 0, rc; if (region_type == PCI_REGION_DRAM) { - old_base = hl_set_dram_bar(hdev, addr); + old_base = hl_set_dram_bar(hdev, addr, region); if (old_base == U64_MAX) return -EIO; } + acc_addr = hdev->pcie_bar[region->bar_id] + addr - region->region_base + + region->offset_in_bar; switch (acc_type) { case DEBUGFS_READ8: - *val = readb(hdev->pcie_bar[region->bar_id] + - addr - region->region_base + region->offset_in_bar); + *val = readb(acc_addr); break; case DEBUGFS_WRITE8: - writeb(*val, hdev->pcie_bar[region->bar_id] + - addr - region->region_base + region->offset_in_bar); + writeb(*val, acc_addr); break; case DEBUGFS_READ32: - *val = readl(hdev->pcie_bar[region->bar_id] + - addr - region->region_base + region->offset_in_bar); + *val = readl(acc_addr); break; case DEBUGFS_WRITE32: - writel(*val, hdev->pcie_bar[region->bar_id] + - addr - region->region_base + region->offset_in_bar); + writel(*val, acc_addr); break; case DEBUGFS_READ64: - *val = readq(hdev->pcie_bar[region->bar_id] + - addr - region->region_base + region->offset_in_bar); + *val = readq(acc_addr); break; case DEBUGFS_WRITE64: - writeq(*val, hdev->pcie_bar[region->bar_id] + - addr - region->region_base + region->offset_in_bar); + writeq(*val, acc_addr); break; } if (region_type == PCI_REGION_DRAM) { - rc = hl_set_dram_bar(hdev, old_base); + rc = hl_set_dram_bar(hdev, old_base, region); if (rc == U64_MAX) return -EIO; } -- cgit v1.2.3 From c833ac1a5f34a21e9e9f8605b2f3f9f8dcaab6a0 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Sun, 4 Sep 2022 10:39:27 +0300 Subject: habanalabs/gaudi2: free event irq if init fails In case initialization fails after event irq was requested, we need to release that irq. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi2/gaudi2.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index db18e066509c..60694b8ed6fe 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -3581,7 +3581,7 @@ static int gaudi2_enable_msix(struct hl_device *hdev) rc = gaudi2_dec_enable_msix(hdev); if (rc) { dev_err(hdev->dev, "Failed to enable decoder IRQ"); - goto free_completion_irq; + goto free_event_irq; } for (i = GAUDI2_IRQ_NUM_USER_FIRST, j = prop->user_dec_intr_count, user_irq_init_cnt = 0; @@ -3612,6 +3612,10 @@ free_user_irq: gaudi2_dec_disable_msix(hdev, GAUDI2_IRQ_NUM_SHARED_DEC1_ABNRM + 1); +free_event_irq: + irq = pci_irq_vector(hdev->pdev, GAUDI2_IRQ_NUM_EVENT_QUEUE); + free_irq(irq, cq); + free_completion_irq: irq = pci_irq_vector(hdev->pdev, GAUDI2_IRQ_NUM_COMPLETION); free_irq(irq, cq); -- cgit v1.2.3 From 6f0818c9fc9b81d8a303a8d3fb1826d71777f7ed Mon Sep 17 00:00:00 2001 From: Tal Cohen Date: Thu, 18 Aug 2022 12:54:23 +0300 Subject: habanalabs: new notifier events for device state Add new notifier events that inform several device states. General H/W error raised on device general H/W error occurs. User engine error is raised when a device engine informs of an error. Signed-off-by: Tal Cohen Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi/gaudi.c | 39 +++++++++++++++++++++--- drivers/misc/habanalabs/gaudi2/gaudi2.c | 54 ++++++++++++++++++++++++++++++++- include/uapi/misc/habanalabs.h | 4 +++ 3 files changed, 91 insertions(+), 6 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 87dbdbb220da..2b328cb62096 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -7685,6 +7685,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr 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); + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; fw_fatal_err_flag = HL_DRV_RESET_FW_FATAL_ERR; goto reset_device; @@ -7694,6 +7695,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr case GAUDI_EVENT_PLL0 ... GAUDI_EVENT_PLL17: gaudi_print_irq_info(hdev, event_type, false); fw_fatal_err_flag = HL_DRV_RESET_FW_FATAL_ERR; + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; goto reset_device; case GAUDI_EVENT_HBM0_SPI_0: @@ -7705,6 +7707,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr gaudi_hbm_event_to_dev(event_type), &eq_entry->hbm_ecc_data); fw_fatal_err_flag = HL_DRV_RESET_FW_FATAL_ERR; + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; goto reset_device; case GAUDI_EVENT_HBM0_SPI_1: @@ -7716,6 +7719,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr gaudi_hbm_event_to_dev(event_type), &eq_entry->hbm_ecc_data); hl_fw_unmask_irq(hdev, event_type); + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; case GAUDI_EVENT_TPC0_DEC: @@ -7730,6 +7734,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr reset_required = gaudi_tpc_read_interrupts(hdev, tpc_dec_event_to_tpc_id(event_type), "AXI_SLV_DEC_Error"); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; if (reset_required) { dev_err(hdev->dev, "reset required due to %s\n", gaudi_irq_map_table[event_type].name); @@ -7738,6 +7743,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr goto reset_device; } else { hl_fw_unmask_irq(hdev, event_type); + event_mask |= HL_NOTIFIER_EVENT_DEVICE_RESET; } break; @@ -7753,6 +7759,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr reset_required = gaudi_tpc_read_interrupts(hdev, tpc_krn_event_to_tpc_id(event_type), "KRN_ERR"); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; if (reset_required) { dev_err(hdev->dev, "reset required due to %s\n", gaudi_irq_map_table[event_type].name); @@ -7761,6 +7768,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr goto reset_device; } else { hl_fw_unmask_irq(hdev, event_type); + event_mask |= HL_NOTIFIER_EVENT_DEVICE_RESET; } break; @@ -7789,9 +7797,25 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr gaudi_print_irq_info(hdev, event_type, true); gaudi_handle_ecc_event(hdev, event_type, &eq_entry->ecc_data); hl_fw_unmask_irq(hdev, event_type); + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; case GAUDI_EVENT_PCIE_DEC: + case GAUDI_EVENT_CPU_AXI_SPLITTER: + case GAUDI_EVENT_PSOC_AXI_DEC: + case GAUDI_EVENT_PSOC_PRSTN_FALL: + gaudi_print_irq_info(hdev, event_type, true); + hl_fw_unmask_irq(hdev, event_type); + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; + break; + + case GAUDI_EVENT_MMU_PAGE_FAULT: + case GAUDI_EVENT_MMU_WR_PERM: + gaudi_print_irq_info(hdev, event_type, true); + hl_fw_unmask_irq(hdev, event_type); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; + break; + case GAUDI_EVENT_MME0_WBC_RSP: case GAUDI_EVENT_MME0_SBAB0_RSP: case GAUDI_EVENT_MME1_WBC_RSP: @@ -7800,11 +7824,6 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr case GAUDI_EVENT_MME2_SBAB0_RSP: case GAUDI_EVENT_MME3_WBC_RSP: case GAUDI_EVENT_MME3_SBAB0_RSP: - case GAUDI_EVENT_CPU_AXI_SPLITTER: - case GAUDI_EVENT_PSOC_AXI_DEC: - case GAUDI_EVENT_PSOC_PRSTN_FALL: - case GAUDI_EVENT_MMU_PAGE_FAULT: - case GAUDI_EVENT_MMU_WR_PERM: case GAUDI_EVENT_RAZWI_OR_ADC: case GAUDI_EVENT_MME0_QM ... GAUDI_EVENT_MME2_QM: case GAUDI_EVENT_DMA0_QM ... GAUDI_EVENT_DMA7_QM: @@ -7824,10 +7843,12 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr gaudi_print_irq_info(hdev, event_type, true); gaudi_handle_qman_err(hdev, event_type, &event_mask); hl_fw_unmask_irq(hdev, event_type); + event_mask |= (HL_NOTIFIER_EVENT_USER_ENGINE_ERR | HL_NOTIFIER_EVENT_DEVICE_RESET); break; case GAUDI_EVENT_RAZWI_OR_ADC_SW: gaudi_print_irq_info(hdev, event_type, true); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; goto reset_device; case GAUDI_EVENT_TPC0_BMON_SPMU: @@ -7841,11 +7862,13 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr case GAUDI_EVENT_DMA_BM_CH0 ... GAUDI_EVENT_DMA_BM_CH7: gaudi_print_irq_info(hdev, event_type, false); hl_fw_unmask_irq(hdev, event_type); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; break; case GAUDI_EVENT_NIC_SEI_0 ... GAUDI_EVENT_NIC_SEI_4: gaudi_print_nic_axi_irq_info(hdev, event_type, &data); hl_fw_unmask_irq(hdev, event_type); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; break; case GAUDI_EVENT_DMA_IF_SEI_0 ... GAUDI_EVENT_DMA_IF_SEI_3: @@ -7853,6 +7876,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr gaudi_print_sm_sei_info(hdev, event_type, &eq_entry->sm_sei_data); rc = hl_state_dump(hdev); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; if (rc) dev_err(hdev->dev, "Error during system state dump %d\n", rc); @@ -7863,6 +7887,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr break; case GAUDI_EVENT_FIX_POWER_ENV_S ... GAUDI_EVENT_FIX_THERMAL_ENV_E: + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; gaudi_print_clk_change_info(hdev, event_type); hl_fw_unmask_irq(hdev, event_type); break; @@ -7872,20 +7897,24 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr dev_err(hdev->dev, "Received high temp H/W interrupt %d (cause %d)\n", event_type, cause); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; break; case GAUDI_EVENT_DEV_RESET_REQ: gaudi_print_irq_info(hdev, event_type, false); + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; goto reset_device; case GAUDI_EVENT_PKT_QUEUE_OUT_SYNC: gaudi_print_irq_info(hdev, event_type, false); gaudi_print_out_of_sync_info(hdev, &eq_entry->pkt_sync_err); + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; goto reset_device; case GAUDI_EVENT_FW_ALIVE_S: gaudi_print_irq_info(hdev, event_type, false); gaudi_print_fw_alive_info(hdev, &eq_entry->fw_alive); + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; goto reset_device; default: diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index 60694b8ed6fe..f749f7377ea6 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -8530,6 +8530,7 @@ static void gaudi2_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_ent struct gaudi2_device *gaudi2 = hdev->asic_specific; bool reset_required = false, skip_reset = false; int index, sbte_index; + u64 event_mask = 0; u16 event_type; ctl = le32_to_cpu(eq_entry->hdr.ctl); @@ -8551,6 +8552,7 @@ static void gaudi2_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_ent fallthrough; case GAUDI2_EVENT_ROTATOR0_SERR ... GAUDI2_EVENT_ROTATOR1_DERR: reset_flags |= HL_DRV_RESET_FW_FATAL_ERR; + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; reset_required = gaudi2_handle_ecc_event(hdev, event_type, &eq_entry->ecc_data); break; @@ -8560,21 +8562,25 @@ static void gaudi2_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_ent fallthrough; case GAUDI2_EVENT_NIC0_QM0 ... GAUDI2_EVENT_NIC11_QM1: gaudi2_handle_qman_err(hdev, event_type); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; break; case GAUDI2_EVENT_ARC_AXI_ERROR_RESPONSE_0: reset_flags |= HL_DRV_RESET_FW_FATAL_ERR; gaudi2_handle_arc_farm_sei_err(hdev); + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; case GAUDI2_EVENT_CPU_AXI_ERR_RSP: gaudi2_handle_cpu_sei_err(hdev); + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; case GAUDI2_EVENT_PDMA_CH0_AXI_ERR_RSP: case GAUDI2_EVENT_PDMA_CH1_AXI_ERR_RSP: reset_flags |= HL_DRV_RESET_FW_FATAL_ERR; gaudi2_handle_qm_sei_err(hdev, event_type, &eq_entry->razwi_info); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; break; case GAUDI2_EVENT_ROTATOR0_AXI_ERROR_RESPONSE: @@ -8582,6 +8588,7 @@ static void gaudi2_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_ent index = event_type - GAUDI2_EVENT_ROTATOR0_AXI_ERROR_RESPONSE; gaudi2_handle_rot_err(hdev, index, &eq_entry->razwi_with_intr_cause); gaudi2_handle_qm_sei_err(hdev, event_type, NULL); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; break; case GAUDI2_EVENT_TPC0_AXI_ERR_RSP ... GAUDI2_EVENT_TPC24_AXI_ERR_RSP: @@ -8589,11 +8596,13 @@ static void gaudi2_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_ent gaudi2_tpc_ack_interrupts(hdev, index, "AXI_ERR_RSP", &eq_entry->razwi_with_intr_cause); gaudi2_handle_qm_sei_err(hdev, event_type, NULL); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; break; case GAUDI2_EVENT_DEC0_AXI_ERR_RSPONSE ... GAUDI2_EVENT_DEC9_AXI_ERR_RSPONSE: index = event_type - GAUDI2_EVENT_DEC0_AXI_ERR_RSPONSE; gaudi2_handle_dec_err(hdev, index, "AXI_ERR_RESPONSE", &eq_entry->razwi_info); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; break; case GAUDI2_EVENT_TPC0_KERNEL_ERR: @@ -8624,6 +8633,7 @@ static void gaudi2_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_ent index = (event_type - GAUDI2_EVENT_TPC0_KERNEL_ERR) / (GAUDI2_EVENT_TPC1_KERNEL_ERR - GAUDI2_EVENT_TPC0_KERNEL_ERR); gaudi2_tpc_ack_interrupts(hdev, index, "KRN_ERR", &eq_entry->razwi_with_intr_cause); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; break; case GAUDI2_EVENT_DEC0_SPI: @@ -8639,6 +8649,7 @@ static void gaudi2_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_ent index = (event_type - GAUDI2_EVENT_DEC0_SPI) / (GAUDI2_EVENT_DEC1_SPI - GAUDI2_EVENT_DEC0_SPI); gaudi2_handle_dec_err(hdev, index, "SPI", &eq_entry->razwi_info); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; break; case GAUDI2_EVENT_MME0_CTRL_AXI_ERROR_RESPONSE: @@ -8651,6 +8662,7 @@ static void gaudi2_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_ent gaudi2_handle_mme_err(hdev, index, "CTRL_AXI_ERROR_RESPONSE", &eq_entry->razwi_info); gaudi2_handle_qm_sei_err(hdev, event_type, NULL); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; break; case GAUDI2_EVENT_MME0_QMAN_SW_ERROR: @@ -8661,6 +8673,7 @@ static void gaudi2_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_ent (GAUDI2_EVENT_MME1_QMAN_SW_ERROR - GAUDI2_EVENT_MME0_QMAN_SW_ERROR); gaudi2_handle_mme_err(hdev, index, "QMAN_SW_ERROR", &eq_entry->razwi_info); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; break; case GAUDI2_EVENT_MME0_WAP_SOURCE_RESULT_INVALID: @@ -8671,50 +8684,58 @@ static void gaudi2_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_ent (GAUDI2_EVENT_MME1_WAP_SOURCE_RESULT_INVALID - GAUDI2_EVENT_MME0_WAP_SOURCE_RESULT_INVALID); gaudi2_handle_mme_wap_err(hdev, index, &eq_entry->razwi_info); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; break; case GAUDI2_EVENT_KDMA_CH0_AXI_ERR_RSP: case GAUDI2_EVENT_KDMA0_CORE: gaudi2_handle_kdma_core_event(hdev, le64_to_cpu(eq_entry->intr_cause.intr_cause_data)); + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; case GAUDI2_EVENT_HDMA2_CORE ... GAUDI2_EVENT_PDMA1_CORE: gaudi2_handle_dma_core_event(hdev, le64_to_cpu(eq_entry->intr_cause.intr_cause_data)); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; break; case GAUDI2_EVENT_PCIE_ADDR_DEC_ERR: gaudi2_print_pcie_addr_dec_info(hdev, le64_to_cpu(eq_entry->intr_cause.intr_cause_data)); reset_flags |= HL_DRV_RESET_FW_FATAL_ERR; + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; case GAUDI2_EVENT_HMMU0_PAGE_FAULT_OR_WR_PERM ... GAUDI2_EVENT_HMMU12_SECURITY_ERROR: case GAUDI2_EVENT_HMMU_0_AXI_ERR_RSP ... GAUDI2_EVENT_HMMU_12_AXI_ERR_RSP: - case GAUDI2_EVENT_PMMU0_PAGE_FAULT_WR_PERM ... GAUDI2_EVENT_PMMU0_SECURITY_ERROR: case GAUDI2_EVENT_PMMU_AXI_ERR_RSP_0: gaudi2_handle_mmu_spi_sei_err(hdev, event_type); reset_flags |= HL_DRV_RESET_FW_FATAL_ERR; + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; break; case GAUDI2_EVENT_HIF0_FATAL ... GAUDI2_EVENT_HIF12_FATAL: gaudi2_handle_hif_fatal(hdev, event_type, le64_to_cpu(eq_entry->intr_cause.intr_cause_data)); reset_flags |= HL_DRV_RESET_FW_FATAL_ERR; + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; case GAUDI2_EVENT_PMMU_FATAL_0: gaudi2_handle_pif_fatal(hdev, le64_to_cpu(eq_entry->intr_cause.intr_cause_data)); reset_flags |= HL_DRV_RESET_FW_FATAL_ERR; + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; case GAUDI2_EVENT_PSOC63_RAZWI_OR_PID_MIN_MAX_INTERRUPT: gaudi2_ack_psoc_razwi_event_handler(hdev); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; break; case GAUDI2_EVENT_HBM0_MC0_SEI_SEVERE ... GAUDI2_EVENT_HBM5_MC1_SEI_NON_SEVERE: + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; if (gaudi2_handle_hbm_mc_sei_err(hdev, event_type, &eq_entry->sei_data)) { reset_flags |= HL_DRV_RESET_FW_FATAL_ERR; reset_required = true; @@ -8723,25 +8744,31 @@ static void gaudi2_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_ent case GAUDI2_EVENT_HBM_CATTRIP_0 ... GAUDI2_EVENT_HBM_CATTRIP_5: gaudi2_handle_hbm_cattrip(hdev, le64_to_cpu(eq_entry->intr_cause.intr_cause_data)); + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; case GAUDI2_EVENT_HBM0_MC0_SPI ... GAUDI2_EVENT_HBM5_MC1_SPI: gaudi2_handle_hbm_mc_spi(hdev, le64_to_cpu(eq_entry->intr_cause.intr_cause_data)); + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; case GAUDI2_EVENT_PCIE_DRAIN_COMPLETE: gaudi2_handle_pcie_drain(hdev, &eq_entry->pcie_drain_ind_data); + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; case GAUDI2_EVENT_PSOC59_RPM_ERROR_OR_DRAIN: gaudi2_handle_psoc_drain(hdev, le64_to_cpu(eq_entry->intr_cause.intr_cause_data)); + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; case GAUDI2_EVENT_CPU_AXI_ECC: reset_flags |= HL_DRV_RESET_FW_FATAL_ERR; + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; case GAUDI2_EVENT_CPU_L2_RAM_ECC: reset_flags |= HL_DRV_RESET_FW_FATAL_ERR; + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; case GAUDI2_EVENT_MME0_SBTE0_AXI_ERR_RSP ... GAUDI2_EVENT_MME0_SBTE4_AXI_ERR_RSP: case GAUDI2_EVENT_MME1_SBTE0_AXI_ERR_RSP ... GAUDI2_EVENT_MME1_SBTE4_AXI_ERR_RSP: @@ -8755,17 +8782,24 @@ static void gaudi2_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_ent GAUDI2_EVENT_MME0_SBTE0_AXI_ERR_RSP); gaudi2_handle_mme_sbte_err(hdev, index, sbte_index, le64_to_cpu(eq_entry->intr_cause.intr_cause_data)); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; break; case GAUDI2_EVENT_VM0_ALARM_A ... GAUDI2_EVENT_VM3_ALARM_B: reset_flags |= HL_DRV_RESET_FW_FATAL_ERR; + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; case GAUDI2_EVENT_PSOC_AXI_ERR_RSP: + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; + break; case GAUDI2_EVENT_PSOC_PRSTN_FALL: + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; case GAUDI2_EVENT_PCIE_APB_TIMEOUT: reset_flags |= HL_DRV_RESET_FW_FATAL_ERR; + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; case GAUDI2_EVENT_PCIE_FATAL_ERR: + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; case GAUDI2_EVENT_TPC0_BMON_SPMU: case GAUDI2_EVENT_TPC1_BMON_SPMU: @@ -8817,6 +8851,7 @@ static void gaudi2_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_ent case GAUDI2_EVENT_DEC8_BMON_SPMU: case GAUDI2_EVENT_DEC9_BMON_SPMU: case GAUDI2_EVENT_ROTATOR0_BMON_SPMU ... GAUDI2_EVENT_SM3_BMON_SPMU: + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; break; case GAUDI2_EVENT_CPU_FIX_POWER_ENV_S: @@ -8824,43 +8859,53 @@ static void gaudi2_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_ent case GAUDI2_EVENT_CPU_FIX_THERMAL_ENV_S: case GAUDI2_EVENT_CPU_FIX_THERMAL_ENV_E: gaudi2_print_clk_change_info(hdev, event_type); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; break; case GAUDI2_EVENT_CPU_PKT_QUEUE_OUT_SYNC: gaudi2_print_out_of_sync_info(hdev, &eq_entry->pkt_sync_err); + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; case GAUDI2_EVENT_PCIE_FLR_REQUESTED: + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; /* Do nothing- FW will handle it */ break; case GAUDI2_EVENT_PCIE_P2P_MSIX: gaudi2_handle_pcie_p2p_msix(hdev); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; break; case GAUDI2_EVENT_SM0_AXI_ERROR_RESPONSE ... GAUDI2_EVENT_SM3_AXI_ERROR_RESPONSE: index = event_type - GAUDI2_EVENT_SM0_AXI_ERROR_RESPONSE; skip_reset = !gaudi2_handle_sm_err(hdev, index); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; break; case GAUDI2_EVENT_PSOC_MME_PLL_LOCK_ERR ... GAUDI2_EVENT_DCORE2_HBM_PLL_LOCK_ERR: + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; case GAUDI2_EVENT_CPU_CPLD_SHUTDOWN_CAUSE: dev_info(hdev->dev, "CPLD shutdown cause, reset reason: 0x%llx\n", le64_to_cpu(eq_entry->data[0])); + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; case GAUDI2_EVENT_CPU_CPLD_SHUTDOWN_EVENT: dev_err(hdev->dev, "CPLD shutdown event, reset reason: 0x%llx\n", le64_to_cpu(eq_entry->data[0])); + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; case GAUDI2_EVENT_CPU_PKT_SANITY_FAILED: gaudi2_print_cpu_pkt_failure_info(hdev, &eq_entry->pkt_sync_err); + event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; case GAUDI2_EVENT_ARC_DCCM_FULL: hl_arc_event_handle(hdev, &eq_entry->arc_data); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; break; default: @@ -8876,15 +8921,22 @@ static void gaudi2_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_ent if (!gaudi2_irq_map_table[event_type].msg) hl_fw_unmask_irq(hdev, event_type); + if (event_mask) + hl_notifier_event_send_all(hdev, event_mask); + return; reset_device: if (hdev->hard_reset_on_fw_events) { hl_device_reset(hdev, reset_flags); + event_mask |= HL_NOTIFIER_EVENT_DEVICE_RESET; } else { if (!gaudi2_irq_map_table[event_type].msg) hl_fw_unmask_irq(hdev, event_type); } + + if (event_mask) + hl_notifier_event_send_all(hdev, event_mask); } static int gaudi2_memset_device_memory(struct hl_device *hdev, u64 addr, u64 size, u64 val) diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index 3005cc04d4b1..a4bab0fd8223 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -715,12 +715,16 @@ enum hl_server_type { * HL_NOTIFIER_EVENT_DEVICE_RESET - Indicates device requires a reset * HL_NOTIFIER_EVENT_CS_TIMEOUT - Indicates CS timeout error * HL_NOTIFIER_EVENT_DEVICE_UNAVAILABLE - Indicates device is unavailable + * HL_NOTIFIER_EVENT_USER_ENGINE_ERR - Indicates device engine in error state + * HL_NOTIFIER_EVENT_GENERAL_HW_ERR - Indicates device HW error */ #define HL_NOTIFIER_EVENT_TPC_ASSERT (1ULL << 0) #define HL_NOTIFIER_EVENT_UNDEFINED_OPCODE (1ULL << 1) #define HL_NOTIFIER_EVENT_DEVICE_RESET (1ULL << 2) #define HL_NOTIFIER_EVENT_CS_TIMEOUT (1ULL << 3) #define HL_NOTIFIER_EVENT_DEVICE_UNAVAILABLE (1ULL << 4) +#define HL_NOTIFIER_EVENT_USER_ENGINE_ERR (1ULL << 5) +#define HL_NOTIFIER_EVENT_GENERAL_HW_ERR (1ULL << 6) /* Opcode for management ioctl * -- cgit v1.2.3 From 82736b063fde67ea2a9b16ef5acf3d5db03e2deb Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Mon, 5 Sep 2022 17:14:45 +0300 Subject: habanalabs: MMU invalidation h/w is per device The code used the mmu mutex to protect access to the context's page tables and invalidation of the MMU cache. Because pgt are per context, the mmu mutex was a member of the context object. The problem is that the device has a single MMU invalidation h/w (per MMU). Therefore, the mmu mutex should not be a property of the context but a property of the device. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_buffer.c | 10 +++++----- drivers/misc/habanalabs/common/habanalabs.h | 10 +++++++--- drivers/misc/habanalabs/common/memory.c | 14 +++++++------- drivers/misc/habanalabs/common/mmu/mmu.c | 22 +++++++++++----------- drivers/misc/habanalabs/gaudi/gaudi.c | 8 ++++---- drivers/misc/habanalabs/gaudi2/gaudi2.c | 16 ++++++++-------- 6 files changed, 42 insertions(+), 38 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_buffer.c b/drivers/misc/habanalabs/common/command_buffer.c index d16de18863ba..2b332991ac6a 100644 --- a/drivers/misc/habanalabs/common/command_buffer.c +++ b/drivers/misc/habanalabs/common/command_buffer.c @@ -44,20 +44,20 @@ static int cb_map_mem(struct hl_ctx *ctx, struct hl_cb *cb) return -ENOMEM; } - mutex_lock(&ctx->mmu_lock); + mutex_lock(&hdev->mmu_lock); rc = hl_mmu_map_contiguous(ctx, cb->virtual_addr, cb->bus_address, cb->roundup_size); if (rc) { dev_err(hdev->dev, "Failed to map VA %#llx to CB\n", cb->virtual_addr); goto err_va_umap; } rc = hl_mmu_invalidate_cache(hdev, false, MMU_OP_USERPTR | MMU_OP_SKIP_LOW_CACHE_INV); - mutex_unlock(&ctx->mmu_lock); + mutex_unlock(&hdev->mmu_lock); cb->is_mmu_mapped = true; return rc; err_va_umap: - mutex_unlock(&ctx->mmu_lock); + mutex_unlock(&hdev->mmu_lock); gen_pool_free(ctx->cb_va_pool, cb->virtual_addr, cb->roundup_size); return rc; } @@ -66,10 +66,10 @@ static void cb_unmap_mem(struct hl_ctx *ctx, struct hl_cb *cb) { struct hl_device *hdev = ctx->hdev; - mutex_lock(&ctx->mmu_lock); + mutex_lock(&hdev->mmu_lock); hl_mmu_unmap_contiguous(ctx, cb->virtual_addr, cb->roundup_size); hl_mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR); - mutex_unlock(&ctx->mmu_lock); + mutex_unlock(&hdev->mmu_lock); gen_pool_free(ctx->cb_va_pool, cb->virtual_addr, cb->roundup_size); } diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 959e3616cc2f..9c2123ddc548 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -1793,8 +1793,6 @@ struct hl_cs_outcome_store { * command submissions for a long time after CS id wraparound. * @va_range: holds available virtual addresses for host and dram mappings. * @mem_hash_lock: protects the mem_hash. - * @mmu_lock: protects the MMU page tables. Any change to the PGT, modifying the - * MMU hash or walking the PGT requires talking this lock. * @hw_block_list_lock: protects the HW block memory list. * @debugfs_list: node in debugfs list of contexts. * @hw_block_mem_list: list of HW block virtual mapped addresses. @@ -1831,7 +1829,6 @@ struct hl_ctx { struct hl_cs_outcome_store outcome_store; struct hl_va_range *va_range[HL_VA_RANGE_TYPE_MAX]; struct mutex mem_hash_lock; - struct mutex mmu_lock; struct mutex hw_block_list_lock; struct list_head debugfs_list; struct list_head hw_block_mem_list; @@ -3079,6 +3076,12 @@ struct hl_reset_info { * @asid_mutex: protects asid_bitmap. * @send_cpu_message_lock: enforces only one message in Host <-> CPU-CP queue. * @debug_lock: protects critical section of setting debug mode for device + * @mmu_lock: protects the MMU page tables and invalidation h/w. Although the + * page tables are per context, the invalidation h/w is per MMU. + * Therefore, we can't allow multiple contexts (we only have two, + * user and kernel) to access the invalidation h/w at the same time. + * In addition, any change to the PGT, modifying the MMU hash or + * walking the PGT requires talking this lock. * @asic_prop: ASIC specific immutable properties. * @asic_funcs: ASIC specific functions. * @asic_specific: ASIC specific information to use only from ASIC files. @@ -3244,6 +3247,7 @@ struct hl_device { struct mutex asid_mutex; struct mutex send_cpu_message_lock; struct mutex debug_lock; + struct mutex mmu_lock; struct asic_fixed_properties asic_prop; const struct hl_asic_funcs *asic_funcs; void *asic_specific; diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c index 0a653fff08d4..096fa3c1ae95 100644 --- a/drivers/misc/habanalabs/common/memory.c +++ b/drivers/misc/habanalabs/common/memory.c @@ -1210,18 +1210,18 @@ static int map_device_va(struct hl_ctx *ctx, struct hl_mem_in *args, u64 *device goto va_block_err; } - mutex_lock(&ctx->mmu_lock); + mutex_lock(&hdev->mmu_lock); rc = map_phys_pg_pack(ctx, ret_vaddr, phys_pg_pack); if (rc) { dev_err(hdev->dev, "mapping page pack failed for handle %u\n", handle); - mutex_unlock(&ctx->mmu_lock); + mutex_unlock(&hdev->mmu_lock); goto map_err; } rc = hl_mmu_invalidate_cache_range(hdev, false, *vm_type | MMU_OP_SKIP_LOW_CACHE_INV, ctx->asid, ret_vaddr, phys_pg_pack->total_size); - mutex_unlock(&ctx->mmu_lock); + mutex_unlock(&hdev->mmu_lock); if (rc) goto map_err; @@ -1362,7 +1362,7 @@ static int unmap_device_va(struct hl_ctx *ctx, struct hl_mem_in *args, else vaddr &= ~(((u64) phys_pg_pack->page_size) - 1); - mutex_lock(&ctx->mmu_lock); + mutex_lock(&hdev->mmu_lock); unmap_phys_pg_pack(ctx, vaddr, phys_pg_pack); @@ -1375,7 +1375,7 @@ static int unmap_device_va(struct hl_ctx *ctx, struct hl_mem_in *args, rc = hl_mmu_invalidate_cache_range(hdev, true, *vm_type, ctx->asid, vaddr, phys_pg_pack->total_size); - mutex_unlock(&ctx->mmu_lock); + mutex_unlock(&hdev->mmu_lock); /* * If the context is closing we don't need to check for the MMU cache @@ -2771,13 +2771,13 @@ void hl_vm_ctx_fini(struct hl_ctx *ctx) unmap_device_va(ctx, &args, true); } - mutex_lock(&ctx->mmu_lock); + mutex_lock(&hdev->mmu_lock); /* invalidate the cache once after the unmapping loop */ hl_mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR); hl_mmu_invalidate_cache(hdev, true, MMU_OP_PHYS_PACK); - mutex_unlock(&ctx->mmu_lock); + mutex_unlock(&hdev->mmu_lock); INIT_LIST_HEAD(&free_list); diff --git a/drivers/misc/habanalabs/common/mmu/mmu.c b/drivers/misc/habanalabs/common/mmu/mmu.c index 264f3b9edc88..cf8946266615 100644 --- a/drivers/misc/habanalabs/common/mmu/mmu.c +++ b/drivers/misc/habanalabs/common/mmu/mmu.c @@ -47,6 +47,8 @@ int hl_mmu_init(struct hl_device *hdev) if (!hdev->mmu_enable) return 0; + mutex_init(&hdev->mmu_lock); + if (hdev->mmu_func[MMU_DR_PGT].init != NULL) { rc = hdev->mmu_func[MMU_DR_PGT].init(hdev); if (rc) @@ -88,6 +90,8 @@ void hl_mmu_fini(struct hl_device *hdev) if (hdev->mmu_func[MMU_HR_PGT].fini != NULL) hdev->mmu_func[MMU_HR_PGT].fini(hdev); + + mutex_destroy(&hdev->mmu_lock); } /** @@ -106,8 +110,6 @@ int hl_mmu_ctx_init(struct hl_ctx *ctx) if (!hdev->mmu_enable) return 0; - mutex_init(&ctx->mmu_lock); - if (hdev->mmu_func[MMU_DR_PGT].ctx_init != NULL) { rc = hdev->mmu_func[MMU_DR_PGT].ctx_init(ctx); if (rc) @@ -151,8 +153,6 @@ void hl_mmu_ctx_fini(struct hl_ctx *ctx) if (hdev->mmu_func[MMU_HR_PGT].ctx_fini != NULL) hdev->mmu_func[MMU_HR_PGT].ctx_fini(ctx); - - mutex_destroy(&ctx->mmu_lock); } /* @@ -609,9 +609,9 @@ int hl_mmu_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr, pgt_residency = mmu_prop->host_resident ? MMU_HR_PGT : MMU_DR_PGT; mmu_funcs = hl_mmu_get_funcs(hdev, pgt_residency, is_dram_addr); - mutex_lock(&ctx->mmu_lock); + mutex_lock(&hdev->mmu_lock); rc = mmu_funcs->get_tlb_info(ctx, virt_addr, hops); - mutex_unlock(&ctx->mmu_lock); + mutex_unlock(&hdev->mmu_lock); if (rc) return rc; @@ -701,16 +701,16 @@ static void hl_mmu_prefetch_work_function(struct work_struct *work) { struct hl_prefetch_work *pfw = container_of(work, struct hl_prefetch_work, pf_work); struct hl_ctx *ctx = pfw->ctx; + struct hl_device *hdev = ctx->hdev; - if (!hl_device_operational(ctx->hdev, NULL)) + if (!hl_device_operational(hdev, NULL)) goto put_ctx; - mutex_lock(&ctx->mmu_lock); + mutex_lock(&hdev->mmu_lock); - ctx->hdev->asic_funcs->mmu_prefetch_cache_range(ctx, pfw->flags, pfw->asid, - pfw->va, pfw->size); + hdev->asic_funcs->mmu_prefetch_cache_range(ctx, pfw->flags, pfw->asid, pfw->va, pfw->size); - mutex_unlock(&ctx->mmu_lock); + mutex_unlock(&hdev->mmu_lock); put_ctx: /* diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 2b328cb62096..48ff3b103b9f 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -8405,13 +8405,13 @@ static int gaudi_internal_cb_pool_init(struct hl_device *hdev, goto destroy_internal_cb_pool; } - mutex_lock(&ctx->mmu_lock); + mutex_lock(&hdev->mmu_lock); rc = hl_mmu_map_contiguous(ctx, hdev->internal_cb_va_base, hdev->internal_cb_pool_dma_addr, HOST_SPACE_INTERNAL_CB_SZ); hl_mmu_invalidate_cache(hdev, false, MMU_OP_USERPTR); - mutex_unlock(&ctx->mmu_lock); + mutex_unlock(&hdev->mmu_lock); if (rc) goto unreserve_internal_cb_pool; @@ -8438,13 +8438,13 @@ static void gaudi_internal_cb_pool_fini(struct hl_device *hdev, if (!(gaudi->hw_cap_initialized & HW_CAP_MMU)) return; - mutex_lock(&ctx->mmu_lock); + mutex_lock(&hdev->mmu_lock); hl_mmu_unmap_contiguous(ctx, hdev->internal_cb_va_base, HOST_SPACE_INTERNAL_CB_SZ); hl_unreserve_va_block(hdev, ctx, hdev->internal_cb_va_base, HOST_SPACE_INTERNAL_CB_SZ); hl_mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR); - mutex_unlock(&ctx->mmu_lock); + mutex_unlock(&hdev->mmu_lock); gen_pool_destroy(hdev->internal_cb_pool); diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index f749f7377ea6..5761ca5d50ae 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -9302,12 +9302,12 @@ static int gaudi2_debugfs_read_dma(struct hl_device *hdev, u64 addr, u32 size, v } /* Create mapping on asic side */ - mutex_lock(&ctx->mmu_lock); + mutex_lock(&hdev->mmu_lock); rc = hl_mmu_map_contiguous(ctx, reserved_va_base, host_mem_dma_addr, SZ_2M); hl_mmu_invalidate_cache_range(hdev, false, MMU_OP_USERPTR | MMU_OP_SKIP_LOW_CACHE_INV, ctx->asid, reserved_va_base, SZ_2M); - mutex_unlock(&ctx->mmu_lock); + mutex_unlock(&hdev->mmu_lock); if (rc) { dev_err(hdev->dev, "Failed to create mapping on asic mmu\n"); goto unreserve_va; @@ -9340,11 +9340,11 @@ static int gaudi2_debugfs_read_dma(struct hl_device *hdev, u64 addr, u32 size, v gaudi2_kdma_set_mmbp_asid(hdev, true, HL_KERNEL_ASID_ID); - mutex_lock(&ctx->mmu_lock); + mutex_lock(&hdev->mmu_lock); hl_mmu_unmap_contiguous(ctx, reserved_va_base, SZ_2M); hl_mmu_invalidate_cache_range(hdev, false, MMU_OP_USERPTR, ctx->asid, reserved_va_base, SZ_2M); - mutex_unlock(&ctx->mmu_lock); + mutex_unlock(&hdev->mmu_lock); unreserve_va: hl_unreserve_va_block(hdev, ctx, reserved_va_base, SZ_2M); free_data_buffer: @@ -9397,11 +9397,11 @@ static int gaudi2_internal_cb_pool_init(struct hl_device *hdev, struct hl_ctx *c goto destroy_internal_cb_pool; } - mutex_lock(&ctx->mmu_lock); + mutex_lock(&hdev->mmu_lock); rc = hl_mmu_map_contiguous(ctx, hdev->internal_cb_va_base, hdev->internal_cb_pool_dma_addr, HOST_SPACE_INTERNAL_CB_SZ); hl_mmu_invalidate_cache(hdev, false, MMU_OP_USERPTR); - mutex_unlock(&ctx->mmu_lock); + mutex_unlock(&hdev->mmu_lock); if (rc) goto unreserve_internal_cb_pool; @@ -9426,11 +9426,11 @@ static void gaudi2_internal_cb_pool_fini(struct hl_device *hdev, struct hl_ctx * if (!(gaudi2->hw_cap_initialized & HW_CAP_PMMU)) return; - mutex_lock(&ctx->mmu_lock); + mutex_lock(&hdev->mmu_lock); hl_mmu_unmap_contiguous(ctx, hdev->internal_cb_va_base, HOST_SPACE_INTERNAL_CB_SZ); hl_unreserve_va_block(hdev, ctx, hdev->internal_cb_va_base, HOST_SPACE_INTERNAL_CB_SZ); hl_mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR); - mutex_unlock(&ctx->mmu_lock); + mutex_unlock(&hdev->mmu_lock); gen_pool_destroy(hdev->internal_cb_pool); -- cgit v1.2.3 From cecde184ca32ae862c5494a7875d03592c893ab9 Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Mon, 5 Sep 2022 16:24:21 +0300 Subject: habanalabs/gaudi2: print RAZWI info upon PCIe access error Add the dump of the RAZWI information when a PCIe access is blocked by RR. Signed-off-by: Tomer Tayar Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi2/gaudi2.c | 52 ++++++++++++++++++++++++--- drivers/misc/habanalabs/gaudi2/gaudi2_masks.h | 13 +++++++ 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index 5761ca5d50ae..c040e01adafe 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -7963,14 +7963,58 @@ static void gaudi2_handle_dma_core_event(struct hl_device *hdev, u64 intr_cause_ gaudi2_dma_core_interrupts_cause[i]); } +static void gaudi2_print_pcie_mstr_rr_mstr_if_razwi_info(struct hl_device *hdev) +{ + u32 mstr_if_base_addr = mmPCIE_MSTR_RR_MSTR_IF_RR_SHRD_HBW_BASE, razwi_happened_addr; + + razwi_happened_addr = mstr_if_base_addr + RR_SHRD_HBW_AW_RAZWI_HAPPENED; + if (RREG32(razwi_happened_addr)) { + gaudi2_razwi_rr_hbw_shared_printf_info(hdev, mstr_if_base_addr, true, "PCIE", true, + NULL); + WREG32(razwi_happened_addr, 0x1); + } + + razwi_happened_addr = mstr_if_base_addr + RR_SHRD_HBW_AR_RAZWI_HAPPENED; + if (RREG32(razwi_happened_addr)) { + gaudi2_razwi_rr_hbw_shared_printf_info(hdev, mstr_if_base_addr, false, "PCIE", true, + NULL); + WREG32(razwi_happened_addr, 0x1); + } + + razwi_happened_addr = mstr_if_base_addr + RR_SHRD_LBW_AW_RAZWI_HAPPENED; + if (RREG32(razwi_happened_addr)) { + gaudi2_razwi_rr_lbw_shared_printf_info(hdev, mstr_if_base_addr, true, "PCIE", true, + NULL); + WREG32(razwi_happened_addr, 0x1); + } + + razwi_happened_addr = mstr_if_base_addr + RR_SHRD_LBW_AR_RAZWI_HAPPENED; + if (RREG32(razwi_happened_addr)) { + gaudi2_razwi_rr_lbw_shared_printf_info(hdev, mstr_if_base_addr, false, "PCIE", true, + NULL); + WREG32(razwi_happened_addr, 0x1); + } +} + static void gaudi2_print_pcie_addr_dec_info(struct hl_device *hdev, u64 intr_cause_data) { int i; - for (i = 0 ; i < GAUDI2_NUM_OF_PCIE_ADDR_DEC_ERR_CAUSE; i++) - if (intr_cause_data & BIT_ULL(i)) - dev_err_ratelimited(hdev->dev, "PCIE ADDR DEC Error: %s\n", - gaudi2_pcie_addr_dec_error_cause[i]); + for (i = 0 ; i < GAUDI2_NUM_OF_PCIE_ADDR_DEC_ERR_CAUSE ; i++) { + if (!(intr_cause_data & BIT_ULL(i))) + continue; + + dev_err_ratelimited(hdev->dev, "PCIE ADDR DEC Error: %s\n", + gaudi2_pcie_addr_dec_error_cause[i]); + + switch (intr_cause_data & BIT_ULL(i)) { + case PCIE_WRAP_PCIE_IC_SEI_INTR_IND_AXI_LBW_ERR_INTR_MASK: + break; + case PCIE_WRAP_PCIE_IC_SEI_INTR_IND_BAD_ACCESS_INTR_MASK: + gaudi2_print_pcie_mstr_rr_mstr_if_razwi_info(hdev); + break; + } + } } static void gaudi2_handle_pif_fatal(struct hl_device *hdev, u64 intr_cause_data) diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2_masks.h b/drivers/misc/habanalabs/gaudi2/gaudi2_masks.h index 0239d118abc5..e9ac87828221 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2_masks.h +++ b/drivers/misc/habanalabs/gaudi2/gaudi2_masks.h @@ -144,4 +144,17 @@ #define DCORE0_SYNC_MNGR_OBJS_SOB_OBJ_SIGN_SHIFT 15 #define DCORE0_SYNC_MNGR_OBJS_SOB_OBJ_SIGN_MASK 0x8000 +#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_AXI_ERR_INTR_SHIFT 0 +#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_AXI_ERR_INTR_MASK 0x1 +#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_AXI_LBW_ERR_INTR_SHIFT 1 +#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_AXI_LBW_ERR_INTR_MASK 0x2 +#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_BAD_ACCESS_INTR_SHIFT 2 +#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_BAD_ACCESS_INTR_MASK 0x4 +#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_AXI_ERR_INTR_MASK_SHIFT 3 +#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_AXI_ERR_INTR_MASK_MASK 0x8 +#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_AXI_LBW_ERR_INTR_MASK_SHIFT 4 +#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_AXI_LBW_ERR_INTR_MASK_MASK 0x10 +#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_BAD_ACCESS_INTR_MASK_SHIFT 5 +#define PCIE_WRAP_PCIE_IC_SEI_INTR_IND_BAD_ACCESS_INTR_MASK_MASK 0x20 + #endif /* GAUDI2_MASKS_H_ */ -- cgit v1.2.3 From f0b6d3cc29b709089f7a0de6a3d64a73ed0d67bd Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Tue, 6 Sep 2022 16:37:14 +0300 Subject: habanalabs/gaudi2: increase hard-reset sleep time to 2 sec The access to the device registers is blocked during hard reset, until preboot runs and allows the access to specific registers, including the PSOC BTM_FSM register which is used to know when the reset is done. Between the reset request and until this register is polled there is a small delay of 500 msec which is not enough for F/W to process the reset and for preboot to run, so the register might be accessed while it is blocked. To avoid it, increase the delay to 2 sec. Signed-off-by: Tomer Tayar Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi2/gaudi2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index c040e01adafe..6ed9b3ce16dd 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -21,7 +21,7 @@ #define GAUDI2_DMA_POOL_BLK_SIZE SZ_256 /* 256 bytes */ -#define GAUDI2_RESET_TIMEOUT_MSEC 500 /* 500ms */ +#define GAUDI2_RESET_TIMEOUT_MSEC 2000 /* 2000ms */ #define GAUDI2_RESET_POLL_TIMEOUT_USEC 50000 /* 50ms */ #define GAUDI2_PLDM_HRESET_TIMEOUT_MSEC 25000 /* 25s */ #define GAUDI2_PLDM_SRESET_TIMEOUT_MSEC 25000 /* 25s */ -- cgit v1.2.3 From 04d53cd2a6fb9936c938b624d99320cf2f842758 Mon Sep 17 00:00:00 2001 From: farah kassabri Date: Mon, 29 Aug 2022 16:56:28 +0300 Subject: habanalabs/gaudi2: get f/w reset status register dynamically Get the firmware reset status address from the dynamic registers we read from the firmware instead of using a define. Signed-off-by: farah kassabri Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi2/gaudi2.c | 5 ++++- drivers/misc/habanalabs/include/common/hl_boot_if.h | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index 6ed9b3ce16dd..b95eab4c237c 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -5439,7 +5439,10 @@ static void gaudi2_execute_soft_reset(struct hl_device *hdev, u32 reset_sleep_ms if (!driver_performs_reset) { /* set SP to indicate reset request sent to FW */ - WREG32(mmCPU_RST_STATUS_TO_HOST, CPU_RST_STATUS_NA); + if (dyn_regs->cpu_rst_status) + WREG32(le32_to_cpu(dyn_regs->cpu_rst_status), CPU_RST_STATUS_NA); + else + WREG32(mmCPU_RST_STATUS_TO_HOST, CPU_RST_STATUS_NA); WREG32(le32_to_cpu(dyn_regs->gic_host_soft_rst_irq), gaudi2_irq_map_table[GAUDI2_EVENT_CPU_SOFT_RESET].cpu_id); diff --git a/drivers/misc/habanalabs/include/common/hl_boot_if.h b/drivers/misc/habanalabs/include/common/hl_boot_if.h index 2e45be5de4fe..e0ea51cc7475 100644 --- a/drivers/misc/habanalabs/include/common/hl_boot_if.h +++ b/drivers/misc/habanalabs/include/common/hl_boot_if.h @@ -431,7 +431,9 @@ struct cpu_dyn_regs { __le32 gic_host_ints_irq; __le32 gic_host_soft_rst_irq; __le32 gic_rot_qm_irq_ctrl; - __le32 reserved1[22]; /* reserve for future use */ + __le32 cpu_rst_status; + __le32 eng_arc_irq_ctrl; + __le32 reserved1[20]; /* reserve for future use */ }; /* TODO: remove the desc magic after the code is updated to use message */ -- cgit v1.2.3 From 97a78e3d8e176de80323b7a01cd6b26f0b6dcdc1 Mon Sep 17 00:00:00 2001 From: Dani Liberman Date: Wed, 7 Sep 2022 16:15:39 +0300 Subject: habanalabs: rename error info structure As a preparation for adding more errors to it, change to more suitable name. Signed-off-by: Dani Liberman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- .../misc/habanalabs/common/command_submission.c | 6 ++--- drivers/misc/habanalabs/common/habanalabs.h | 12 ++++----- drivers/misc/habanalabs/common/habanalabs_drv.c | 6 ++--- drivers/misc/habanalabs/common/habanalabs_ioctl.c | 30 ++++++++++----------- drivers/misc/habanalabs/gaudi/gaudi.c | 31 +++++++++++----------- 5 files changed, 43 insertions(+), 42 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index 746b688d34cf..fbe5003191bf 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -826,10 +826,10 @@ static void cs_timedout(struct work_struct *work) } /* Save only the first CS timeout parameters */ - rc = atomic_cmpxchg(&hdev->last_error.cs_timeout.write_enable, 1, 0); + rc = atomic_cmpxchg(&hdev->captured_err_info.cs_timeout.write_enable, 1, 0); if (rc) { - hdev->last_error.cs_timeout.timestamp = ktime_get(); - hdev->last_error.cs_timeout.seq = cs->sequence; + hdev->captured_err_info.cs_timeout.timestamp = ktime_get(); + hdev->captured_err_info.cs_timeout.seq = cs->sequence; event_mask = device_reset ? (HL_NOTIFIER_EVENT_CS_TIMEOUT | HL_NOTIFIER_EVENT_DEVICE_RESET) : HL_NOTIFIER_EVENT_CS_TIMEOUT; diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 9c2123ddc548..44050d463e23 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -2981,12 +2981,12 @@ struct undefined_opcode_info { }; /** - * struct last_error_session_info - info about last session errors occurred. - * @cs_timeout: CS timeout error last information. - * @razwi: razwi last information. + * struct hl_error_info - holds information collected during an error. + * @cs_timeout: CS timeout error information. + * @razwi: razwi information. * @undef_opcode: undefined opcode information */ -struct last_error_session_info { +struct hl_error_info { struct cs_timeout_info cs_timeout; struct razwi_info razwi; struct undefined_opcode_info undef_opcode; @@ -3111,7 +3111,7 @@ struct hl_reset_info { * @state_dump_specs: constants and dictionaries needed to dump system state. * @multi_cs_completion: array of multi-CS completion. * @clk_throttling: holds information about current/previous clock throttling events - * @last_error: holds information about last session in which CS timeout or razwi error occurred. + * @captured_err_info: holds information about errors. * @reset_info: holds current device reset information. * @stream_master_qid_arr: pointer to array with QIDs of master streams. * @fw_major_version: major version of current loaded preboot. @@ -3286,7 +3286,7 @@ struct hl_device { struct multi_cs_completion multi_cs_completion[ MULTI_CS_MAX_USER_CTX]; struct hl_clk_throttle clk_throttling; - struct last_error_session_info last_error; + struct hl_error_info captured_err_info; struct hl_reset_info reset_info; diff --git a/drivers/misc/habanalabs/common/habanalabs_drv.c b/drivers/misc/habanalabs/common/habanalabs_drv.c index fd9c8680f954..5250bfb6790e 100644 --- a/drivers/misc/habanalabs/common/habanalabs_drv.c +++ b/drivers/misc/habanalabs/common/habanalabs_drv.c @@ -211,9 +211,9 @@ int hl_device_open(struct inode *inode, struct file *filp) hl_debugfs_add_file(hpriv); - atomic_set(&hdev->last_error.cs_timeout.write_enable, 1); - atomic_set(&hdev->last_error.razwi.write_enable, 1); - hdev->last_error.undef_opcode.write_enable = true; + atomic_set(&hdev->captured_err_info.cs_timeout.write_enable, 1); + atomic_set(&hdev->captured_err_info.razwi.write_enable, 1); + hdev->captured_err_info.undef_opcode.write_enable = true; hdev->open_counter++; hdev->last_successful_open_jif = jiffies; diff --git a/drivers/misc/habanalabs/common/habanalabs_ioctl.c b/drivers/misc/habanalabs/common/habanalabs_ioctl.c index c7bd000750c8..ab0be082f3a6 100644 --- a/drivers/misc/habanalabs/common/habanalabs_ioctl.c +++ b/drivers/misc/habanalabs/common/habanalabs_ioctl.c @@ -593,8 +593,8 @@ static int cs_timeout_info(struct hl_fpriv *hpriv, struct hl_info_args *args) if ((!max_size) || (!out)) return -EINVAL; - info.seq = hdev->last_error.cs_timeout.seq; - info.timestamp = ktime_to_ns(hdev->last_error.cs_timeout.timestamp); + info.seq = hdev->captured_err_info.cs_timeout.seq; + info.timestamp = ktime_to_ns(hdev->captured_err_info.cs_timeout.timestamp); return copy_to_user(out, &info, min_t(size_t, max_size, sizeof(info))) ? -EFAULT : 0; } @@ -609,12 +609,12 @@ static int razwi_info(struct hl_fpriv *hpriv, struct hl_info_args *args) if ((!max_size) || (!out)) return -EINVAL; - info.timestamp = ktime_to_ns(hdev->last_error.razwi.timestamp); - info.addr = hdev->last_error.razwi.addr; - info.engine_id_1 = hdev->last_error.razwi.engine_id_1; - info.engine_id_2 = hdev->last_error.razwi.engine_id_2; - info.no_engine_id = hdev->last_error.razwi.non_engine_initiator; - info.error_type = hdev->last_error.razwi.type; + info.timestamp = ktime_to_ns(hdev->captured_err_info.razwi.timestamp); + info.addr = hdev->captured_err_info.razwi.addr; + info.engine_id_1 = hdev->captured_err_info.razwi.engine_id_1; + info.engine_id_2 = hdev->captured_err_info.razwi.engine_id_2; + info.no_engine_id = hdev->captured_err_info.razwi.non_engine_initiator; + info.error_type = hdev->captured_err_info.razwi.type; return copy_to_user(out, &info, min_t(size_t, max_size, sizeof(info))) ? -EFAULT : 0; } @@ -629,13 +629,13 @@ static int undefined_opcode_info(struct hl_fpriv *hpriv, struct hl_info_args *ar if ((!max_size) || (!out)) return -EINVAL; - info.timestamp = ktime_to_ns(hdev->last_error.undef_opcode.timestamp); - info.engine_id = hdev->last_error.undef_opcode.engine_id; - info.cq_addr = hdev->last_error.undef_opcode.cq_addr; - info.cq_size = hdev->last_error.undef_opcode.cq_size; - info.stream_id = hdev->last_error.undef_opcode.stream_id; - info.cb_addr_streams_len = hdev->last_error.undef_opcode.cb_addr_streams_len; - memcpy(info.cb_addr_streams, hdev->last_error.undef_opcode.cb_addr_streams, + info.timestamp = ktime_to_ns(hdev->captured_err_info.undef_opcode.timestamp); + info.engine_id = hdev->captured_err_info.undef_opcode.engine_id; + info.cq_addr = hdev->captured_err_info.undef_opcode.cq_addr; + info.cq_size = hdev->captured_err_info.undef_opcode.cq_size; + info.stream_id = hdev->captured_err_info.undef_opcode.stream_id; + info.cb_addr_streams_len = hdev->captured_err_info.undef_opcode.cb_addr_streams_len; + memcpy(info.cb_addr_streams, hdev->captured_err_info.undef_opcode.cb_addr_streams, sizeof(info.cb_addr_streams)); return copy_to_user(out, &info, min_t(size_t, max_size, sizeof(info))) ? -EFAULT : 0; diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 48ff3b103b9f..f81a141b4741 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -6894,9 +6894,9 @@ static void gaudi_handle_sw_config_stream_data(struct hl_device *hdev, u32 strea stream, cq_ptr, size); if (event_mask & HL_NOTIFIER_EVENT_UNDEFINED_OPCODE) { - hdev->last_error.undef_opcode.cq_addr = cq_ptr; - hdev->last_error.undef_opcode.cq_size = size; - hdev->last_error.undef_opcode.stream_id = stream; + hdev->captured_err_info.undef_opcode.cq_addr = cq_ptr; + hdev->captured_err_info.undef_opcode.cq_size = size; + hdev->captured_err_info.undef_opcode.stream_id = stream; } } @@ -6962,7 +6962,7 @@ static void gaudi_handle_last_pqes_on_err(struct hl_device *hdev, u32 qid_base, } if (event_mask & HL_NOTIFIER_EVENT_UNDEFINED_OPCODE) { - struct undefined_opcode_info *undef_opcode = &hdev->last_error.undef_opcode; + struct undefined_opcode_info *undef_opcode = &hdev->captured_err_info.undef_opcode; u32 arr_idx = undef_opcode->cb_addr_streams_len; if (arr_idx == 0) { @@ -7046,11 +7046,11 @@ static void gaudi_handle_qman_err_generic(struct hl_device *hdev, } /* check for undefined opcode */ if (glbl_sts_val & TPC0_QM_GLBL_STS1_CP_UNDEF_CMD_ERR_MASK && - hdev->last_error.undef_opcode.write_enable) { - memset(&hdev->last_error.undef_opcode, 0, - sizeof(hdev->last_error.undef_opcode)); + hdev->captured_err_info.undef_opcode.write_enable) { + memset(&hdev->captured_err_info.undef_opcode, 0, + sizeof(hdev->captured_err_info.undef_opcode)); - hdev->last_error.undef_opcode.write_enable = false; + hdev->captured_err_info.undef_opcode.write_enable = false; *event_mask |= HL_NOTIFIER_EVENT_UNDEFINED_OPCODE; } @@ -7332,18 +7332,19 @@ static void gaudi_print_irq_info(struct hl_device *hdev, u16 event_type, gaudi_print_and_get_mmu_error_info(hdev, &razwi_addr, &razwi_type); /* In case it's the first razwi, save its parameters*/ - rc = atomic_cmpxchg(&hdev->last_error.razwi.write_enable, 1, 0); + rc = atomic_cmpxchg(&hdev->captured_err_info.razwi.write_enable, 1, 0); if (rc) { - hdev->last_error.razwi.timestamp = ktime_get(); - hdev->last_error.razwi.addr = razwi_addr; - hdev->last_error.razwi.engine_id_1 = engine_id_1; - hdev->last_error.razwi.engine_id_2 = engine_id_2; + hdev->captured_err_info.razwi.timestamp = ktime_get(); + hdev->captured_err_info.razwi.addr = razwi_addr; + hdev->captured_err_info.razwi.engine_id_1 = engine_id_1; + hdev->captured_err_info.razwi.engine_id_2 = engine_id_2; /* * If first engine id holds non valid value the razwi initiator * does not have engine id */ - hdev->last_error.razwi.non_engine_initiator = (engine_id_1 == U16_MAX); - hdev->last_error.razwi.type = razwi_type; + hdev->captured_err_info.razwi.non_engine_initiator = + (engine_id_1 == U16_MAX); + hdev->captured_err_info.razwi.type = razwi_type; } } -- cgit v1.2.3 From ff13b900b0b2b28486b714f615b1f919973275c2 Mon Sep 17 00:00:00 2001 From: Tal Cohen Date: Wed, 17 Aug 2022 12:46:07 +0300 Subject: habanalabs/gaudi: change TPC Assert to use TPC DEC instead of QMAN err This change is done while there is a problem to use QMAN error for TPC assert async. The problem involves security limitation that exists to generate the assert via QMAN error. Signed-off-by: Tal Cohen Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi/gaudi.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index f81a141b4741..e80ebace49c8 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -7216,12 +7216,6 @@ static void gaudi_handle_qman_err(struct hl_device *hdev, u16 event_type, u64 *e switch (event_type) { case GAUDI_EVENT_TPC0_QM ... GAUDI_EVENT_TPC7_QM: - /* In TPC QM event, notify on TPC assertion. While there isn't - * a specific event for assertion yet, the FW generates QM event. - * The SW upper layer will inspect an internal mapped area to indicate - * if the event is a tpc assertion or tpc QM. - */ - *event_mask |= HL_NOTIFIER_EVENT_TPC_ASSERT; index = event_type - GAUDI_EVENT_TPC0_QM; qid_base = GAUDI_QUEUE_ID_TPC_0_0 + index * QMAN_STREAMS; qman_base = mmTPC0_QM_BASE + index * TPC_QMAN_OFFSET; @@ -7731,6 +7725,12 @@ static void gaudi_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entr case GAUDI_EVENT_TPC5_DEC: case GAUDI_EVENT_TPC6_DEC: case GAUDI_EVENT_TPC7_DEC: + /* In TPC DEC event, notify on TPC assertion. While there isn't + * a specific event for assertion yet, the FW generates TPC DEC event. + * The SW upper layer will inspect an internal mapped area to indicate + * if the event is a TPC Assertion or a "real" TPC DEC. + */ + event_mask |= HL_NOTIFIER_EVENT_TPC_ASSERT; gaudi_print_irq_info(hdev, event_type, true); reset_required = gaudi_tpc_read_interrupts(hdev, tpc_dec_event_to_tpc_id(event_type), -- cgit v1.2.3 From 43657dadfeffbec63b9ed358e6f82e9c64ff334c Mon Sep 17 00:00:00 2001 From: Dani Liberman Date: Thu, 8 Sep 2022 18:24:41 +0300 Subject: habanalabs/gaudi2: add handling to pmmu events in eqe handler In order to get the error cause and the captured address in case of page fault, added pmmu events to eqe handler. Signed-off-by: Dani Liberman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi2/gaudi2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index b95eab4c237c..b8b8b2dc2095 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -8756,6 +8756,7 @@ static void gaudi2_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_ent case GAUDI2_EVENT_HMMU0_PAGE_FAULT_OR_WR_PERM ... GAUDI2_EVENT_HMMU12_SECURITY_ERROR: case GAUDI2_EVENT_HMMU_0_AXI_ERR_RSP ... GAUDI2_EVENT_HMMU_12_AXI_ERR_RSP: + case GAUDI2_EVENT_PMMU0_PAGE_FAULT_WR_PERM ... GAUDI2_EVENT_PMMU0_SECURITY_ERROR: case GAUDI2_EVENT_PMMU_AXI_ERR_RSP_0: gaudi2_handle_mmu_spi_sei_err(hdev, event_type); reset_flags |= HL_DRV_RESET_FW_FATAL_ERR; -- cgit v1.2.3 From 0c88760f8f5e13e32f624a1da71144b240b05125 Mon Sep 17 00:00:00 2001 From: Dani Liberman Date: Mon, 1 Aug 2022 15:23:44 +0300 Subject: habanalabs/gaudi2: add secured attestation info uapi User will provide a nonce via the ioctl, and will retrieve secured attestation data of the boot, generated using given nonce. Signed-off-by: Dani Liberman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/firmware_if.c | 46 ++++++++++++++ drivers/misc/habanalabs/common/habanalabs.h | 3 + drivers/misc/habanalabs/common/habanalabs_ioctl.c | 52 +++++++++++++++ drivers/misc/habanalabs/include/common/cpucp_if.h | 77 ++++++++++++++++++++++- include/uapi/misc/habanalabs.h | 43 +++++++++++++ 5 files changed, 219 insertions(+), 2 deletions(-) diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index c2375917fc02..26a7529083e1 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -2988,3 +2988,49 @@ void hl_fw_set_max_power(struct hl_device *hdev) if (rc) dev_err(hdev->dev, "Failed to set max power, error %d\n", rc); } + +static int hl_fw_get_sec_attest_data(struct hl_device *hdev, u32 packet_id, void *data, u32 size, + u32 nonce, u32 timeout) +{ + struct cpucp_packet pkt = {}; + dma_addr_t req_dma_addr; + void *req_cpu_addr; + int rc; + + req_cpu_addr = hl_cpu_accessible_dma_pool_alloc(hdev, size, &req_dma_addr); + if (!data) { + dev_err(hdev->dev, + "Failed to allocate DMA memory for CPU-CP packet %u\n", packet_id); + return -ENOMEM; + } + + memset(data, 0, size); + + pkt.ctl = cpu_to_le32(packet_id << CPUCP_PKT_CTL_OPCODE_SHIFT); + pkt.addr = cpu_to_le64(req_dma_addr); + pkt.data_max_size = cpu_to_le32(size); + pkt.nonce = cpu_to_le32(nonce); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + timeout, NULL); + if (rc) { + dev_err(hdev->dev, + "Failed to handle CPU-CP pkt %u, error %d\n", packet_id, rc); + goto out; + } + + memcpy(data, req_cpu_addr, size); + +out: + hl_cpu_accessible_dma_pool_free(hdev, size, req_cpu_addr); + + return rc; +} + +int hl_fw_get_sec_attest_info(struct hl_device *hdev, struct cpucp_sec_attest_info *sec_attest_info, + u32 nonce) +{ + return hl_fw_get_sec_attest_data(hdev, CPUCP_PACKET_SEC_ATTEST_GET, sec_attest_info, + sizeof(struct cpucp_sec_attest_info), nonce, + HL_CPUCP_SEC_ATTEST_INFO_TINEOUT_USEC); +} diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 44050d463e23..58c95b13be69 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -66,6 +66,7 @@ struct hl_fpriv; #define HL_CPUCP_INFO_TIMEOUT_USEC 10000000 /* 10s */ #define HL_CPUCP_EEPROM_TIMEOUT_USEC 10000000 /* 10s */ #define HL_CPUCP_MON_DUMP_TIMEOUT_USEC 10000000 /* 10s */ +#define HL_CPUCP_SEC_ATTEST_INFO_TINEOUT_USEC 10000000 /* 10s */ #define HL_FW_STATUS_POLL_INTERVAL_USEC 10000 /* 10ms */ #define HL_FW_COMMS_STATUS_PLDM_POLL_INTERVAL_USEC 1000000 /* 1s */ @@ -3748,6 +3749,8 @@ int hl_get_pwm_info(struct hl_device *hdev, int sensor_index, u32 attr, long *va void hl_set_pwm_info(struct hl_device *hdev, int sensor_index, u32 attr, long value); long hl_fw_get_max_power(struct hl_device *hdev); void hl_fw_set_max_power(struct hl_device *hdev); +int hl_fw_get_sec_attest_info(struct hl_device *hdev, struct cpucp_sec_attest_info *sec_attest_info, + u32 nonce); 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); diff --git a/drivers/misc/habanalabs/common/habanalabs_ioctl.c b/drivers/misc/habanalabs/common/habanalabs_ioctl.c index ab0be082f3a6..43afe40966e5 100644 --- a/drivers/misc/habanalabs/common/habanalabs_ioctl.c +++ b/drivers/misc/habanalabs/common/habanalabs_ioctl.c @@ -662,6 +662,55 @@ static int dev_mem_alloc_page_sizes_info(struct hl_fpriv *hpriv, struct hl_info_ return copy_to_user(out, &info, min_t(size_t, max_size, sizeof(info))) ? -EFAULT : 0; } +static int sec_attest_info(struct hl_fpriv *hpriv, struct hl_info_args *args) +{ + void __user *out = (void __user *) (uintptr_t) args->return_pointer; + struct cpucp_sec_attest_info *sec_attest_info; + struct hl_info_sec_attest *info; + u32 max_size = args->return_size; + int rc; + + if ((!max_size) || (!out)) + return -EINVAL; + + sec_attest_info = kmalloc(sizeof(*sec_attest_info), GFP_KERNEL); + if (!sec_attest_info) + return -ENOMEM; + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + rc = -ENOMEM; + goto free_sec_attest_info; + } + + rc = hl_fw_get_sec_attest_info(hpriv->hdev, sec_attest_info, args->sec_attest_nonce); + if (rc) + goto free_info; + + info->nonce = le32_to_cpu(sec_attest_info->nonce); + info->pcr_quote_len = le16_to_cpu(sec_attest_info->pcr_quote_len); + info->pub_data_len = le16_to_cpu(sec_attest_info->pub_data_len); + info->certificate_len = le16_to_cpu(sec_attest_info->certificate_len); + info->pcr_num_reg = sec_attest_info->pcr_num_reg; + info->pcr_reg_len = sec_attest_info->pcr_reg_len; + info->quote_sig_len = sec_attest_info->quote_sig_len; + memcpy(&info->pcr_data, &sec_attest_info->pcr_data, sizeof(info->pcr_data)); + memcpy(&info->pcr_quote, &sec_attest_info->pcr_quote, sizeof(info->pcr_quote)); + memcpy(&info->public_data, &sec_attest_info->public_data, sizeof(info->public_data)); + memcpy(&info->certificate, &sec_attest_info->certificate, sizeof(info->certificate)); + memcpy(&info->quote_sig, &sec_attest_info->quote_sig, sizeof(info->quote_sig)); + + rc = copy_to_user(out, info, + min_t(size_t, max_size, sizeof(*info))) ? -EFAULT : 0; + +free_info: + kfree(info); +free_sec_attest_info: + kfree(sec_attest_info); + + return rc; +} + static int eventfd_register(struct hl_fpriv *hpriv, struct hl_info_args *args) { int rc; @@ -844,6 +893,9 @@ static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, case HL_INFO_DRAM_PENDING_ROWS: return dram_pending_rows_info(hpriv, args); + case HL_INFO_SECURED_ATTESTATION: + return sec_attest_info(hpriv, args); + case HL_INFO_REGISTER_EVENTFD: return eventfd_register(hpriv, args); diff --git a/drivers/misc/habanalabs/include/common/cpucp_if.h b/drivers/misc/habanalabs/include/common/cpucp_if.h index 9593d1a26945..baa5aa43b6f4 100644 --- a/drivers/misc/habanalabs/include/common/cpucp_if.h +++ b/drivers/misc/habanalabs/include/common/cpucp_if.h @@ -629,6 +629,12 @@ enum pq_init_status { * CPUCP_PACKET_ENGINE_CORE_ASID_SET - * Packet to perform engine core ASID configuration * + * CPUCP_PACKET_SEC_ATTEST_GET - + * Get the attestaion data that is collected during various stages of the + * boot sequence. the attestation data is also hashed with some unique + * number (nonce) provided by the host to prevent replay attacks. + * public key and certificate also provided as part of the FW response. + * * CPUCP_PACKET_MONITOR_DUMP_GET - * Get monitors registers dump from the CpuCP kernel. * The CPU will put the registers dump in the a buffer allocated by the driver @@ -691,15 +697,15 @@ enum cpucp_packet_id { CPUCP_PACKET_RESERVED, /* not used */ CPUCP_PACKET_ENGINE_CORE_ASID_SET, /* internal */ CPUCP_PACKET_RESERVED2, /* not used */ + CPUCP_PACKET_SEC_ATTEST_GET, /* internal */ CPUCP_PACKET_RESERVED3, /* not used */ CPUCP_PACKET_RESERVED4, /* not used */ - CPUCP_PACKET_RESERVED5, /* not used */ CPUCP_PACKET_MONITOR_DUMP_GET, /* debugfs */ + CPUCP_PACKET_RESERVED5, /* not used */ CPUCP_PACKET_RESERVED6, /* not used */ CPUCP_PACKET_RESERVED7, /* not used */ CPUCP_PACKET_RESERVED8, /* not used */ CPUCP_PACKET_RESERVED9, /* not used */ - CPUCP_PACKET_RESERVED10, /* not used */ CPUCP_PACKET_ACTIVE_STATUS_SET, /* internal */ CPUCP_PACKET_ID_MAX /* must be last */ }; @@ -794,6 +800,9 @@ struct cpucp_packet { * result cannot be used to hold general purpose data. */ __le32 status_mask; + + /* random, used once number, for security packets */ + __le32 nonce; }; /* For NIC requests */ @@ -1219,6 +1228,70 @@ enum cpu_reset_status { CPU_RST_STATUS_SOFT_RST_DONE = 1, }; +#define SEC_PCR_DATA_BUF_SZ 256 +#define SEC_PCR_QUOTE_BUF_SZ 510 /* (512 - 2) 2 bytes used for size */ +#define SEC_SIGNATURE_BUF_SZ 255 /* (256 - 1) 1 byte used for size */ +#define SEC_PUB_DATA_BUF_SZ 510 /* (512 - 2) 2 bytes used for size */ +#define SEC_CERTIFICATE_BUF_SZ 2046 /* (2048 - 2) 2 bytes used for size */ + +/* + * struct cpucp_sec_attest_info - attestation report of the boot + * @pcr_data: raw values of the PCR registers + * @pcr_num_reg: number of PCR registers in the pcr_data array + * @pcr_reg_len: length of each PCR register in the pcr_data array (bytes) + * @nonce: number only used once. random number provided by host. this also + * passed to the quote command as a qualifying data. + * @pcr_quote_len: length of the attestation quote data (bytes) + * @pcr_quote: attestation report data structure + * @quote_sig_len: length of the attestation report signature (bytes) + * @quote_sig: signature structure of the attestation report + * @pub_data_len: length of the public data (bytes) + * @public_data: public key for the signed attestation + * (outPublic + name + qualifiedName) + * @certificate_len: length of the certificate (bytes) + * @certificate: certificate for the attestation signing key + */ +struct cpucp_sec_attest_info { + __u8 pcr_data[SEC_PCR_DATA_BUF_SZ]; + __u8 pcr_num_reg; + __u8 pcr_reg_len; + __le16 pad0; + __le32 nonce; + __le16 pcr_quote_len; + __u8 pcr_quote[SEC_PCR_QUOTE_BUF_SZ]; + __u8 quote_sig_len; + __u8 quote_sig[SEC_SIGNATURE_BUF_SZ]; + __le16 pub_data_len; + __u8 public_data[SEC_PUB_DATA_BUF_SZ]; + __le16 certificate_len; + __u8 certificate[SEC_CERTIFICATE_BUF_SZ]; +}; + +/* + * struct cpucp_dev_info_signed - device information signed by a secured device + * @info: device information structure as defined above + * @nonce: number only used once. random number provided by host. this number is + * hashed and signed along with the device information. + * @info_sig_len: length of the attestation signature (bytes) + * @info_sig: signature of the info + nonce data. + * @pub_data_len: length of the public data (bytes) + * @public_data: public key info signed info data + * (outPublic + name + qualifiedName) + * @certificate_len: length of the certificate (bytes) + * @certificate: certificate for the signing key + */ +struct cpucp_dev_info_signed { + struct cpucp_info info; /* assumed to be 64bit aligned */ + __le32 nonce; + __le32 pad0; + __u8 info_sig_len; + __u8 info_sig[SEC_SIGNATURE_BUF_SZ]; + __le16 pub_data_len; + __u8 public_data[SEC_PUB_DATA_BUF_SZ]; + __le16 certificate_len; + __u8 certificate[SEC_CERTIFICATE_BUF_SZ]; +}; + /* * struct dcore_monitor_regs_data - DCORE monitor regs data. * the structure follows sync manager block layout. relevant only to Gaudi. diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index a4bab0fd8223..e00ebe05097d 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -773,6 +773,7 @@ enum hl_server_type { * Razwi initiator. * Razwi cause, was it a page fault or MMU access error. * HL_INFO_DEV_MEM_ALLOC_PAGE_SIZES - Retrieve valid page sizes for device memory allocation + * HL_INFO_SECURED_ATTESTATION - Retrieve attestation report of the boot. * HL_INFO_REGISTER_EVENTFD - Register eventfd for event notifications. * HL_INFO_UNREGISTER_EVENTFD - Unregister eventfd * HL_INFO_GET_EVENTS - Retrieve the last occurred events @@ -802,6 +803,7 @@ enum hl_server_type { #define HL_INFO_CS_TIMEOUT_EVENT 24 #define HL_INFO_RAZWI_EVENT 25 #define HL_INFO_DEV_MEM_ALLOC_PAGE_SIZES 26 +#define HL_INFO_SECURED_ATTESTATION 27 #define HL_INFO_REGISTER_EVENTFD 28 #define HL_INFO_UNREGISTER_EVENTFD 29 #define HL_INFO_GET_EVENTS 30 @@ -1133,6 +1135,45 @@ struct hl_info_dev_memalloc_page_sizes { __u64 page_order_bitmask; }; +#define SEC_PCR_DATA_BUF_SZ 256 +#define SEC_PCR_QUOTE_BUF_SZ 510 /* (512 - 2) 2 bytes used for size */ +#define SEC_SIGNATURE_BUF_SZ 255 /* (256 - 1) 1 byte used for size */ +#define SEC_PUB_DATA_BUF_SZ 510 /* (512 - 2) 2 bytes used for size */ +#define SEC_CERTIFICATE_BUF_SZ 2046 /* (2048 - 2) 2 bytes used for size */ + +/* + * struct hl_info_sec_attest - attestation report of the boot + * @nonce: number only used once. random number provided by host. this also passed to the quote + * command as a qualifying data. + * @pcr_quote_len: length of the attestation quote data (bytes) + * @pub_data_len: length of the public data (bytes) + * @certificate_len: length of the certificate (bytes) + * @pcr_num_reg: number of PCR registers in the pcr_data array + * @pcr_reg_len: length of each PCR register in the pcr_data array (bytes) + * @quote_sig_len: length of the attestation report signature (bytes) + * @pcr_data: raw values of the PCR registers + * @pcr_quote: attestation report data structure + * @quote_sig: signature structure of the attestation report + * @public_data: public key for the signed attestation + * (outPublic + name + qualifiedName) + * @certificate: certificate for the attestation signing key + */ +struct hl_info_sec_attest { + __u32 nonce; + __u16 pcr_quote_len; + __u16 pub_data_len; + __u16 certificate_len; + __u8 pcr_num_reg; + __u8 pcr_reg_len; + __u8 quote_sig_len; + __u8 pcr_data[SEC_PCR_DATA_BUF_SZ]; + __u8 pcr_quote[SEC_PCR_QUOTE_BUF_SZ]; + __u8 quote_sig[SEC_SIGNATURE_BUF_SZ]; + __u8 public_data[SEC_PUB_DATA_BUF_SZ]; + __u8 certificate[SEC_CERTIFICATE_BUF_SZ]; + __u8 pad0[2]; +}; + enum gaudi_dcores { HL_GAUDI_WS_DCORE, HL_GAUDI_WN_DCORE, @@ -1158,6 +1199,7 @@ enum gaudi_dcores { * driver. It is possible for the user to allocate buffer larger than * needed, hence updating this variable so user will know the exact amount * of bytes copied by the kernel to the buffer. + * @sec_attest_nonce: Nonce number used for attestation report. * @pad: Padding to 64 bit. */ struct hl_info_args { @@ -1172,6 +1214,7 @@ struct hl_info_args { __u32 pll_index; __u32 eventfd; __u32 user_buffer_actual_size; + __u32 sec_attest_nonce; }; __u32 pad; -- cgit v1.2.3 From 006fd8cb659bb02789dc1ec48836fff5f348ba8d Mon Sep 17 00:00:00 2001 From: Li zeming Date: Mon, 19 Sep 2022 10:20:54 +0800 Subject: habanalabs/gaudi2: Remove unnecessary (void*) conversions The void pointer object can be directly assigned to different structure objects, it does not need to be cast. Signed-off-by: Li zeming Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi2/gaudi2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2.c b/drivers/misc/habanalabs/gaudi2/gaudi2.c index b8b8b2dc2095..75c4bef7841c 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2.c @@ -9592,7 +9592,7 @@ static u32 gaudi2_get_queue_id_for_cq(struct hl_device *hdev, u32 cq_idx) static u32 gaudi2_gen_signal_cb(struct hl_device *hdev, void *data, u16 sob_id, u32 size, bool eb) { - struct hl_cb *cb = (struct hl_cb *) data; + struct hl_cb *cb = data; struct packet_msg_short *pkt; u32 value, ctl, pkt_size = sizeof(*pkt); @@ -9685,7 +9685,7 @@ static u32 gaudi2_add_fence_pkt(struct packet_fence *pkt) static u32 gaudi2_gen_wait_cb(struct hl_device *hdev, struct hl_gen_wait_properties *prop) { - struct hl_cb *cb = (struct hl_cb *) prop->data; + struct hl_cb *cb = prop->data; void *buf = (void *) (uintptr_t) (cb->kernel_address); u64 monitor_base, fence_addr = 0; @@ -9737,7 +9737,7 @@ static u32 gaudi2_gen_wait_cb(struct hl_device *hdev, struct hl_gen_wait_propert static void gaudi2_reset_sob(struct hl_device *hdev, void *data) { - struct hl_hw_sob *hw_sob = (struct hl_hw_sob *) data; + struct hl_hw_sob *hw_sob = data; dev_dbg(hdev->dev, "reset SOB, q_idx: %d, sob_id: %d\n", hw_sob->q_idx, hw_sob->sob_id); -- cgit v1.2.3 From 4f3ce5e0d0f85d6be0a2bc3a2aa75ba3b649c7c6 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Mon, 19 Sep 2022 11:30:03 +0300 Subject: habanalabs: failure to open device due to reset is debug level If the user wants to open the device, and the device is currently in reset, the user will get an error from the open(). We don't need to display an error in the dmesg for that as it is not a real error and we can spam the kernel log with this message. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/habanalabs_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/habanalabs/common/habanalabs_drv.c b/drivers/misc/habanalabs/common/habanalabs_drv.c index 5250bfb6790e..112632afe7d5 100644 --- a/drivers/misc/habanalabs/common/habanalabs_drv.c +++ b/drivers/misc/habanalabs/common/habanalabs_drv.c @@ -161,7 +161,7 @@ int hl_device_open(struct inode *inode, struct file *filp) mutex_lock(&hdev->fpriv_list_lock); if (!hl_device_operational(hdev, &status)) { - dev_err_ratelimited(hdev->dev, + dev_dbg_ratelimited(hdev->dev, "Can't open %s because it is %s\n", dev_name(hdev->dev), hdev->status[status]); @@ -271,7 +271,7 @@ int hl_device_open_ctrl(struct inode *inode, struct file *filp) mutex_lock(&hdev->fpriv_ctrl_list_lock); if (!hl_device_operational(hdev, NULL)) { - dev_err_ratelimited(hdev->dev_ctrl, + dev_dbg_ratelimited(hdev->dev_ctrl, "Can't open %s because it is disabled or in reset\n", dev_name(hdev->dev_ctrl)); rc = -EPERM; -- cgit v1.2.3 From bb677d527e14184d89012ce332128f3767fa9925 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Thu, 15 Sep 2022 11:10:56 +0300 Subject: habanalabs/gaudi2: allow user to flush PCIE by read In order for the user to flush PCIE he needs to read some register from PCIE block. The chosen register is SPECIAL_GLBL_SPARE_0 and hence needs to be unsecured. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi2/gaudi2_security.c | 7 +- .../include/gaudi2/asic_reg/gaudi2_regs.h | 1 + .../gaudi2/asic_reg/pcie_wrap_special_regs.h | 185 +++++++++++++++++++++ 3 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_wrap_special_regs.h diff --git a/drivers/misc/habanalabs/gaudi2/gaudi2_security.c b/drivers/misc/habanalabs/gaudi2/gaudi2_security.c index c4165db06db2..c6906fb14229 100644 --- a/drivers/misc/habanalabs/gaudi2/gaudi2_security.c +++ b/drivers/misc/habanalabs/gaudi2/gaudi2_security.c @@ -2559,6 +2559,10 @@ static const u32 gaudi2_pb_pcie[] = { mmPCIE_WRAP_BASE, }; +static const u32 gaudi2_pb_pcie_unsecured_regs[] = { + mmPCIE_WRAP_SPECIAL_GLBL_SPARE_0, +}; + static const u32 gaudi2_pb_thermal_sensor0[] = { mmDCORE0_XFT_BASE, mmDCORE0_TSTDVS_BASE, @@ -3418,7 +3422,8 @@ static int gaudi2_init_protection_bits(struct hl_device *hdev) rc |= hl_init_pb(hdev, HL_PB_SHARED, HL_PB_NA, HL_PB_SINGLE_INSTANCE, HL_PB_NA, gaudi2_pb_pcie, ARRAY_SIZE(gaudi2_pb_pcie), - NULL, HL_PB_NA); + gaudi2_pb_pcie_unsecured_regs, + ARRAY_SIZE(gaudi2_pb_pcie_unsecured_regs)); /* Thermal Sensor. * Skip when security is enabled in F/W, because the blocks are protected by privileged RR. diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/gaudi2_regs.h b/drivers/misc/habanalabs/include/gaudi2/asic_reg/gaudi2_regs.h index bfda4223bdc8..6aa1b1412462 100644 --- a/drivers/misc/habanalabs/include/gaudi2/asic_reg/gaudi2_regs.h +++ b/drivers/misc/habanalabs/include/gaudi2/asic_reg/gaudi2_regs.h @@ -132,6 +132,7 @@ #include "dcore0_mme_ctrl_lo_arch_tensor_a_regs.h" #include "dcore0_mme_ctrl_lo_arch_tensor_b_regs.h" #include "dcore0_mme_ctrl_lo_arch_tensor_cout_regs.h" +#include "pcie_wrap_special_regs.h" #include "pdma0_qm_masks.h" #include "pdma0_core_masks.h" diff --git a/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_wrap_special_regs.h b/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_wrap_special_regs.h new file mode 100644 index 000000000000..46558e7a7f63 --- /dev/null +++ b/drivers/misc/habanalabs/include/gaudi2/asic_reg/pcie_wrap_special_regs.h @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright 2016-2020 HabanaLabs, Ltd. + * All Rights Reserved. + * + */ + +/************************************ + ** This is an auto-generated file ** + ** DO NOT EDIT BELOW ** + ************************************/ + +#ifndef ASIC_REG_PCIE_WRAP_SPECIAL_REGS_H_ +#define ASIC_REG_PCIE_WRAP_SPECIAL_REGS_H_ + +/* + ***************************************** + * PCIE_WRAP_SPECIAL + * (Prototype: SPECIAL_REGS) + ***************************************** + */ + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_0 0x4C01E80 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_1 0x4C01E84 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_2 0x4C01E88 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_3 0x4C01E8C + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_4 0x4C01E90 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_5 0x4C01E94 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_6 0x4C01E98 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_7 0x4C01E9C + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_8 0x4C01EA0 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_9 0x4C01EA4 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_10 0x4C01EA8 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_11 0x4C01EAC + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_12 0x4C01EB0 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_13 0x4C01EB4 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_14 0x4C01EB8 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_15 0x4C01EBC + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_16 0x4C01EC0 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_17 0x4C01EC4 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_18 0x4C01EC8 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_19 0x4C01ECC + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_20 0x4C01ED0 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_21 0x4C01ED4 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_22 0x4C01ED8 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_23 0x4C01EDC + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_24 0x4C01EE0 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_25 0x4C01EE4 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_26 0x4C01EE8 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_27 0x4C01EEC + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_28 0x4C01EF0 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_29 0x4C01EF4 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_30 0x4C01EF8 + +#define mmPCIE_WRAP_SPECIAL_GLBL_PRIV_31 0x4C01EFC + +#define mmPCIE_WRAP_SPECIAL_MEM_GW_DATA 0x4C01F00 + +#define mmPCIE_WRAP_SPECIAL_MEM_GW_REQ 0x4C01F04 + +#define mmPCIE_WRAP_SPECIAL_MEM_NUMOF 0x4C01F0C + +#define mmPCIE_WRAP_SPECIAL_MEM_ECC_SEL 0x4C01F10 + +#define mmPCIE_WRAP_SPECIAL_MEM_ECC_CTL 0x4C01F14 + +#define mmPCIE_WRAP_SPECIAL_MEM_ECC_ERR_MASK 0x4C01F18 + +#define mmPCIE_WRAP_SPECIAL_MEM_ECC_GLBL_ERR_MASK 0x4C01F1C + +#define mmPCIE_WRAP_SPECIAL_MEM_ECC_ERR_STS 0x4C01F20 + +#define mmPCIE_WRAP_SPECIAL_MEM_ECC_ERR_ADDR 0x4C01F24 + +#define mmPCIE_WRAP_SPECIAL_MEM_RM 0x4C01F28 + +#define mmPCIE_WRAP_SPECIAL_GLBL_ERR_MASK 0x4C01F40 + +#define mmPCIE_WRAP_SPECIAL_GLBL_ERR_ADDR 0x4C01F44 + +#define mmPCIE_WRAP_SPECIAL_GLBL_ERR_CAUSE 0x4C01F48 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SPARE_0 0x4C01F60 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SPARE_1 0x4C01F64 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SPARE_2 0x4C01F68 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SPARE_3 0x4C01F6C + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_0 0x4C01F80 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_1 0x4C01F84 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_2 0x4C01F88 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_3 0x4C01F8C + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_4 0x4C01F90 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_5 0x4C01F94 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_6 0x4C01F98 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_7 0x4C01F9C + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_8 0x4C01FA0 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_9 0x4C01FA4 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_10 0x4C01FA8 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_11 0x4C01FAC + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_12 0x4C01FB0 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_13 0x4C01FB4 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_14 0x4C01FB8 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_15 0x4C01FBC + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_16 0x4C01FC0 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_17 0x4C01FC4 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_18 0x4C01FC8 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_19 0x4C01FCC + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_20 0x4C01FD0 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_21 0x4C01FD4 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_22 0x4C01FD8 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_23 0x4C01FDC + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_24 0x4C01FE0 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_25 0x4C01FE4 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_26 0x4C01FE8 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_27 0x4C01FEC + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_28 0x4C01FF0 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_29 0x4C01FF4 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_30 0x4C01FF8 + +#define mmPCIE_WRAP_SPECIAL_GLBL_SEC_31 0x4C01FFC + +#endif /* ASIC_REG_PCIE_WRAP_SPECIAL_REGS_H_ */ -- cgit v1.2.3 From 8412bb69ed789464adadf7f0906971c7be29e204 Mon Sep 17 00:00:00 2001 From: Ohad Sharabi Date: Thu, 15 Sep 2022 09:19:03 +0300 Subject: habanalabs: build ASICs from new to old Newer ASICs code changes more often, has more chance to fail compilation. So, let's compile them first so errors in those files will fail compilation sooner. Signed-off-by: Ohad Sharabi Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/misc/habanalabs/Makefile b/drivers/misc/habanalabs/Makefile index b35d7000c86b..a48a9e0969ed 100644 --- a/drivers/misc/habanalabs/Makefile +++ b/drivers/misc/habanalabs/Makefile @@ -8,13 +8,13 @@ obj-$(CONFIG_HABANA_AI) := habanalabs.o include $(src)/common/Makefile habanalabs-y += $(HL_COMMON_FILES) -include $(src)/goya/Makefile -habanalabs-y += $(HL_GOYA_FILES) +include $(src)/gaudi2/Makefile +habanalabs-y += $(HL_GAUDI2_FILES) include $(src)/gaudi/Makefile habanalabs-y += $(HL_GAUDI_FILES) -include $(src)/gaudi2/Makefile -habanalabs-y += $(HL_GAUDI2_FILES) +include $(src)/goya/Makefile +habanalabs-y += $(HL_GOYA_FILES) habanalabs-$(CONFIG_DEBUG_FS) += common/debugfs.o -- cgit v1.2.3 From 6b9b9e244fdd0d6c5ee21b7b9d74282d9e43733a Mon Sep 17 00:00:00 2001 From: farah kassabri Date: Tue, 30 Aug 2022 13:01:03 +0300 Subject: habanalabs: remove some f/w descriptor validations To be forward-backward compatible with the firmware in the initial communication during preboot, we need to remove the validation of the header size. This will allow us to add more fields to the lkd_fw_comms_desc structure. Instead of the validation of the header size, we just print warning when some mismatch in descriptor has been revealed, and we calculate the CRC base on descriptor size reported by the firmware instead of calculating it ourselves. Signed-off-by: farah kassabri Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/firmware_if.c | 43 +++++++++------------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index 26a7529083e1..2de6a9bd564d 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -1900,50 +1900,36 @@ static int hl_fw_dynamic_validate_descriptor(struct hl_device *hdev, u64 addr; int rc; - if (le32_to_cpu(fw_desc->header.magic) != HL_COMMS_DESC_MAGIC) { - dev_err(hdev->dev, "Invalid magic for dynamic FW descriptor (%x)\n", + if (le32_to_cpu(fw_desc->header.magic) != HL_COMMS_DESC_MAGIC) + dev_warn(hdev->dev, "Invalid magic for dynamic FW descriptor (%x)\n", fw_desc->header.magic); - return -EIO; - } - if (fw_desc->header.version != HL_COMMS_DESC_VER) { - dev_err(hdev->dev, "Invalid version for dynamic FW descriptor (%x)\n", + if (fw_desc->header.version != HL_COMMS_DESC_VER) + dev_warn(hdev->dev, "Invalid version for dynamic FW descriptor (%x)\n", fw_desc->header.version); - return -EIO; - } /* - * calc CRC32 of data without header. + * Calc CRC32 of data without header. use the size of the descriptor + * reported by firmware, without calculating it ourself, to allow adding + * more fields to the lkd_fw_comms_desc structure. * note that no alignment/stride address issues here as all structures - * are 64 bit padded + * are 64 bit padded. */ - data_size = sizeof(struct lkd_fw_comms_desc) - - sizeof(struct comms_desc_header); data_ptr = (u8 *)fw_desc + sizeof(struct comms_desc_header); - - if (le16_to_cpu(fw_desc->header.size) != data_size) { - dev_err(hdev->dev, - "Invalid descriptor size 0x%x, expected size 0x%zx\n", - le16_to_cpu(fw_desc->header.size), data_size); - return -EIO; - } + data_size = le16_to_cpu(fw_desc->header.size); data_crc32 = hl_fw_compat_crc32(data_ptr, data_size); - if (data_crc32 != le32_to_cpu(fw_desc->header.crc32)) { - dev_err(hdev->dev, - "CRC32 mismatch for dynamic FW descriptor (%x:%x)\n", - data_crc32, fw_desc->header.crc32); + dev_err(hdev->dev, "CRC32 mismatch for dynamic FW descriptor (%x:%x)\n", + data_crc32, fw_desc->header.crc32); return -EIO; } /* find memory region to which to copy the image */ addr = le64_to_cpu(fw_desc->img_addr); region_id = hl_get_pci_memory_region(hdev, addr); - if ((region_id != PCI_REGION_SRAM) && - ((region_id != PCI_REGION_DRAM))) { - dev_err(hdev->dev, - "Invalid region to copy FW image address=%llx\n", addr); + if ((region_id != PCI_REGION_SRAM) && ((region_id != PCI_REGION_DRAM))) { + dev_err(hdev->dev, "Invalid region to copy FW image address=%llx\n", addr); return -EIO; } @@ -1960,8 +1946,7 @@ static int hl_fw_dynamic_validate_descriptor(struct hl_device *hdev, fw_loader->dynamic_loader.fw_image_size, region); if (rc) { - dev_err(hdev->dev, - "invalid mem transfer request for FW image\n"); + dev_err(hdev->dev, "invalid mem transfer request for FW image\n"); return rc; } -- cgit v1.2.3 From e403856468456aeaff68a5cb0a851d945c133ed9 Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Mon, 19 Sep 2022 18:46:03 +0300 Subject: habanalabs/gaudi: use 8KB aligned address for TPC kernels I$ prefetch is enabled when sending a TPC kernel to initialize the TPC memory, and it has a restriction that the base address will be aligned to 8KB. Currently the base address is 128 bytes from the start address of the device SRAM, so prefetching will start 128 bytes before the actual kernel memory. Modify the kernel address to be 8KB aligned. Signed-off-by: Tomer Tayar Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi/gaudi.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index e80ebace49c8..92560414e843 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -984,9 +984,10 @@ static int _gaudi_init_tpc_mem(struct hl_device *hdev, init_tpc_mem_pkt->ctl = cpu_to_le32(ctl); init_tpc_mem_pkt->src_addr = cpu_to_le64(tpc_kernel_src_addr); - dst_addr = (prop->sram_user_base_address & - GAUDI_PKT_LIN_DMA_DST_ADDR_MASK) >> - GAUDI_PKT_LIN_DMA_DST_ADDR_SHIFT; + + /* TPC_CMD is configured with I$ prefetch enabled, so address should be aligned to 8KB */ + dst_addr = FIELD_PREP(GAUDI_PKT_LIN_DMA_DST_ADDR_MASK, + round_up(prop->sram_user_base_address, SZ_8K)); init_tpc_mem_pkt->dst_addr |= cpu_to_le64(dst_addr); job = hl_cs_allocate_job(hdev, QUEUE_TYPE_EXT, true); -- cgit v1.2.3 From 259cee1c2422bcff7ba6bb4e8179faadb52ebdee Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Tue, 20 Sep 2022 00:08:40 +0300 Subject: habanalabs: eliminate aggregate use warning When doing sizeof() and giving as argument a dereference of a pointer-to-a-pointer object, clang will issue a warning. Eliminate the warning by passing struct * Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_submission.c | 4 ++-- drivers/misc/habanalabs/common/device.c | 4 ++-- drivers/misc/habanalabs/common/hw_queue.c | 4 +--- drivers/misc/habanalabs/common/hwmon.c | 3 ++- drivers/misc/habanalabs/common/memory.c | 3 +-- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index fbe5003191bf..fa05770865c6 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -2571,7 +2571,7 @@ static int hl_cs_poll_fences(struct multi_cs_data *mcs_data, struct multi_cs_com ktime_t max_ktime, first_cs_time; enum hl_cs_wait_status status; - memset(fence_ptr, 0, arr_len * sizeof(*fence_ptr)); + memset(fence_ptr, 0, arr_len * sizeof(struct hl_fence *)); /* get all fences under the same lock */ rc = hl_ctx_get_fences(mcs_data->ctx, seq_arr, fence_ptr, arr_len); @@ -2873,7 +2873,7 @@ static int hl_multi_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data) } /* allocate array for the fences */ - fence_arr = kmalloc_array(seq_arr_len, sizeof(*fence_arr), GFP_KERNEL); + fence_arr = kmalloc_array(seq_arr_len, sizeof(struct hl_fence *), GFP_KERNEL); if (!fence_arr) { rc = -ENOMEM; goto free_seq_arr; diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index c6a00bb259fb..233d8b46c831 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -719,7 +719,7 @@ static int device_early_init(struct hl_device *hdev) if (hdev->asic_prop.completion_queues_count) { hdev->cq_wq = kcalloc(hdev->asic_prop.completion_queues_count, - sizeof(*hdev->cq_wq), + sizeof(struct workqueue_struct *), GFP_KERNEL); if (!hdev->cq_wq) { rc = -ENOMEM; @@ -1863,7 +1863,7 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass) } hdev->shadow_cs_queue = kcalloc(hdev->asic_prop.max_pending_cs, - sizeof(*hdev->shadow_cs_queue), GFP_KERNEL); + sizeof(struct hl_cs *), GFP_KERNEL); if (!hdev->shadow_cs_queue) { rc = -ENOMEM; goto cq_fini; diff --git a/drivers/misc/habanalabs/common/hw_queue.c b/drivers/misc/habanalabs/common/hw_queue.c index 3f15ab9d827f..d0087c0ec48c 100644 --- a/drivers/misc/habanalabs/common/hw_queue.c +++ b/drivers/misc/habanalabs/common/hw_queue.c @@ -826,9 +826,7 @@ static int ext_and_cpu_queue_init(struct hl_device *hdev, struct hl_hw_queue *q, q->kernel_address = p; - q->shadow_queue = kmalloc_array(HL_QUEUE_LENGTH, - sizeof(*q->shadow_queue), - GFP_KERNEL); + q->shadow_queue = kmalloc_array(HL_QUEUE_LENGTH, sizeof(struct hl_cs_job *), GFP_KERNEL); if (!q->shadow_queue) { dev_err(hdev->dev, "Failed to allocate shadow queue for H/W queue %d\n", diff --git a/drivers/misc/habanalabs/common/hwmon.c b/drivers/misc/habanalabs/common/hwmon.c index 8c262aeb425e..55eb0203817f 100644 --- a/drivers/misc/habanalabs/common/hwmon.c +++ b/drivers/misc/habanalabs/common/hwmon.c @@ -194,7 +194,8 @@ int hl_build_hwmon_channel_info(struct hl_device *hdev, struct cpucp_sensor *sen curr_arr[sensors_by_type_next_index[type]++] = flags; } - channels_info = kcalloc(num_active_sensor_types + 1, sizeof(*channels_info), GFP_KERNEL); + channels_info = kcalloc(num_active_sensor_types + 1, sizeof(struct hwmon_channel_info *), + GFP_KERNEL); if (!channels_info) { rc = -ENOMEM; goto channels_info_array_err; diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c index 096fa3c1ae95..ef28f3b37b93 100644 --- a/drivers/misc/habanalabs/common/memory.c +++ b/drivers/misc/habanalabs/common/memory.c @@ -2308,8 +2308,7 @@ static int get_user_memory(struct hl_device *hdev, u64 addr, u64 size, return -EFAULT; } - userptr->pages = kvmalloc_array(npages, sizeof(*userptr->pages), - GFP_KERNEL); + userptr->pages = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL); if (!userptr->pages) return -ENOMEM; -- cgit v1.2.3 From 7aa429e8d40ed7e8ab3c0ff5e2836c051ab2434a Mon Sep 17 00:00:00 2001 From: Huang Yiwei Date: Wed, 14 Sep 2022 14:41:22 +0800 Subject: interconnect: qcom: Kconfig: Make INTERCONNECT_QCOM tristate Make INTERCONNECT_QCOM tristate so that icc-common.c can be compiled as a module. Signed-off-by: Huang Yiwei Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220914064122.16222-1-quic_hyiwei@quicinc.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/Kconfig | 2 +- drivers/interconnect/qcom/icc-common.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index 25d5b4baf6f6..1a1c941635a2 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only config INTERCONNECT_QCOM - bool "Qualcomm Network-on-Chip interconnect drivers" + tristate "Qualcomm Network-on-Chip interconnect drivers" depends on ARCH_QCOM help Support for Qualcomm's Network-on-Chip interconnect hardware. diff --git a/drivers/interconnect/qcom/icc-common.c b/drivers/interconnect/qcom/icc-common.c index 0822ce207b5d..f27f4fdc4531 100644 --- a/drivers/interconnect/qcom/icc-common.c +++ b/drivers/interconnect/qcom/icc-common.c @@ -5,6 +5,7 @@ #include #include +#include #include "icc-common.h" @@ -32,3 +33,5 @@ struct icc_node_data *qcom_icc_xlate_extended(struct of_phandle_args *spec, void return ndata; } EXPORT_SYMBOL_GPL(qcom_icc_xlate_extended); + +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From b402783711bfe93e9da0e3f9eab3aee95cece29d Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Fri, 9 Sep 2022 17:16:09 +0100 Subject: dt-bindings: iio: st,st-sensors add LPS22DF. Add support for the ST LPS22DF, an I2C/SPI pressure sensor. Signed-off-by: Martyn Welch Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220909161611.780720-1-martyn.welch@collabora.co.uk Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/st,st-sensors.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/st,st-sensors.yaml b/Documentation/devicetree/bindings/iio/st,st-sensors.yaml index fcb2902683c7..250439b13152 100644 --- a/Documentation/devicetree/bindings/iio/st,st-sensors.yaml +++ b/Documentation/devicetree/bindings/iio/st,st-sensors.yaml @@ -73,6 +73,7 @@ properties: - description: STMicroelectronics Pressure Sensors enum: - st,lps001wp-press + - st,lps22df - st,lps22hb-press - st,lps22hh - st,lps25h-press @@ -141,6 +142,7 @@ allOf: - st,lis2mdl - st,lis3l02dq - st,lis3lv02dl-accel + - st,lps22df - st,lps22hb-press - st,lps22hh - st,lps25h-press -- cgit v1.2.3 From 92ba0ab945aee1f3d23b045c893290380e3e059c Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Fri, 9 Sep 2022 17:16:10 +0100 Subject: iio: pressure: st_pressure: Add support for LPS22DF Add support for ST LPS22DF pressure sensor. Datasheet: https://www.st.com/resource/en/datasheet/lps22df.pdf Signed-off-by: Martyn Welch Link: https://lore.kernel.org/r/20220909161611.780720-2-martyn.welch@collabora.co.uk Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/st_pressure.h | 2 + drivers/iio/pressure/st_pressure_core.c | 70 +++++++++++++++++++++++++++++++++ drivers/iio/pressure/st_pressure_i2c.c | 5 +++ drivers/iio/pressure/st_pressure_spi.c | 5 +++ 4 files changed, 82 insertions(+) diff --git a/drivers/iio/pressure/st_pressure.h b/drivers/iio/pressure/st_pressure.h index 156e6a72dc5c..6e11bea784fa 100644 --- a/drivers/iio/pressure/st_pressure.h +++ b/drivers/iio/pressure/st_pressure.h @@ -22,6 +22,7 @@ enum st_press_type { LPS33HW, LPS35HW, LPS22HH, + LPS22DF, ST_PRESS_MAX, }; @@ -32,6 +33,7 @@ enum st_press_type { #define LPS33HW_PRESS_DEV_NAME "lps33hw" #define LPS35HW_PRESS_DEV_NAME "lps35hw" #define LPS22HH_PRESS_DEV_NAME "lps22hh" +#define LPS22DF_PRESS_DEV_NAME "lps22df" /** * struct st_sensors_platform_data - default press platform data diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index 76913a2028d2..80176e3083af 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -552,6 +552,76 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .multi_read_bit = false, .bootime = 2, }, + { + /* + * CUSTOM VALUES FOR LPS22DF SENSOR + * See LPS22DF datasheet: + * http://www.st.com/resource/en/datasheet/lps22df.pdf + */ + .wai = 0xb4, + .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, + .sensors_supported = { + [0] = LPS22DF_PRESS_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_press_lps22hb_channels, + .num_ch = ARRAY_SIZE(st_press_lps22hb_channels), + .odr = { + .addr = 0x10, + .mask = 0x78, + .odr_avl = { + { .hz = 1, .value = 0x01 }, + { .hz = 4, .value = 0x02 }, + { .hz = 10, .value = 0x03 }, + { .hz = 25, .value = 0x04 }, + { .hz = 50, .value = 0x05 }, + { .hz = 75, .value = 0x06 }, + { .hz = 100, .value = 0x07 }, + { .hz = 200, .value = 0x08 }, + }, + }, + .pw = { + .addr = 0x10, + .mask = 0x78, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .fs = { + .fs_avl = { + /* + * Pressure and temperature sensitivity values + * as defined in table 2 of LPS22DF datasheet. + */ + [0] = { + .num = ST_PRESS_FS_AVL_1260MB, + .gain = ST_PRESS_KPASCAL_NANO_SCALE, + .gain2 = ST_PRESS_LPS22HB_LSB_PER_CELSIUS, + }, + }, + }, + .bdu = { + .addr = 0x11, + .mask = BIT(3), + }, + .drdy_irq = { + .int1 = { + .addr = 0x13, + .mask = BIT(5), + .addr_od = 0x12, + .mask_od = BIT(1), + }, + .addr_ihl = 0x12, + .mask_ihl = BIT(3), + .stat_drdy = { + .addr = ST_SENSORS_DEFAULT_STAT_ADDR, + .mask = 0x03, + }, + }, + .sim = { + .addr = 0x0E, + .value = BIT(5), + }, + .multi_read_bit = false, + .bootime = 2, + }, }; static int st_press_write_raw(struct iio_dev *indio_dev, diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c index 7035777fd988..58fede861891 100644 --- a/drivers/iio/pressure/st_pressure_i2c.c +++ b/drivers/iio/pressure/st_pressure_i2c.c @@ -47,6 +47,10 @@ static const struct of_device_id st_press_of_match[] = { .compatible = "st,lps22hh", .data = LPS22HH_PRESS_DEV_NAME, }, + { + .compatible = "st,lps22df", + .data = LPS22DF_PRESS_DEV_NAME, + }, {}, }; MODULE_DEVICE_TABLE(of, st_press_of_match); @@ -67,6 +71,7 @@ static const struct i2c_device_id st_press_id_table[] = { { LPS33HW_PRESS_DEV_NAME, LPS33HW }, { LPS35HW_PRESS_DEV_NAME, LPS35HW }, { LPS22HH_PRESS_DEV_NAME, LPS22HH }, + { LPS22DF_PRESS_DEV_NAME, LPS22DF }, {}, }; MODULE_DEVICE_TABLE(i2c, st_press_id_table); diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c index bfab8e7fb061..25cca5ad7c55 100644 --- a/drivers/iio/pressure/st_pressure_spi.c +++ b/drivers/iio/pressure/st_pressure_spi.c @@ -51,6 +51,10 @@ static const struct of_device_id st_press_of_match[] = { .compatible = "st,lps22hh", .data = LPS22HH_PRESS_DEV_NAME, }, + { + .compatible = "st,lps22df", + .data = LPS22DF_PRESS_DEV_NAME, + }, {}, }; MODULE_DEVICE_TABLE(of, st_press_of_match); @@ -97,6 +101,7 @@ static const struct spi_device_id st_press_id_table[] = { { LPS33HW_PRESS_DEV_NAME }, { LPS35HW_PRESS_DEV_NAME }, { LPS22HH_PRESS_DEV_NAME }, + { LPS22DF_PRESS_DEV_NAME }, { "lps001wp-press" }, { "lps25h-press", }, { "lps331ap-press" }, -- cgit v1.2.3 From 5d5129b17f8315d317db01a3f6e050e8ca23952f Mon Sep 17 00:00:00 2001 From: Angel Iglesias Date: Tue, 13 Sep 2022 00:26:44 +0200 Subject: iio: pressure: bmp280: fix datasheet links Updated links for BMP280 and BME280 datasheets on Bosch website. Datasheet of BMP180 is no longer available on the manufacturer's website, changed the link to a copy hosted by a third party. Reported-by: Andy Shevchenko Signed-off-by: Angel Iglesias Link: https://lore.kernel.org/r/20220912222645.377874-1-ang.iglesiasg@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-core.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index fe7aa81e7cc9..01cd32003ca8 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -9,9 +9,15 @@ * Driver for Bosch Sensortec BMP180 and BMP280 digital pressure sensor. * * Datasheet: - * https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMP180-DS000-121.pdf - * https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMP280-DS001-12.pdf - * https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME280_DS001-11.pdf + * https://cdn-shop.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf + * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf + * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf + * + * Notice: + * The link to the bmp180 datasheet points to an outdated version missing these changes: + * - Changed document referral from ANP015 to BST-MPS-AN004-00 on page 26 + * - Updated equation for B3 param on section 3.5 to ((((long)AC1 * 4 + X3) << oss) + 2) / 4 + * - Updated RoHS directive to 2011/65/EU effective 8 June 2011 on page 26 */ #define pr_fmt(fmt) "bmp280: " fmt -- cgit v1.2.3 From abf90541d19c17a29bf6137febe71bb2770896f3 Mon Sep 17 00:00:00 2001 From: George Mois Date: Mon, 5 Sep 2022 16:20:17 +0300 Subject: dt-bindings: iio: accel: adxl313: Add compatibles for adxl312 and adxl314 Extend the adi,adxl313.yaml file with information regrding the ADXL312 and ADXL314 devices. Signed-off-by: George Mois Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220905132018.364900-1-george.mois@analog.com Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml index 05fa7af409cc..185b68ffb536 100644 --- a/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml +++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml @@ -4,20 +4,24 @@ $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 +title: Analog Devices ADXL312, ADXL313, and ADXL314 3-Axis Digital Accelerometers maintainers: - Lucas Stankus description: | - Analog Devices ADXL313 3-Axis Digital Accelerometer that supports - both I2C & SPI interfaces. + Analog Devices ADXL312, ADXL313, and ADXL314 3-Axis Digital Accelerometer that + support both I2C & SPI interfaces. + https://www.analog.com/en/products/adxl312.html https://www.analog.com/en/products/adxl313.html + https://www.analog.com/en/products/adxl314.html properties: compatible: enum: + - adi,adxl312 - adi,adxl313 + - adi,adxl314 reg: maxItems: 1 -- cgit v1.2.3 From a7a1c60bc4c9159aa3a4cb3e1814145666c3a00c Mon Sep 17 00:00:00 2001 From: George Mois Date: Mon, 5 Sep 2022 16:20:18 +0300 Subject: drivers: iio: accel: adxl312 and adxl314 support ADXL312 and ADXL314 are small, thin, low power, 3-axis accelerometers with high resolution (13-bit) measurement up to +/-12 g and +/- 200 g respectively. Implement support for ADXL312 and ADXL314 by extending the ADXL313 driver. Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL312.pdf Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL314.pdf Signed-off-by: George Mois Link: https://lore.kernel.org/r/20220905132018.364900-2-george.mois@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl313.h | 35 ++++++- drivers/iio/accel/adxl313_core.c | 202 ++++++++++++++++++++++++++++----------- drivers/iio/accel/adxl313_i2c.c | 74 +++++++++----- drivers/iio/accel/adxl313_spi.c | 63 +++++++++--- 4 files changed, 281 insertions(+), 93 deletions(-) diff --git a/drivers/iio/accel/adxl313.h b/drivers/iio/accel/adxl313.h index 4415f2fc07e1..72f624af4686 100644 --- a/drivers/iio/accel/adxl313.h +++ b/drivers/iio/accel/adxl313.h @@ -8,6 +8,8 @@ #ifndef _ADXL313_H_ #define _ADXL313_H_ +#include + /* ADXL313 register definitions */ #define ADXL313_REG_DEVID0 0x00 #define ADXL313_REG_DEVID1 0x01 @@ -26,6 +28,7 @@ #define ADXL313_REG_FIFO_STATUS 0x39 #define ADXL313_DEVID0 0xAD +#define ADXL313_DEVID0_ADXL312_314 0xE5 #define ADXL313_DEVID1 0x1D #define ADXL313_PARTID 0xCB #define ADXL313_SOFT_RESET 0x52 @@ -37,18 +40,46 @@ #define ADXL313_MEASUREMENT_MODE BIT(3) #define ADXL313_RANGE_MSK GENMASK(1, 0) -#define ADXL313_RANGE_4G 3 +#define ADXL313_RANGE_MAX 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 adxl312_readable_regs_table; extern const struct regmap_access_table adxl313_readable_regs_table; +extern const struct regmap_access_table adxl314_readable_regs_table; +extern const struct regmap_access_table adxl312_writable_regs_table; extern const struct regmap_access_table adxl313_writable_regs_table; +extern const struct regmap_access_table adxl314_writable_regs_table; + +enum adxl313_device_type { + ADXL312, + ADXL313, + ADXL314, +}; + +struct adxl313_data { + struct regmap *regmap; + const struct adxl313_chip_info *chip_info; + struct mutex lock; /* lock to protect transf_buf */ + __le16 transf_buf __aligned(IIO_DMA_MINALIGN); +}; + +struct adxl313_chip_info { + const char *name; + enum adxl313_device_type type; + int scale_factor; + bool variable_range; + bool soft_reset; + int (*check_id)(struct device *dev, struct adxl313_data *data); +}; + +extern const struct adxl313_chip_info adxl31x_chip_info[]; int adxl313_core_probe(struct device *dev, struct regmap *regmap, - const char *name, + const struct adxl313_chip_info *chip_info, 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 index afeef779e1d0..4de0a41bd679 100644 --- a/drivers/iio/accel/adxl313_core.c +++ b/drivers/iio/accel/adxl313_core.c @@ -8,12 +8,18 @@ */ #include -#include #include #include #include "adxl313.h" +static const struct regmap_range adxl312_readable_reg_range[] = { + regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_DEVID0), + 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), +}; + 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), @@ -22,12 +28,109 @@ static const struct regmap_range adxl313_readable_reg_range[] = { regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_FIFO_STATUS), }; +const struct regmap_access_table adxl312_readable_regs_table = { + .yes_ranges = adxl312_readable_reg_range, + .n_yes_ranges = ARRAY_SIZE(adxl312_readable_reg_range), +}; +EXPORT_SYMBOL_NS_GPL(adxl312_readable_regs_table, IIO_ADXL313); + 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_NS_GPL(adxl313_readable_regs_table, IIO_ADXL313); +const struct regmap_access_table adxl314_readable_regs_table = { + .yes_ranges = adxl312_readable_reg_range, + .n_yes_ranges = ARRAY_SIZE(adxl312_readable_reg_range), +}; +EXPORT_SYMBOL_NS_GPL(adxl314_readable_regs_table, IIO_ADXL313); + +static int adxl312_check_id(struct device *dev, + struct adxl313_data *data) +{ + unsigned int regval; + int ret; + + ret = regmap_read(data->regmap, ADXL313_REG_DEVID0, ®val); + if (ret) + return ret; + + if (regval != ADXL313_DEVID0_ADXL312_314) + dev_warn(dev, "Invalid manufacturer ID: %#02x\n", regval); + + return 0; +} + +static int adxl313_check_id(struct device *dev, + struct adxl313_data *data) +{ + unsigned int regval; + int ret; + + ret = regmap_read(data->regmap, ADXL313_REG_DEVID0, ®val); + if (ret) + return ret; + + if (regval != ADXL313_DEVID0) + dev_warn(dev, "Invalid manufacturer ID: 0x%02x\n", regval); + + /* Check DEVID1 and PARTID */ + if (regval == ADXL313_DEVID0) { + ret = regmap_read(data->regmap, ADXL313_REG_DEVID1, ®val); + if (ret) + return ret; + + if (regval != ADXL313_DEVID1) + dev_warn(dev, "Invalid mems ID: 0x%02x\n", regval); + + ret = regmap_read(data->regmap, ADXL313_REG_PARTID, ®val); + if (ret) + return ret; + + if (regval != ADXL313_PARTID) + dev_warn(dev, "Invalid device ID: 0x%02x\n", regval); + } + + return 0; +} + +const struct adxl313_chip_info adxl31x_chip_info[] = { + [ADXL312] = { + .name = "adxl312", + .type = ADXL312, + .scale_factor = 28425072, + .variable_range = true, + .soft_reset = false, + .check_id = &adxl312_check_id, + }, + [ADXL313] = { + .name = "adxl313", + .type = ADXL313, + .scale_factor = 9576806, + .variable_range = true, + .soft_reset = true, + .check_id = &adxl313_check_id, + }, + [ADXL314] = { + .name = "adxl314", + .type = ADXL314, + .scale_factor = 478858719, + .variable_range = false, + .soft_reset = false, + .check_id = &adxl312_check_id, + }, +}; +EXPORT_SYMBOL_NS_GPL(adxl31x_chip_info, IIO_ADXL313); + +static const struct regmap_range adxl312_writable_reg_range[] = { + 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), +}; + 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)), @@ -37,17 +140,23 @@ static const struct regmap_range adxl313_writable_reg_range[] = { regmap_reg_range(ADXL313_REG_FIFO_CTL, ADXL313_REG_FIFO_CTL), }; +const struct regmap_access_table adxl312_writable_regs_table = { + .yes_ranges = adxl312_writable_reg_range, + .n_yes_ranges = ARRAY_SIZE(adxl312_writable_reg_range), +}; +EXPORT_SYMBOL_NS_GPL(adxl312_writable_regs_table, IIO_ADXL313); + 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_NS_GPL(adxl313_writable_regs_table, IIO_ADXL313); -struct adxl313_data { - struct regmap *regmap; - struct mutex lock; /* lock to protect transf_buf */ - __le16 transf_buf __aligned(IIO_DMA_MINALIGN); +const struct regmap_access_table adxl314_writable_regs_table = { + .yes_ranges = adxl312_writable_reg_range, + .n_yes_ranges = ARRAY_SIZE(adxl312_writable_reg_range), }; +EXPORT_SYMBOL_NS_GPL(adxl314_writable_regs_table, IIO_ADXL313); static const int adxl313_odr_freqs[][2] = { [0] = { 6, 250000 }, @@ -156,12 +265,10 @@ static int adxl313_read_raw(struct iio_dev *indio_dev, *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; + + *val2 = data->chip_info->scale_factor; + return IIO_VAL_INT_PLUS_NANO; case IIO_CHAN_INFO_CALIBBIAS: ret = regmap_read(data->regmap, @@ -170,7 +277,7 @@ static int adxl313_read_raw(struct iio_dev *indio_dev, return ret; /* - * 8-bit resolution at +/- 0.5g, that is 4x accel data scale + * 8-bit resolution at minimum range, that is 4x accel data scale * factor at full resolution */ *val = sign_extend32(regval, 7) * 4; @@ -198,7 +305,7 @@ static int adxl313_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_CALIBBIAS: /* - * 8-bit resolution at +/- 0.5g, that is 4x accel data scale + * 8-bit resolution at minimum range, that is 4x accel data scale * factor at full resolution */ if (clamp_val(val, -128 * 4, 127 * 4) != val) @@ -223,14 +330,18 @@ static const struct iio_info adxl313_info = { 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 sw reset available, ensures the device is in a consistent + * state after start up + */ + if (data->chip_info->soft_reset) { + ret = regmap_write(data->regmap, ADXL313_REG_SOFT_RESET, + ADXL313_SOFT_RESET); + if (ret) + return ret; + } if (setup) { ret = setup(dev, data->regmap); @@ -238,46 +349,25 @@ static int adxl313_setup(struct device *dev, struct adxl313_data *data, return ret; } - ret = regmap_read(data->regmap, ADXL313_REG_DEVID0, ®val); + ret = data->chip_info->check_id(dev, data); 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; + /* Sets the range to maximum, full resolution, if applicable */ + if (data->chip_info->variable_range) { + ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT, + ADXL313_RANGE_MSK, + FIELD_PREP(ADXL313_RANGE_MSK, ADXL313_RANGE_MAX)); + if (ret) + return ret; - if (regval != ADXL313_PARTID) { - dev_err(dev, "Invalid device ID: 0x%02x\n", regval); - return -ENODEV; + /* Enables full resolution */ + ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT, + ADXL313_FULL_RES, ADXL313_FULL_RES); + if (ret) + return ret; } - /* 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, @@ -288,7 +378,7 @@ static int adxl313_setup(struct device *dev, struct adxl313_data *data, * 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 + * @chip_info: Structure containing device specific data * @setup: Setup routine to be executed right before the standard device * setup, can also be set to NULL if not required * @@ -296,7 +386,7 @@ static int adxl313_setup(struct device *dev, struct adxl313_data *data, */ int adxl313_core_probe(struct device *dev, struct regmap *regmap, - const char *name, + const struct adxl313_chip_info *chip_info, int (*setup)(struct device *, struct regmap *)) { struct adxl313_data *data; @@ -309,9 +399,11 @@ int adxl313_core_probe(struct device *dev, data = iio_priv(indio_dev); data->regmap = regmap; + data->chip_info = chip_info; + mutex_init(&data->lock); - indio_dev->name = name; + indio_dev->name = chip_info->name; indio_dev->info = &adxl313_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = adxl313_channels; diff --git a/drivers/iio/accel/adxl313_i2c.c b/drivers/iio/accel/adxl313_i2c.c index c329765dbf60..99cc7fc29488 100644 --- a/drivers/iio/accel/adxl313_i2c.c +++ b/drivers/iio/accel/adxl313_i2c.c @@ -14,42 +14,72 @@ #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 const struct regmap_config adxl31x_i2c_regmap_config[] = { + [ADXL312] = { + .reg_bits = 8, + .val_bits = 8, + .rd_table = &adxl312_readable_regs_table, + .wr_table = &adxl312_writable_regs_table, + .max_register = 0x39, + }, + [ADXL313] = { + .reg_bits = 8, + .val_bits = 8, + .rd_table = &adxl313_readable_regs_table, + .wr_table = &adxl313_writable_regs_table, + .max_register = 0x39, + }, + [ADXL314] = { + .reg_bits = 8, + .val_bits = 8, + .rd_table = &adxl314_readable_regs_table, + .wr_table = &adxl314_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" }, + { .name = "adxl312", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] }, + { .name = "adxl313", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] }, + { .name = "adxl314", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] }, { } }; MODULE_DEVICE_TABLE(i2c, adxl313_i2c_id); static const struct of_device_id adxl313_of_match[] = { - { .compatible = "adi,adxl313" }, + { .compatible = "adi,adxl312", .data = &adxl31x_chip_info[ADXL312] }, + { .compatible = "adi,adxl313", .data = &adxl31x_chip_info[ADXL313] }, + { .compatible = "adi,adxl314", .data = &adxl31x_chip_info[ADXL314] }, { } }; MODULE_DEVICE_TABLE(of, adxl313_of_match); +static int adxl313_i2c_probe(struct i2c_client *client) +{ + const struct adxl313_chip_info *chip_data; + struct regmap *regmap; + + /* + * Retrieves device specific data as a pointer to a + * adxl313_chip_info structure + */ + chip_data = device_get_match_data(&client->dev); + if (!chip_data) + chip_data = (const struct adxl313_chip_info *)i2c_match_id(adxl313_i2c_id, client)->driver_data; + + regmap = devm_regmap_init_i2c(client, + &adxl31x_i2c_regmap_config[chip_data->type]); + 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, chip_data, NULL); +} + static struct i2c_driver adxl313_i2c_driver = { .driver = { .name = "adxl313_i2c", diff --git a/drivers/iio/accel/adxl313_spi.c b/drivers/iio/accel/adxl313_spi.c index a3c6d553462d..b7cc15678a2b 100644 --- a/drivers/iio/accel/adxl313_spi.c +++ b/drivers/iio/accel/adxl313_spi.c @@ -11,17 +11,38 @@ #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 const struct regmap_config adxl31x_spi_regmap_config[] = { + [ADXL312] = { + .reg_bits = 8, + .val_bits = 8, + .rd_table = &adxl312_readable_regs_table, + .wr_table = &adxl312_writable_regs_table, + .max_register = 0x39, + /* Setting bits 7 and 6 enables multiple-byte read */ + .read_flag_mask = BIT(7) | BIT(6), + }, + [ADXL313] = { + .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), + }, + [ADXL314] = { + .reg_bits = 8, + .val_bits = 8, + .rd_table = &adxl314_readable_regs_table, + .wr_table = &adxl314_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) @@ -42,7 +63,7 @@ static int adxl313_spi_setup(struct device *dev, struct regmap *regmap) static int adxl313_spi_probe(struct spi_device *spi) { - const struct spi_device_id *id = spi_get_device_id(spi); + const struct adxl313_chip_info *chip_data; struct regmap *regmap; int ret; @@ -51,26 +72,40 @@ static int adxl313_spi_probe(struct spi_device *spi) if (ret) return ret; - regmap = devm_regmap_init_spi(spi, &adxl313_spi_regmap_config); + /* + * Retrieves device specific data as a pointer to a + * adxl313_chip_info structure + */ + chip_data = device_get_match_data(&spi->dev); + if (!chip_data) + chip_data = (const struct adxl313_chip_info *)spi_get_device_id(spi)->driver_data; + + regmap = devm_regmap_init_spi(spi, + &adxl31x_spi_regmap_config[chip_data->type]); + 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); + return adxl313_core_probe(&spi->dev, regmap, + chip_data, &adxl313_spi_setup); } static const struct spi_device_id adxl313_spi_id[] = { - { "adxl313" }, + { .name = "adxl312", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] }, + { .name = "adxl313", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL313] }, + { .name = "adxl314", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL314] }, { } }; MODULE_DEVICE_TABLE(spi, adxl313_spi_id); static const struct of_device_id adxl313_of_match[] = { - { .compatible = "adi,adxl313" }, + { .compatible = "adi,adxl312", .data = &adxl31x_chip_info[ADXL312] }, + { .compatible = "adi,adxl313", .data = &adxl31x_chip_info[ADXL313] }, + { .compatible = "adi,adxl314", .data = &adxl31x_chip_info[ADXL314] }, { } }; -- cgit v1.2.3 From d9d0c0725ae2798bd51f38ae26c2477f6eed40b3 Mon Sep 17 00:00:00 2001 From: Crt Mori Date: Tue, 6 Sep 2022 13:26:32 +0200 Subject: iio: temperature: mlx90614 Refactoring available filter attributes Change/refactor to the new way of defining available attribute values. Signed-off-by: Crt Mori Link: https://lore.kernel.org/r/20220906112632.244453-1-cmo@melexis.com Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/mlx90614.c | 41 ++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c index c253a5315988..3157416be912 100644 --- a/drivers/iio/temperature/mlx90614.c +++ b/drivers/iio/temperature/mlx90614.c @@ -79,16 +79,15 @@ struct mlx90614_data { /* Bandwidth values for IIR filtering */ static const int mlx90614_iir_values[] = {77, 31, 20, 15, 723, 153, 110, 86}; -static IIO_CONST_ATTR(in_temp_object_filter_low_pass_3db_frequency_available, - "0.15 0.20 0.31 0.77 0.86 1.10 1.53 7.23"); - -static struct attribute *mlx90614_attributes[] = { - &iio_const_attr_in_temp_object_filter_low_pass_3db_frequency_available.dev_attr.attr, - NULL, -}; - -static const struct attribute_group mlx90614_attr_group = { - .attrs = mlx90614_attributes, +static const int mlx90614_freqs[][2] = { + {0, 150000}, + {0, 200000}, + {0, 310000}, + {0, 770000}, + {0, 860000}, + {1, 100000}, + {1, 530000}, + {7, 230000} }; /* @@ -373,6 +372,22 @@ static int mlx90614_write_raw_get_fmt(struct iio_dev *indio_dev, } } +static int mlx90614_read_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_LOW_PASS_FILTER_3DB_FREQUENCY: + *vals = (int *)mlx90614_freqs; + *type = IIO_VAL_INT_PLUS_MICRO; + *length = 2 * ARRAY_SIZE(mlx90614_freqs); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + static const struct iio_chan_spec mlx90614_channels[] = { { .type = IIO_TEMP, @@ -389,6 +404,8 @@ static const struct iio_chan_spec mlx90614_channels[] = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_CALIBEMISSIVITY) | BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), }, @@ -401,6 +418,8 @@ static const struct iio_chan_spec mlx90614_channels[] = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_CALIBEMISSIVITY) | BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), }, @@ -410,7 +429,7 @@ static const struct iio_info mlx90614_info = { .read_raw = mlx90614_read_raw, .write_raw = mlx90614_write_raw, .write_raw_get_fmt = mlx90614_write_raw_get_fmt, - .attrs = &mlx90614_attr_group, + .read_avail = mlx90614_read_avail, }; #ifdef CONFIG_PM -- cgit v1.2.3 From 558a25f903b4af6361b7fbeea08a6446a0745653 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Tue, 13 Sep 2022 09:34:12 +0200 Subject: iio: dac: ad5593r: Fix i2c read protocol requirements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For reliable operation across the full range of supported interface rates, the AD5593R needs a STOP condition between address write, and data read (like show in the datasheet Figure 40) so in turn i2c_smbus_read_word_swapped cannot be used. While at it, a simple helper was added to make the code simpler. Fixes: 56ca9db862bf ("iio: dac: Add support for the AD5592R/AD5593R ADCs/DACs") Signed-off-by: Michael Hennerich Signed-off-by: Nuno Sá Cc: Link: https://lore.kernel.org/r/20220913073413.140475-2-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5593r.c | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/drivers/iio/dac/ad5593r.c b/drivers/iio/dac/ad5593r.c index 34e1319a9712..356dc0bab115 100644 --- a/drivers/iio/dac/ad5593r.c +++ b/drivers/iio/dac/ad5593r.c @@ -13,6 +13,8 @@ #include #include +#include + #define AD5593R_MODE_CONF (0 << 4) #define AD5593R_MODE_DAC_WRITE (1 << 4) #define AD5593R_MODE_ADC_READBACK (4 << 4) @@ -20,6 +22,24 @@ #define AD5593R_MODE_GPIO_READBACK (6 << 4) #define AD5593R_MODE_REG_READBACK (7 << 4) +static int ad5593r_read_word(struct i2c_client *i2c, u8 reg, u16 *value) +{ + int ret; + u8 buf[2]; + + ret = i2c_smbus_write_byte(i2c, reg); + if (ret < 0) + return ret; + + ret = i2c_master_recv(i2c, buf, sizeof(buf)); + if (ret < 0) + return ret; + + *value = get_unaligned_be16(buf); + + return 0; +} + static int ad5593r_write_dac(struct ad5592r_state *st, unsigned chan, u16 value) { struct i2c_client *i2c = to_i2c_client(st->dev); @@ -38,13 +58,7 @@ static int ad5593r_read_adc(struct ad5592r_state *st, unsigned chan, u16 *value) if (val < 0) return (int) val; - val = i2c_smbus_read_word_swapped(i2c, AD5593R_MODE_ADC_READBACK); - if (val < 0) - return (int) val; - - *value = (u16) val; - - return 0; + return ad5593r_read_word(i2c, AD5593R_MODE_ADC_READBACK, value); } static int ad5593r_reg_write(struct ad5592r_state *st, u8 reg, u16 value) @@ -58,25 +72,19 @@ static int ad5593r_reg_write(struct ad5592r_state *st, u8 reg, u16 value) static int ad5593r_reg_read(struct ad5592r_state *st, u8 reg, u16 *value) { struct i2c_client *i2c = to_i2c_client(st->dev); - s32 val; - - val = i2c_smbus_read_word_swapped(i2c, AD5593R_MODE_REG_READBACK | reg); - if (val < 0) - return (int) val; - *value = (u16) val; - - return 0; + return ad5593r_read_word(i2c, AD5593R_MODE_REG_READBACK | reg, value); } static int ad5593r_gpio_read(struct ad5592r_state *st, u8 *value) { struct i2c_client *i2c = to_i2c_client(st->dev); - s32 val; + u16 val; + int ret; - val = i2c_smbus_read_word_swapped(i2c, AD5593R_MODE_GPIO_READBACK); - if (val < 0) - return (int) val; + ret = ad5593r_read_word(i2c, AD5593R_MODE_GPIO_READBACK, &val); + if (ret) + return ret; *value = (u8) val; -- cgit v1.2.3 From f13c81a3359ccfb2cd8ea2d0609d7d7a91e3dde7 Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Tue, 13 Sep 2022 09:34:13 +0200 Subject: iio: dac: ad5593r: add check for i2c functionality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure that the needed i2c functionality is supported during probe. Signed-off-by: Nuno Sá Signed-off-by: Michael Hennerich Link: https://lore.kernel.org/r/20220913073413.140475-3-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5593r.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/iio/dac/ad5593r.c b/drivers/iio/dac/ad5593r.c index 356dc0bab115..420981e7c5c3 100644 --- a/drivers/iio/dac/ad5593r.c +++ b/drivers/iio/dac/ad5593r.c @@ -102,6 +102,10 @@ static const struct ad5592r_rw_ops ad5593r_rw_ops = { static int ad5593r_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + if (!i2c_check_functionality(i2c->adapter, + I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C)) + return -EOPNOTSUPP; + return ad5592r_probe(&i2c->dev, id->name, &ad5593r_rw_ops); } -- cgit v1.2.3 From 5f0c359defea73c0ca27fb47a3a891abf2f5a504 Mon Sep 17 00:00:00 2001 From: Angel Iglesias Date: Tue, 13 Sep 2022 01:44:52 +0200 Subject: iio: pressure: bmp280: reorder local variables following reverse xmas tree Reordered definitions of local variables following the reverse christmas tree convention. Suggested-by: Andy Shevchenko Signed-off-by: Angel Iglesias Link: https://lore.kernel.org/r/363a106afbfe30ce590b80b1494c8b3322870f8a.1663025017.git.ang.iglesiasg@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-core.c | 65 +++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index 01cd32003ca8..d9d0c1c7f843 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -157,13 +157,14 @@ static int bmp280_read_calib(struct bmp280_data *data, struct bmp280_calib *calib, unsigned int chip) { - int ret; + __le16 p_buf[BMP280_COMP_PRESS_REG_COUNT / 2]; + __le16 t_buf[BMP280_COMP_TEMP_REG_COUNT / 2]; + struct device *dev = data->dev; unsigned int tmp; __le16 l16; __be16 b16; - struct device *dev = data->dev; - __le16 t_buf[BMP280_COMP_TEMP_REG_COUNT / 2]; - __le16 p_buf[BMP280_COMP_PRESS_REG_COUNT / 2]; + int ret; + /* Read temperature calibration values. */ ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_TEMP_START, @@ -267,8 +268,8 @@ static int bmp280_read_calib(struct bmp280_data *data, static u32 bmp280_compensate_humidity(struct bmp280_data *data, s32 adc_humidity) { - s32 var; struct bmp280_calib *calib = &data->calib.bmp280; + s32 var; var = ((s32)data->t_fine) - (s32)76800; var = ((((adc_humidity << 14) - (calib->H4 << 20) - (calib->H5 * var)) @@ -292,8 +293,8 @@ static u32 bmp280_compensate_humidity(struct bmp280_data *data, static s32 bmp280_compensate_temp(struct bmp280_data *data, s32 adc_temp) { - s32 var1, var2; struct bmp280_calib *calib = &data->calib.bmp280; + s32 var1, var2; var1 = (((adc_temp >> 3) - ((s32)calib->T1 << 1)) * ((s32)calib->T2)) >> 11; @@ -315,8 +316,8 @@ static s32 bmp280_compensate_temp(struct bmp280_data *data, static u32 bmp280_compensate_press(struct bmp280_data *data, s32 adc_press) { - s64 var1, var2, p; struct bmp280_calib *calib = &data->calib.bmp280; + s64 var1, var2, p; var1 = ((s64)data->t_fine) - 128000; var2 = var1 * var1 * (s64)calib->P6; @@ -341,9 +342,9 @@ static u32 bmp280_compensate_press(struct bmp280_data *data, static int bmp280_read_temp(struct bmp280_data *data, int *val) { - int ret; - __be32 tmp = 0; s32 adc_temp, comp_temp; + __be32 tmp = 0; + int ret; ret = regmap_bulk_read(data->regmap, BMP280_REG_TEMP_MSB, &tmp, 3); if (ret < 0) { @@ -374,10 +375,10 @@ static int bmp280_read_temp(struct bmp280_data *data, static int bmp280_read_press(struct bmp280_data *data, int *val, int *val2) { - int ret; + u32 comp_press; __be32 tmp = 0; s32 adc_press; - u32 comp_press; + int ret; /* Read and compensate temperature so we get a reading of t_fine. */ ret = bmp280_read_temp(data, NULL); @@ -406,10 +407,10 @@ static int bmp280_read_press(struct bmp280_data *data, static int bmp280_read_humid(struct bmp280_data *data, int *val, int *val2) { + u32 comp_humidity; + s32 adc_humidity; __be16 tmp; int ret; - s32 adc_humidity; - u32 comp_humidity; /* Read and compensate temperature so we get a reading of t_fine. */ ret = bmp280_read_temp(data, NULL); @@ -439,8 +440,8 @@ static int bmp280_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { - int ret; struct bmp280_data *data = iio_priv(indio_dev); + int ret; pm_runtime_get_sync(data->dev); mutex_lock(&data->lock); @@ -496,9 +497,9 @@ static int bmp280_read_raw(struct iio_dev *indio_dev, static int bmp280_write_oversampling_ratio_humid(struct bmp280_data *data, int val) { - int i; const int *avail = data->chip_info->oversampling_humid_avail; const int n = data->chip_info->num_oversampling_humid_avail; + int i; for (i = 0; i < n; i++) { if (avail[i] == val) { @@ -513,9 +514,9 @@ static int bmp280_write_oversampling_ratio_humid(struct bmp280_data *data, static int bmp280_write_oversampling_ratio_temp(struct bmp280_data *data, int val) { - int i; const int *avail = data->chip_info->oversampling_temp_avail; const int n = data->chip_info->num_oversampling_temp_avail; + int i; for (i = 0; i < n; i++) { if (avail[i] == val) { @@ -530,9 +531,9 @@ static int bmp280_write_oversampling_ratio_temp(struct bmp280_data *data, static int bmp280_write_oversampling_ratio_press(struct bmp280_data *data, int val) { - int i; const int *avail = data->chip_info->oversampling_press_avail; const int n = data->chip_info->num_oversampling_press_avail; + int i; for (i = 0; i < n; i++) { if (avail[i] == val) { @@ -548,8 +549,8 @@ static int bmp280_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { - int ret = 0; struct bmp280_data *data = iio_priv(indio_dev); + int ret = 0; switch (mask) { case IIO_CHAN_INFO_OVERSAMPLING_RATIO: @@ -616,9 +617,9 @@ static const struct iio_info bmp280_info = { static int bmp280_chip_config(struct bmp280_data *data) { - int ret; u8 osrs = BMP280_OSRS_TEMP_X(data->oversampling_temp + 1) | BMP280_OSRS_PRESS_X(data->oversampling_press + 1); + int ret; ret = regmap_write_bits(data->regmap, BMP280_REG_CTRL_MEAS, BMP280_OSRS_TEMP_MASK | @@ -659,8 +660,8 @@ static const struct bmp280_chip_info bmp280_chip_info = { static int bme280_chip_config(struct bmp280_data *data) { - int ret; u8 osrs = BMP280_OSRS_HUMIDITIY_X(data->oversampling_humid + 1); + int ret; /* * Oversampling of humidity must be set before oversampling of @@ -693,10 +694,10 @@ static const struct bmp280_chip_info bme280_chip_info = { static int bmp180_measure(struct bmp280_data *data, u8 ctrl_meas) { - int ret; const int conversion_time_max[] = { 4500, 7500, 13500, 25500 }; unsigned int delay_us; unsigned int ctrl; + int ret; if (data->use_eoc) reinit_completion(&data->done); @@ -757,9 +758,9 @@ static int bmp180_read_adc_temp(struct bmp280_data *data, int *val) static int bmp180_read_calib(struct bmp280_data *data, struct bmp180_calib *calib) { + __be16 buf[BMP180_REG_CALIB_COUNT / 2]; int ret; int i; - __be16 buf[BMP180_REG_CALIB_COUNT / 2]; ret = regmap_bulk_read(data->regmap, BMP180_REG_CALIB_START, buf, sizeof(buf)); @@ -799,8 +800,8 @@ static int bmp180_read_calib(struct bmp280_data *data, */ static s32 bmp180_compensate_temp(struct bmp280_data *data, s32 adc_temp) { - s32 x1, x2; struct bmp180_calib *calib = &data->calib.bmp180; + s32 x1, x2; x1 = ((adc_temp - calib->AC6) * calib->AC5) >> 15; x2 = (calib->MC << 11) / (x1 + calib->MD); @@ -811,8 +812,8 @@ static s32 bmp180_compensate_temp(struct bmp280_data *data, s32 adc_temp) static int bmp180_read_temp(struct bmp280_data *data, int *val) { - int ret; s32 adc_temp, comp_temp; + int ret; ret = bmp180_read_adc_temp(data, &adc_temp); if (ret) @@ -834,9 +835,9 @@ static int bmp180_read_temp(struct bmp280_data *data, int *val) static int bmp180_read_adc_press(struct bmp280_data *data, int *val) { - int ret; - __be32 tmp = 0; u8 oss = data->oversampling_press; + __be32 tmp = 0; + int ret; ret = bmp180_measure(data, BMP180_MEAS_PRESS_X(oss)); if (ret) @@ -858,11 +859,11 @@ static int bmp180_read_adc_press(struct bmp280_data *data, int *val) */ static u32 bmp180_compensate_press(struct bmp280_data *data, s32 adc_press) { + struct bmp180_calib *calib = &data->calib.bmp180; + s32 oss = data->oversampling_press; s32 x1, x2, x3, p; s32 b3, b6; u32 b4, b7; - s32 oss = data->oversampling_press; - struct bmp180_calib *calib = &data->calib.bmp180; b6 = data->t_fine - 4000; x1 = (calib->B2 * (b6 * b6 >> 12)) >> 11; @@ -889,9 +890,9 @@ static u32 bmp180_compensate_press(struct bmp280_data *data, s32 adc_press) static int bmp180_read_press(struct bmp280_data *data, int *val, int *val2) { - int ret; - s32 adc_press; u32 comp_press; + s32 adc_press; + int ret; /* Read and compensate temperature so we get a reading of t_fine. */ ret = bmp180_read_temp(data, NULL); @@ -996,11 +997,11 @@ int bmp280_common_probe(struct device *dev, const char *name, int irq) { - int ret; struct iio_dev *indio_dev; struct bmp280_data *data; - unsigned int chip_id; struct gpio_desc *gpiod; + unsigned int chip_id; + int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) -- cgit v1.2.3 From 2405f8cc8485d7c06fdd7b85a0df1a3febd076d6 Mon Sep 17 00:00:00 2001 From: Angel Iglesias Date: Tue, 13 Sep 2022 01:45:49 +0200 Subject: iio: pressure: bmp280: use FIELD_GET, FIELD_PREP and GENMASK Cleaned and simplified register values construction and extraction converting to use FIELD_PREP and FIELD_GET macros. Replaced hardcoded bit masks with GENMASK macro. Signed-off-by: Angel Iglesias Link: https://lore.kernel.org/r/3cbe56f29c2a46bc5dc23c5b72e1b43c9207f44d.1663025017.git.ang.iglesiasg@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-core.c | 25 +++++++----- drivers/iio/pressure/bmp280.h | 78 ++++++++++++++++++++------------------ 2 files changed, 57 insertions(+), 46 deletions(-) diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index d9d0c1c7f843..74f273b07f33 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -22,6 +22,8 @@ #define pr_fmt(fmt) "bmp280: " fmt +#include +#include #include #include #include @@ -248,7 +250,7 @@ static int bmp280_read_calib(struct bmp280_data *data, dev_err(dev, "failed to read H5 comp value\n"); return ret; } - calib->H5 = sign_extend32(((le16_to_cpu(l16) >> 4) & 0xfff), 11); + calib->H5 = sign_extend32(FIELD_GET(BMP280_COMP_H5_MASK, le16_to_cpu(l16)), 11); ret = regmap_read(data->regmap, BMP280_REG_COMP_H6, &tmp); if (ret < 0) { @@ -352,7 +354,7 @@ static int bmp280_read_temp(struct bmp280_data *data, return ret; } - adc_temp = be32_to_cpu(tmp) >> 12; + adc_temp = FIELD_GET(BMP280_MEAS_TRIM_MASK, be32_to_cpu(tmp)); if (adc_temp == BMP280_TEMP_SKIPPED) { /* reading was skipped */ dev_err(data->dev, "reading temperature skipped\n"); @@ -391,7 +393,7 @@ static int bmp280_read_press(struct bmp280_data *data, return ret; } - adc_press = be32_to_cpu(tmp) >> 12; + adc_press = FIELD_GET(BMP280_MEAS_TRIM_MASK, be32_to_cpu(tmp)); if (adc_press == BMP280_PRESS_SKIPPED) { /* reading was skipped */ dev_err(data->dev, "reading pressure skipped\n"); @@ -617,8 +619,8 @@ static const struct iio_info bmp280_info = { static int bmp280_chip_config(struct bmp280_data *data) { - u8 osrs = BMP280_OSRS_TEMP_X(data->oversampling_temp + 1) | - BMP280_OSRS_PRESS_X(data->oversampling_press + 1); + u8 osrs = FIELD_PREP(BMP280_OSRS_TEMP_MASK, data->oversampling_temp + 1) | + FIELD_PREP(BMP280_OSRS_PRESS_MASK, data->oversampling_press + 1); int ret; ret = regmap_write_bits(data->regmap, BMP280_REG_CTRL_MEAS, @@ -660,7 +662,7 @@ static const struct bmp280_chip_info bmp280_chip_info = { static int bme280_chip_config(struct bmp280_data *data) { - u8 osrs = BMP280_OSRS_HUMIDITIY_X(data->oversampling_humid + 1); + u8 osrs = FIELD_PREP(BMP280_OSRS_HUMIDITY_MASK, data->oversampling_humid + 1); int ret; /* @@ -717,7 +719,7 @@ static int bmp180_measure(struct bmp280_data *data, u8 ctrl_meas) if (!ret) dev_err(data->dev, "timeout waiting for completion\n"); } else { - if (ctrl_meas == BMP180_MEAS_TEMP) + if (FIELD_GET(BMP180_MEAS_CTRL_MASK, ctrl_meas) == BMP180_MEAS_TEMP) delay_us = 4500; else delay_us = @@ -742,7 +744,9 @@ static int bmp180_read_adc_temp(struct bmp280_data *data, int *val) __be16 tmp; int ret; - ret = bmp180_measure(data, BMP180_MEAS_TEMP); + ret = bmp180_measure(data, + FIELD_PREP(BMP180_MEAS_CTRL_MASK, BMP180_MEAS_TEMP) | + BMP180_MEAS_SCO); if (ret) return ret; @@ -839,7 +843,10 @@ static int bmp180_read_adc_press(struct bmp280_data *data, int *val) __be32 tmp = 0; int ret; - ret = bmp180_measure(data, BMP180_MEAS_PRESS_X(oss)); + ret = bmp180_measure(data, + FIELD_PREP(BMP180_MEAS_CTRL_MASK, BMP180_MEAS_PRESS) | + FIELD_PREP(BMP180_OSRS_PRESS_MASK, oss) | + BMP180_MEAS_SCO); if (ret) return ret; diff --git a/drivers/iio/pressure/bmp280.h b/drivers/iio/pressure/bmp280.h index 57ba0e85db91..4a501836d27a 100644 --- a/drivers/iio/pressure/bmp280.h +++ b/drivers/iio/pressure/bmp280.h @@ -13,6 +13,9 @@ #define BMP280_REG_PRESS_LSB 0xF8 #define BMP280_REG_PRESS_MSB 0xF7 +/* Helper mask to truncate excess 12 bits on pressure and temp readings */ +#define BMP280_MEAS_TRIM_MASK GENMASK(31, 12) + #define BMP280_REG_CONFIG 0xF5 #define BMP280_REG_CTRL_MEAS 0xF4 #define BMP280_REG_STATUS 0xF3 @@ -32,44 +35,43 @@ #define BMP280_REG_COMP_PRESS_START 0x8E #define BMP280_COMP_PRESS_REG_COUNT 18 -#define BMP280_FILTER_MASK (BIT(4) | BIT(3) | BIT(2)) +#define BMP280_COMP_H5_MASK GENMASK(15, 4) + +#define BMP280_FILTER_MASK GENMASK(4, 2) #define BMP280_FILTER_OFF 0 -#define BMP280_FILTER_2X BIT(2) -#define BMP280_FILTER_4X BIT(3) -#define BMP280_FILTER_8X (BIT(3) | BIT(2)) -#define BMP280_FILTER_16X BIT(4) +#define BMP280_FILTER_2X 1 +#define BMP280_FILTER_4X 2 +#define BMP280_FILTER_8X 3 +#define BMP280_FILTER_16X 4 -#define BMP280_OSRS_HUMIDITY_MASK (BIT(2) | BIT(1) | BIT(0)) -#define BMP280_OSRS_HUMIDITIY_X(osrs_h) ((osrs_h) << 0) +#define BMP280_OSRS_HUMIDITY_MASK GENMASK(2, 0) #define BMP280_OSRS_HUMIDITY_SKIP 0 -#define BMP280_OSRS_HUMIDITY_1X BMP280_OSRS_HUMIDITIY_X(1) -#define BMP280_OSRS_HUMIDITY_2X BMP280_OSRS_HUMIDITIY_X(2) -#define BMP280_OSRS_HUMIDITY_4X BMP280_OSRS_HUMIDITIY_X(3) -#define BMP280_OSRS_HUMIDITY_8X BMP280_OSRS_HUMIDITIY_X(4) -#define BMP280_OSRS_HUMIDITY_16X BMP280_OSRS_HUMIDITIY_X(5) +#define BMP280_OSRS_HUMIDITY_1X 1 +#define BMP280_OSRS_HUMIDITY_2X 2 +#define BMP280_OSRS_HUMIDITY_4X 3 +#define BMP280_OSRS_HUMIDITY_8X 4 +#define BMP280_OSRS_HUMIDITY_16X 5 -#define BMP280_OSRS_TEMP_MASK (BIT(7) | BIT(6) | BIT(5)) +#define BMP280_OSRS_TEMP_MASK GENMASK(7, 5) #define BMP280_OSRS_TEMP_SKIP 0 -#define BMP280_OSRS_TEMP_X(osrs_t) ((osrs_t) << 5) -#define BMP280_OSRS_TEMP_1X BMP280_OSRS_TEMP_X(1) -#define BMP280_OSRS_TEMP_2X BMP280_OSRS_TEMP_X(2) -#define BMP280_OSRS_TEMP_4X BMP280_OSRS_TEMP_X(3) -#define BMP280_OSRS_TEMP_8X BMP280_OSRS_TEMP_X(4) -#define BMP280_OSRS_TEMP_16X BMP280_OSRS_TEMP_X(5) - -#define BMP280_OSRS_PRESS_MASK (BIT(4) | BIT(3) | BIT(2)) +#define BMP280_OSRS_TEMP_1X 1 +#define BMP280_OSRS_TEMP_2X 2 +#define BMP280_OSRS_TEMP_4X 3 +#define BMP280_OSRS_TEMP_8X 4 +#define BMP280_OSRS_TEMP_16X 5 + +#define BMP280_OSRS_PRESS_MASK GENMASK(4, 2) #define BMP280_OSRS_PRESS_SKIP 0 -#define BMP280_OSRS_PRESS_X(osrs_p) ((osrs_p) << 2) -#define BMP280_OSRS_PRESS_1X BMP280_OSRS_PRESS_X(1) -#define BMP280_OSRS_PRESS_2X BMP280_OSRS_PRESS_X(2) -#define BMP280_OSRS_PRESS_4X BMP280_OSRS_PRESS_X(3) -#define BMP280_OSRS_PRESS_8X BMP280_OSRS_PRESS_X(4) -#define BMP280_OSRS_PRESS_16X BMP280_OSRS_PRESS_X(5) - -#define BMP280_MODE_MASK (BIT(1) | BIT(0)) +#define BMP280_OSRS_PRESS_1X 1 +#define BMP280_OSRS_PRESS_2X 2 +#define BMP280_OSRS_PRESS_4X 3 +#define BMP280_OSRS_PRESS_8X 4 +#define BMP280_OSRS_PRESS_16X 5 + +#define BMP280_MODE_MASK GENMASK(1, 0) #define BMP280_MODE_SLEEP 0 -#define BMP280_MODE_FORCED BIT(0) -#define BMP280_MODE_NORMAL (BIT(1) | BIT(0)) +#define BMP280_MODE_FORCED 1 +#define BMP280_MODE_NORMAL 3 /* BMP180 specific registers */ #define BMP180_REG_OUT_XLSB 0xF8 @@ -79,13 +81,15 @@ #define BMP180_REG_CALIB_START 0xAA #define BMP180_REG_CALIB_COUNT 22 +#define BMP180_MEAS_CTRL_MASK GENMASK(4, 0) +#define BMP180_MEAS_TEMP 0x0E +#define BMP180_MEAS_PRESS 0x14 #define BMP180_MEAS_SCO BIT(5) -#define BMP180_MEAS_TEMP (0x0E | BMP180_MEAS_SCO) -#define BMP180_MEAS_PRESS_X(oss) ((oss) << 6 | 0x14 | BMP180_MEAS_SCO) -#define BMP180_MEAS_PRESS_1X BMP180_MEAS_PRESS_X(0) -#define BMP180_MEAS_PRESS_2X BMP180_MEAS_PRESS_X(1) -#define BMP180_MEAS_PRESS_4X BMP180_MEAS_PRESS_X(2) -#define BMP180_MEAS_PRESS_8X BMP180_MEAS_PRESS_X(3) +#define BMP180_OSRS_PRESS_MASK GENMASK(7, 6) +#define BMP180_MEAS_PRESS_1X 0 +#define BMP180_MEAS_PRESS_2X 1 +#define BMP180_MEAS_PRESS_4X 2 +#define BMP180_MEAS_PRESS_8X 3 /* BMP180 and BMP280 common registers */ #define BMP280_REG_CTRL_MEAS 0xF4 -- cgit v1.2.3 From 83cb40beaefaf59b224efdabecaac611b783da74 Mon Sep 17 00:00:00 2001 From: Angel Iglesias Date: Tue, 13 Sep 2022 01:46:42 +0200 Subject: iio: pressure: bmp280: Simplify bmp280 calibration data reading On bmp280 and bme280, the temperature and pressure calibration parameters are available on a contiguous memory region. Considering this arrangement, simplified the calibration reading function by using only one buffer to read in batch temperature and pressure registers. Signed-off-by: Angel Iglesias Link: https://lore.kernel.org/r/96d81282c95006d857f4d836d2ff3ee0740a85a0.1663025017.git.ang.iglesiasg@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-core.c | 58 +++++++++++++++----------------------- drivers/iio/pressure/bmp280.h | 3 ++ 2 files changed, 26 insertions(+), 35 deletions(-) diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index 74f273b07f33..c86dae3c5cec 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -134,8 +134,7 @@ struct bmp280_chip_info { * These enums are used for indexing into the array of compensation * parameters for BMP280. */ -enum { T1, T2, T3 }; -enum { P1, P2, P3, P4, P5, P6, P7, P8, P9 }; +enum { T1, T2, T3, P1, P2, P3, P4, P5, P6, P7, P8, P9 }; static const struct iio_chan_spec bmp280_channels[] = { { @@ -159,8 +158,7 @@ static int bmp280_read_calib(struct bmp280_data *data, struct bmp280_calib *calib, unsigned int chip) { - __le16 p_buf[BMP280_COMP_PRESS_REG_COUNT / 2]; - __le16 t_buf[BMP280_COMP_TEMP_REG_COUNT / 2]; + __le16 c_buf[BMP280_CONTIGUOUS_CALIB_REGS / 2]; struct device *dev = data->dev; unsigned int tmp; __le16 l16; @@ -168,43 +166,33 @@ static int bmp280_read_calib(struct bmp280_data *data, int ret; - /* Read temperature calibration values. */ + /* Read temperature and pressure calibration values. */ ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_TEMP_START, - t_buf, BMP280_COMP_TEMP_REG_COUNT); + c_buf, sizeof(c_buf)); if (ret < 0) { dev_err(data->dev, - "failed to read temperature calibration parameters\n"); + "failed to read temperature and pressure calibration parameters\n"); return ret; } - /* Toss the temperature calibration data into the entropy pool */ - add_device_randomness(t_buf, sizeof(t_buf)); - - calib->T1 = le16_to_cpu(t_buf[T1]); - calib->T2 = le16_to_cpu(t_buf[T2]); - calib->T3 = le16_to_cpu(t_buf[T3]); - - /* Read pressure calibration values. */ - ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_PRESS_START, - p_buf, BMP280_COMP_PRESS_REG_COUNT); - if (ret < 0) { - dev_err(data->dev, - "failed to read pressure calibration parameters\n"); - return ret; - } - - /* Toss the pressure calibration data into the entropy pool */ - add_device_randomness(p_buf, sizeof(p_buf)); - - calib->P1 = le16_to_cpu(p_buf[P1]); - calib->P2 = le16_to_cpu(p_buf[P2]); - calib->P3 = le16_to_cpu(p_buf[P3]); - calib->P4 = le16_to_cpu(p_buf[P4]); - calib->P5 = le16_to_cpu(p_buf[P5]); - calib->P6 = le16_to_cpu(p_buf[P6]); - calib->P7 = le16_to_cpu(p_buf[P7]); - calib->P8 = le16_to_cpu(p_buf[P8]); - calib->P9 = le16_to_cpu(p_buf[P9]); + /* Toss the temperature and pressure calibration data into the entropy pool */ + add_device_randomness(c_buf, sizeof(c_buf)); + + /* Parse temperature calibration values. */ + calib->T1 = le16_to_cpu(c_buf[T1]); + calib->T2 = le16_to_cpu(c_buf[T2]); + calib->T3 = le16_to_cpu(c_buf[T3]); + + /* Parse pressure calibration values. */ + calib->P1 = le16_to_cpu(c_buf[P1]); + calib->P2 = le16_to_cpu(c_buf[P2]); + calib->P3 = le16_to_cpu(c_buf[P3]); + calib->P4 = le16_to_cpu(c_buf[P4]); + calib->P5 = le16_to_cpu(c_buf[P5]); + calib->P6 = le16_to_cpu(c_buf[P6]); + calib->P7 = le16_to_cpu(c_buf[P7]); + calib->P8 = le16_to_cpu(c_buf[P8]); + calib->P9 = le16_to_cpu(c_buf[P9]); /* * Read humidity calibration values. diff --git a/drivers/iio/pressure/bmp280.h b/drivers/iio/pressure/bmp280.h index 4a501836d27a..03a539223417 100644 --- a/drivers/iio/pressure/bmp280.h +++ b/drivers/iio/pressure/bmp280.h @@ -37,6 +37,9 @@ #define BMP280_COMP_H5_MASK GENMASK(15, 4) +#define BMP280_CONTIGUOUS_CALIB_REGS (BMP280_COMP_TEMP_REG_COUNT + \ + BMP280_COMP_PRESS_REG_COUNT) + #define BMP280_FILTER_MASK GENMASK(4, 2) #define BMP280_FILTER_OFF 0 #define BMP280_FILTER_2X 1 -- cgit v1.2.3 From b00e805a47a86fb5890a1c2451e4d89043b1761d Mon Sep 17 00:00:00 2001 From: Angel Iglesias Date: Tue, 13 Sep 2022 01:47:31 +0200 Subject: iio: pressure: bmp280: simplify driver initialization logic Simplified common initialization logic of different sensor types unifying calibration and initial configuration recovery. Default config param values of each sensor type are stored inside chip_info structure and used to initialize sensor data struct instance. The helper functions for read each sensor type calibration are converted to a callback available on the chip_info struct. Separated bme280 specific calibration code from bmp280 function. Dropped the additional chip_id argument in bmp280 code as is not longer required. Now both bmp280/bme280 calibration function use same signature as bmp180. Suggested-by: Jonathan Cameron Signed-off-by: Angel Iglesias Link: https://lore.kernel.org/r/584c90f309e4f24bf2e4aa2b15c8577d288f978d.1663025017.git.ang.iglesiasg@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-core.c | 126 ++++++++++++++++++++++++------------- 1 file changed, 83 insertions(+), 43 deletions(-) diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index c86dae3c5cec..067e56ba6c84 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -115,19 +115,28 @@ struct bmp280_data { }; struct bmp280_chip_info { + unsigned int id_reg; + + int num_channels; + unsigned int start_up_time; + const int *oversampling_temp_avail; int num_oversampling_temp_avail; + int oversampling_temp_default; const int *oversampling_press_avail; int num_oversampling_press_avail; + int oversampling_press_default; const int *oversampling_humid_avail; int num_oversampling_humid_avail; + int oversampling_humid_default; int (*chip_config)(struct bmp280_data *); int (*read_temp)(struct bmp280_data *, int *); int (*read_press)(struct bmp280_data *, int *, int *); int (*read_humid)(struct bmp280_data *, int *, int *); + int (*read_calib)(struct bmp280_data *); }; /* @@ -154,15 +163,10 @@ static const struct iio_chan_spec bmp280_channels[] = { }, }; -static int bmp280_read_calib(struct bmp280_data *data, - struct bmp280_calib *calib, - unsigned int chip) +static int bmp280_read_calib(struct bmp280_data *data) { + struct bmp280_calib *calib = &data->calib.bmp280; __le16 c_buf[BMP280_CONTIGUOUS_CALIB_REGS / 2]; - struct device *dev = data->dev; - unsigned int tmp; - __le16 l16; - __be16 b16; int ret; @@ -194,6 +198,25 @@ static int bmp280_read_calib(struct bmp280_data *data, calib->P8 = le16_to_cpu(c_buf[P8]); calib->P9 = le16_to_cpu(c_buf[P9]); + return 0; +} + +static int bme280_read_calib(struct bmp280_data *data) +{ + struct bmp280_calib *calib = &data->calib.bmp280; + struct device *dev = data->dev; + unsigned int tmp; + __le16 l16; + __be16 b16; + int ret; + + /* Load shared calibration params with bmp280 first */ + ret = bmp280_read_calib(data); + if (ret < 0) { + dev_err(dev, "failed to read common bmp280 calibration parameters\n"); + return ret; + } + /* * Read humidity calibration values. * Due to some odd register addressing we cannot just @@ -201,8 +224,6 @@ static int bmp280_read_calib(struct bmp280_data *data, * value separately and sometimes do some bit shifting... * Humidity data is only available on BME280. */ - if (chip != BME280_CHIP_ID) - return 0; ret = regmap_read(data->regmap, BMP280_REG_COMP_H1, &tmp); if (ret < 0) { @@ -637,15 +658,32 @@ static int bmp280_chip_config(struct bmp280_data *data) static const int bmp280_oversampling_avail[] = { 1, 2, 4, 8, 16 }; static const struct bmp280_chip_info bmp280_chip_info = { + .id_reg = BMP280_REG_ID, + .start_up_time = 2000, + .num_channels = 2, + .oversampling_temp_avail = bmp280_oversampling_avail, .num_oversampling_temp_avail = ARRAY_SIZE(bmp280_oversampling_avail), + /* + * Oversampling config values on BMx280 have one additional setting + * that other generations of the family don't: + * The value 0 means the measurement is bypassed instead of + * oversampling set to x1. + * + * To account for this difference, and preserve the same common + * config logic, this is handled later on chip_config callback + * incrementing one unit the oversampling setting. + */ + .oversampling_temp_default = BMP280_OSRS_TEMP_2X - 1, .oversampling_press_avail = bmp280_oversampling_avail, .num_oversampling_press_avail = ARRAY_SIZE(bmp280_oversampling_avail), + .oversampling_press_default = BMP280_OSRS_PRESS_16X - 1, .chip_config = bmp280_chip_config, .read_temp = bmp280_read_temp, .read_press = bmp280_read_press, + .read_calib = bmp280_read_calib, }; static int bme280_chip_config(struct bmp280_data *data) @@ -667,19 +705,27 @@ static int bme280_chip_config(struct bmp280_data *data) } static const struct bmp280_chip_info bme280_chip_info = { + .id_reg = BMP280_REG_ID, + .start_up_time = 2000, + .num_channels = 3, + .oversampling_temp_avail = bmp280_oversampling_avail, .num_oversampling_temp_avail = ARRAY_SIZE(bmp280_oversampling_avail), + .oversampling_temp_default = BMP280_OSRS_TEMP_2X - 1, .oversampling_press_avail = bmp280_oversampling_avail, .num_oversampling_press_avail = ARRAY_SIZE(bmp280_oversampling_avail), + .oversampling_press_default = BMP280_OSRS_PRESS_16X - 1, .oversampling_humid_avail = bmp280_oversampling_avail, .num_oversampling_humid_avail = ARRAY_SIZE(bmp280_oversampling_avail), + .oversampling_humid_default = BMP280_OSRS_HUMIDITY_16X - 1, .chip_config = bme280_chip_config, .read_temp = bmp280_read_temp, .read_press = bmp280_read_press, .read_humid = bmp280_read_humid, + .read_calib = bme280_read_calib, }; static int bmp180_measure(struct bmp280_data *data, u8 ctrl_meas) @@ -747,9 +793,9 @@ static int bmp180_read_adc_temp(struct bmp280_data *data, int *val) return 0; } -static int bmp180_read_calib(struct bmp280_data *data, - struct bmp180_calib *calib) +static int bmp180_read_calib(struct bmp280_data *data) { + struct bmp180_calib *calib = &data->calib.bmp180; __be16 buf[BMP180_REG_CALIB_COUNT / 2]; int ret; int i; @@ -915,17 +961,24 @@ static const int bmp180_oversampling_temp_avail[] = { 1 }; static const int bmp180_oversampling_press_avail[] = { 1, 2, 4, 8 }; static const struct bmp280_chip_info bmp180_chip_info = { + .id_reg = BMP280_REG_ID, + .start_up_time = 2000, + .num_channels = 2, + .oversampling_temp_avail = bmp180_oversampling_temp_avail, .num_oversampling_temp_avail = ARRAY_SIZE(bmp180_oversampling_temp_avail), + .oversampling_temp_default = 0, .oversampling_press_avail = bmp180_oversampling_press_avail, .num_oversampling_press_avail = ARRAY_SIZE(bmp180_oversampling_press_avail), + .oversampling_press_default = BMP180_MEAS_PRESS_8X, .chip_config = bmp180_chip_config, .read_temp = bmp180_read_temp, .read_press = bmp180_read_press, + .read_calib = bmp180_read_calib, }; static irqreturn_t bmp085_eoc_irq(int irq, void *d) @@ -992,6 +1045,7 @@ int bmp280_common_probe(struct device *dev, const char *name, int irq) { + const struct bmp280_chip_info *chip_info; struct iio_dev *indio_dev; struct bmp280_data *data; struct gpio_desc *gpiod; @@ -1013,30 +1067,25 @@ int bmp280_common_probe(struct device *dev, switch (chip) { case BMP180_CHIP_ID: - indio_dev->num_channels = 2; - data->chip_info = &bmp180_chip_info; - data->oversampling_press = ilog2(8); - data->oversampling_temp = ilog2(1); - data->start_up_time = 10000; + chip_info = &bmp180_chip_info; break; case BMP280_CHIP_ID: - indio_dev->num_channels = 2; - data->chip_info = &bmp280_chip_info; - data->oversampling_press = ilog2(16); - data->oversampling_temp = ilog2(2); - data->start_up_time = 2000; + chip_info = &bmp280_chip_info; break; case BME280_CHIP_ID: - indio_dev->num_channels = 3; - data->chip_info = &bme280_chip_info; - data->oversampling_press = ilog2(16); - data->oversampling_humid = ilog2(16); - data->oversampling_temp = ilog2(2); - data->start_up_time = 2000; + chip_info = &bme280_chip_info; break; default: return -EINVAL; } + data->chip_info = chip_info; + + /* Apply initial values from chip info structure */ + indio_dev->num_channels = chip_info->num_channels; + data->oversampling_press = chip_info->oversampling_press_default; + data->oversampling_humid = chip_info->oversampling_humid_default; + data->oversampling_temp = chip_info->oversampling_temp_default; + data->start_up_time = chip_info->start_up_time; /* Bring up regulators */ regulator_bulk_set_supply_names(data->supplies, @@ -1073,7 +1122,8 @@ int bmp280_common_probe(struct device *dev, } data->regmap = regmap; - ret = regmap_read(regmap, BMP280_REG_ID, &chip_id); + + ret = regmap_read(regmap, data->chip_info->id_reg, &chip_id); if (ret < 0) return ret; if (chip_id != chip) { @@ -1093,21 +1143,11 @@ int bmp280_common_probe(struct device *dev, * non-volatile memory during production". Let's read them out at probe * time once. They will not change. */ - if (chip_id == BMP180_CHIP_ID) { - ret = bmp180_read_calib(data, &data->calib.bmp180); - if (ret < 0) { - dev_err(data->dev, - "failed to read calibration coefficients\n"); - return ret; - } - } else if (chip_id == BMP280_CHIP_ID || chip_id == BME280_CHIP_ID) { - ret = bmp280_read_calib(data, &data->calib.bmp280, chip_id); - if (ret < 0) { - dev_err(data->dev, - "failed to read calibration coefficients\n"); - return ret; - } - } + + ret = data->chip_info->read_calib(data); + if (ret < 0) + return dev_err_probe(data->dev, ret, + "failed to read calibration coefficients\n"); /* * Attempt to grab an optional EOC IRQ - only the BMP085 has this -- cgit v1.2.3 From 327b5c0512c18287162d0f12949aae41d64358b0 Mon Sep 17 00:00:00 2001 From: Angel Iglesias Date: Tue, 13 Sep 2022 01:48:21 +0200 Subject: iio: pressure: bmp280: Fix alignment for DMA safety Adds DMA-safe buffers to driver data struct to store raw data from sensors The multiple buffers used thorough the driver share the same memory allocated as part of the device data instance. The union containing the buffers is aligned to allow safe usage with DMA operations, such as regmap bulk read calls. Updated measurement and calibration reading functions to use the safe DMA buffers. Suggested-by: Jonathan Cameron Signed-off-by: Angel Iglesias Link: https://lore.kernel.org/r/7919793f7f63224d5ce413c66d648029683c17ac.1663025017.git.ang.iglesiasg@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-core.c | 131 +++++++++++++++++++++---------------- drivers/iio/pressure/bmp280.h | 4 +- 2 files changed, 76 insertions(+), 59 deletions(-) diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index 067e56ba6c84..035a29871b7a 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -38,6 +38,8 @@ #include #include +#include + #include "bmp280.h" /* @@ -112,6 +114,21 @@ struct bmp280_data { * calculation. */ s32 t_fine; + + /* + * DMA (thus cache coherency maintenance) may require the + * transfer buffers to live in their own cache lines. + */ + union { + /* Sensor data buffer */ + u8 buf[3]; + /* Calibration data buffers */ + __le16 bmp280_cal_buf[BMP280_CONTIGUOUS_CALIB_REGS / 2]; + __be16 bmp180_cal_buf[BMP180_REG_CALIB_COUNT / 2]; + /* Miscellaneous, endianess-aware data buffers */ + __le16 le16; + __be16 be16; + } __aligned(IIO_DMA_MINALIGN); }; struct bmp280_chip_info { @@ -166,13 +183,12 @@ static const struct iio_chan_spec bmp280_channels[] = { static int bmp280_read_calib(struct bmp280_data *data) { struct bmp280_calib *calib = &data->calib.bmp280; - __le16 c_buf[BMP280_CONTIGUOUS_CALIB_REGS / 2]; int ret; /* Read temperature and pressure calibration values. */ ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_TEMP_START, - c_buf, sizeof(c_buf)); + data->bmp280_cal_buf, sizeof(data->bmp280_cal_buf)); if (ret < 0) { dev_err(data->dev, "failed to read temperature and pressure calibration parameters\n"); @@ -180,23 +196,23 @@ static int bmp280_read_calib(struct bmp280_data *data) } /* Toss the temperature and pressure calibration data into the entropy pool */ - add_device_randomness(c_buf, sizeof(c_buf)); + add_device_randomness(data->bmp280_cal_buf, sizeof(data->bmp280_cal_buf)); /* Parse temperature calibration values. */ - calib->T1 = le16_to_cpu(c_buf[T1]); - calib->T2 = le16_to_cpu(c_buf[T2]); - calib->T3 = le16_to_cpu(c_buf[T3]); + calib->T1 = le16_to_cpu(data->bmp280_cal_buf[T1]); + calib->T2 = le16_to_cpu(data->bmp280_cal_buf[T2]); + calib->T3 = le16_to_cpu(data->bmp280_cal_buf[T3]); /* Parse pressure calibration values. */ - calib->P1 = le16_to_cpu(c_buf[P1]); - calib->P2 = le16_to_cpu(c_buf[P2]); - calib->P3 = le16_to_cpu(c_buf[P3]); - calib->P4 = le16_to_cpu(c_buf[P4]); - calib->P5 = le16_to_cpu(c_buf[P5]); - calib->P6 = le16_to_cpu(c_buf[P6]); - calib->P7 = le16_to_cpu(c_buf[P7]); - calib->P8 = le16_to_cpu(c_buf[P8]); - calib->P9 = le16_to_cpu(c_buf[P9]); + calib->P1 = le16_to_cpu(data->bmp280_cal_buf[P1]); + calib->P2 = le16_to_cpu(data->bmp280_cal_buf[P2]); + calib->P3 = le16_to_cpu(data->bmp280_cal_buf[P3]); + calib->P4 = le16_to_cpu(data->bmp280_cal_buf[P4]); + calib->P5 = le16_to_cpu(data->bmp280_cal_buf[P5]); + calib->P6 = le16_to_cpu(data->bmp280_cal_buf[P6]); + calib->P7 = le16_to_cpu(data->bmp280_cal_buf[P7]); + calib->P8 = le16_to_cpu(data->bmp280_cal_buf[P8]); + calib->P9 = le16_to_cpu(data->bmp280_cal_buf[P9]); return 0; } @@ -206,8 +222,6 @@ static int bme280_read_calib(struct bmp280_data *data) struct bmp280_calib *calib = &data->calib.bmp280; struct device *dev = data->dev; unsigned int tmp; - __le16 l16; - __be16 b16; int ret; /* Load shared calibration params with bmp280 first */ @@ -232,12 +246,13 @@ static int bme280_read_calib(struct bmp280_data *data) } calib->H1 = tmp; - ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H2, &l16, 2); + ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H2, + &data->le16, sizeof(data->le16)); if (ret < 0) { dev_err(dev, "failed to read H2 comp value\n"); return ret; } - calib->H2 = sign_extend32(le16_to_cpu(l16), 15); + calib->H2 = sign_extend32(le16_to_cpu(data->le16), 15); ret = regmap_read(data->regmap, BMP280_REG_COMP_H3, &tmp); if (ret < 0) { @@ -246,20 +261,22 @@ static int bme280_read_calib(struct bmp280_data *data) } calib->H3 = tmp; - ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H4, &b16, 2); + ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H4, + &data->be16, sizeof(data->be16)); if (ret < 0) { dev_err(dev, "failed to read H4 comp value\n"); return ret; } - calib->H4 = sign_extend32(((be16_to_cpu(b16) >> 4) & 0xff0) | - (be16_to_cpu(b16) & 0xf), 11); + calib->H4 = sign_extend32(((be16_to_cpu(data->be16) >> 4) & 0xff0) | + (be16_to_cpu(data->be16) & 0xf), 11); - ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H5, &l16, 2); + ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H5, + &data->le16, sizeof(data->le16)); if (ret < 0) { dev_err(dev, "failed to read H5 comp value\n"); return ret; } - calib->H5 = sign_extend32(FIELD_GET(BMP280_COMP_H5_MASK, le16_to_cpu(l16)), 11); + calib->H5 = sign_extend32(FIELD_GET(BMP280_COMP_H5_MASK, le16_to_cpu(data->le16)), 11); ret = regmap_read(data->regmap, BMP280_REG_COMP_H6, &tmp); if (ret < 0) { @@ -354,16 +371,16 @@ static int bmp280_read_temp(struct bmp280_data *data, int *val) { s32 adc_temp, comp_temp; - __be32 tmp = 0; int ret; - ret = regmap_bulk_read(data->regmap, BMP280_REG_TEMP_MSB, &tmp, 3); + ret = regmap_bulk_read(data->regmap, BMP280_REG_TEMP_MSB, + data->buf, sizeof(data->buf)); if (ret < 0) { dev_err(data->dev, "failed to read temperature\n"); return ret; } - adc_temp = FIELD_GET(BMP280_MEAS_TRIM_MASK, be32_to_cpu(tmp)); + adc_temp = FIELD_GET(BMP280_MEAS_TRIM_MASK, get_unaligned_be24(data->buf)); if (adc_temp == BMP280_TEMP_SKIPPED) { /* reading was skipped */ dev_err(data->dev, "reading temperature skipped\n"); @@ -387,7 +404,6 @@ static int bmp280_read_press(struct bmp280_data *data, int *val, int *val2) { u32 comp_press; - __be32 tmp = 0; s32 adc_press; int ret; @@ -396,13 +412,14 @@ static int bmp280_read_press(struct bmp280_data *data, if (ret < 0) return ret; - ret = regmap_bulk_read(data->regmap, BMP280_REG_PRESS_MSB, &tmp, 3); + ret = regmap_bulk_read(data->regmap, BMP280_REG_PRESS_MSB, + data->buf, sizeof(data->buf)); if (ret < 0) { dev_err(data->dev, "failed to read pressure\n"); return ret; } - adc_press = FIELD_GET(BMP280_MEAS_TRIM_MASK, be32_to_cpu(tmp)); + adc_press = FIELD_GET(BMP280_MEAS_TRIM_MASK, get_unaligned_be24(data->buf)); if (adc_press == BMP280_PRESS_SKIPPED) { /* reading was skipped */ dev_err(data->dev, "reading pressure skipped\n"); @@ -420,7 +437,6 @@ static int bmp280_read_humid(struct bmp280_data *data, int *val, int *val2) { u32 comp_humidity; s32 adc_humidity; - __be16 tmp; int ret; /* Read and compensate temperature so we get a reading of t_fine. */ @@ -428,13 +444,14 @@ static int bmp280_read_humid(struct bmp280_data *data, int *val, int *val2) if (ret < 0) return ret; - ret = regmap_bulk_read(data->regmap, BMP280_REG_HUMIDITY_MSB, &tmp, 2); + ret = regmap_bulk_read(data->regmap, BMP280_REG_HUMIDITY_MSB, + &data->be16, sizeof(data->be16)); if (ret < 0) { dev_err(data->dev, "failed to read humidity\n"); return ret; } - adc_humidity = be16_to_cpu(tmp); + adc_humidity = be16_to_cpu(data->be16); if (adc_humidity == BMP280_HUMIDITY_SKIPPED) { /* reading was skipped */ dev_err(data->dev, "reading humidity skipped\n"); @@ -775,7 +792,6 @@ static int bmp180_measure(struct bmp280_data *data, u8 ctrl_meas) static int bmp180_read_adc_temp(struct bmp280_data *data, int *val) { - __be16 tmp; int ret; ret = bmp180_measure(data, @@ -784,11 +800,12 @@ static int bmp180_read_adc_temp(struct bmp280_data *data, int *val) if (ret) return ret; - ret = regmap_bulk_read(data->regmap, BMP180_REG_OUT_MSB, &tmp, 2); + ret = regmap_bulk_read(data->regmap, BMP180_REG_OUT_MSB, + &data->be16, sizeof(data->be16)); if (ret) return ret; - *val = be16_to_cpu(tmp); + *val = be16_to_cpu(data->be16); return 0; } @@ -796,36 +813,36 @@ static int bmp180_read_adc_temp(struct bmp280_data *data, int *val) static int bmp180_read_calib(struct bmp280_data *data) { struct bmp180_calib *calib = &data->calib.bmp180; - __be16 buf[BMP180_REG_CALIB_COUNT / 2]; int ret; int i; - ret = regmap_bulk_read(data->regmap, BMP180_REG_CALIB_START, buf, - sizeof(buf)); + ret = regmap_bulk_read(data->regmap, BMP180_REG_CALIB_START, + data->bmp180_cal_buf, sizeof(data->bmp180_cal_buf)); if (ret < 0) return ret; /* None of the words has the value 0 or 0xFFFF */ - for (i = 0; i < ARRAY_SIZE(buf); i++) { - if (buf[i] == cpu_to_be16(0) || buf[i] == cpu_to_be16(0xffff)) + for (i = 0; i < ARRAY_SIZE(data->bmp180_cal_buf); i++) { + if (data->bmp180_cal_buf[i] == cpu_to_be16(0) || + data->bmp180_cal_buf[i] == cpu_to_be16(0xffff)) return -EIO; } /* Toss the calibration data into the entropy pool */ - add_device_randomness(buf, sizeof(buf)); - - calib->AC1 = be16_to_cpu(buf[AC1]); - calib->AC2 = be16_to_cpu(buf[AC2]); - calib->AC3 = be16_to_cpu(buf[AC3]); - calib->AC4 = be16_to_cpu(buf[AC4]); - calib->AC5 = be16_to_cpu(buf[AC5]); - calib->AC6 = be16_to_cpu(buf[AC6]); - calib->B1 = be16_to_cpu(buf[B1]); - calib->B2 = be16_to_cpu(buf[B2]); - calib->MB = be16_to_cpu(buf[MB]); - calib->MC = be16_to_cpu(buf[MC]); - calib->MD = be16_to_cpu(buf[MD]); + add_device_randomness(data->bmp180_cal_buf, sizeof(data->bmp180_cal_buf)); + + calib->AC1 = be16_to_cpu(data->bmp180_cal_buf[AC1]); + calib->AC2 = be16_to_cpu(data->bmp180_cal_buf[AC2]); + calib->AC3 = be16_to_cpu(data->bmp180_cal_buf[AC3]); + calib->AC4 = be16_to_cpu(data->bmp180_cal_buf[AC4]); + calib->AC5 = be16_to_cpu(data->bmp180_cal_buf[AC5]); + calib->AC6 = be16_to_cpu(data->bmp180_cal_buf[AC6]); + calib->B1 = be16_to_cpu(data->bmp180_cal_buf[B1]); + calib->B2 = be16_to_cpu(data->bmp180_cal_buf[B2]); + calib->MB = be16_to_cpu(data->bmp180_cal_buf[MB]); + calib->MC = be16_to_cpu(data->bmp180_cal_buf[MC]); + calib->MD = be16_to_cpu(data->bmp180_cal_buf[MD]); return 0; } @@ -874,7 +891,6 @@ static int bmp180_read_temp(struct bmp280_data *data, int *val) static int bmp180_read_adc_press(struct bmp280_data *data, int *val) { u8 oss = data->oversampling_press; - __be32 tmp = 0; int ret; ret = bmp180_measure(data, @@ -884,11 +900,12 @@ static int bmp180_read_adc_press(struct bmp280_data *data, int *val) if (ret) return ret; - ret = regmap_bulk_read(data->regmap, BMP180_REG_OUT_MSB, &tmp, 3); + ret = regmap_bulk_read(data->regmap, BMP180_REG_OUT_MSB, + data->buf, sizeof(data->buf)); if (ret) return ret; - *val = (be32_to_cpu(tmp) >> 8) >> (8 - oss); + *val = get_unaligned_be24(data->buf) >> (8 - oss); return 0; } diff --git a/drivers/iio/pressure/bmp280.h b/drivers/iio/pressure/bmp280.h index 03a539223417..c9214a7e5cc6 100644 --- a/drivers/iio/pressure/bmp280.h +++ b/drivers/iio/pressure/bmp280.h @@ -13,8 +13,8 @@ #define BMP280_REG_PRESS_LSB 0xF8 #define BMP280_REG_PRESS_MSB 0xF7 -/* Helper mask to truncate excess 12 bits on pressure and temp readings */ -#define BMP280_MEAS_TRIM_MASK GENMASK(31, 12) +/* Helper mask to truncate excess 4 bits on pressure and temp readings */ +#define BMP280_MEAS_TRIM_MASK GENMASK(24, 4) #define BMP280_REG_CONFIG 0xF5 #define BMP280_REG_CTRL_MEAS 0xF4 -- cgit v1.2.3 From 18d1bb377023cad76e01b598da3da53da9fc36b7 Mon Sep 17 00:00:00 2001 From: Angel Iglesias Date: Tue, 13 Sep 2022 01:50:07 +0200 Subject: iio: pressure: bmp280: reorder i2c device tables declarations Change device tables declarations to forward order like in SPI codepath. Suggested-by: Andy Shevchenko Signed-off-by: Angel Iglesias Link: https://lore.kernel.org/r/a3969b60e428b9bd29ea1ebc6dd69aa5bbe59da0.1663025017.git.ang.iglesiasg@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-i2c.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/iio/pressure/bmp280-i2c.c b/drivers/iio/pressure/bmp280-i2c.c index bf4a7a617537..5b51ebfc6f2b 100644 --- a/drivers/iio/pressure/bmp280-i2c.c +++ b/drivers/iio/pressure/bmp280-i2c.c @@ -37,18 +37,18 @@ static int bmp280_i2c_probe(struct i2c_client *client, } static const struct of_device_id bmp280_of_i2c_match[] = { - { .compatible = "bosch,bme280", .data = (void *)BME280_CHIP_ID }, - { .compatible = "bosch,bmp280", .data = (void *)BMP280_CHIP_ID }, - { .compatible = "bosch,bmp180", .data = (void *)BMP180_CHIP_ID }, { .compatible = "bosch,bmp085", .data = (void *)BMP180_CHIP_ID }, + { .compatible = "bosch,bmp180", .data = (void *)BMP180_CHIP_ID }, + { .compatible = "bosch,bmp280", .data = (void *)BMP280_CHIP_ID }, + { .compatible = "bosch,bme280", .data = (void *)BME280_CHIP_ID }, { }, }; MODULE_DEVICE_TABLE(of, bmp280_of_i2c_match); static const struct i2c_device_id bmp280_i2c_id[] = { - {"bmp280", BMP280_CHIP_ID }, - {"bmp180", BMP180_CHIP_ID }, {"bmp085", BMP180_CHIP_ID }, + {"bmp180", BMP180_CHIP_ID }, + {"bmp280", BMP280_CHIP_ID }, {"bme280", BME280_CHIP_ID }, { }, }; -- cgit v1.2.3 From 8d329309184d5824e44c6426bf878c5f1e1156e5 Mon Sep 17 00:00:00 2001 From: Angel Iglesias Date: Tue, 13 Sep 2022 01:52:13 +0200 Subject: iio: pressure: bmp280: Add support for BMP380 sensor family Adds compatibility with the new generation of this sensor, the BMP380. Includes basic sensor initialization to do pressure and temp measurements and allows tuning oversampling settings for each channel. The compensation algorithms are adapted from the device datasheet and the repository https://github.com/BoschSensortec/BMP3-Sensor-API. Signed-off-by: Angel Iglesias Link: https://lore.kernel.org/r/f1da2a2f1bc5bb083f318335c23b4f3d9bb8e536.1663025017.git.ang.iglesiasg@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/Kconfig | 6 +- drivers/iio/pressure/bmp280-core.c | 386 +++++++++++++++++++++++++++++++++++ drivers/iio/pressure/bmp280-i2c.c | 5 + drivers/iio/pressure/bmp280-regmap.c | 55 +++++ drivers/iio/pressure/bmp280-spi.c | 5 + drivers/iio/pressure/bmp280.h | 101 +++++++++ 6 files changed, 555 insertions(+), 3 deletions(-) diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index 0ff756cea63a..c9453389e4f7 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -17,14 +17,14 @@ config ABP060MG will be called abp060mg. config BMP280 - tristate "Bosch Sensortec BMP180/BMP280 pressure sensor I2C driver" + tristate "Bosch Sensortec BMP180/BMP280/BMP380 pressure sensor I2C driver" depends on (I2C || SPI_MASTER) select REGMAP select BMP280_I2C if (I2C) select BMP280_SPI if (SPI_MASTER) help - Say yes here to build support for Bosch Sensortec BMP180 and BMP280 - pressure and temperature sensors. Also supports the BME280 with + Say yes here to build support for Bosch Sensortec BMP180, BMP280 and + BMP380 pressure and temperature sensors. Also supports the BME280 with an additional humidity sensor channel. To compile this driver as a module, choose M here: the core module diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index 035a29871b7a..962f5d428257 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -12,6 +12,7 @@ * https://cdn-shop.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf + * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp388-ds001.pdf * * Notice: * The link to the bmp180 datasheet points to an outdated version missing these changes: @@ -84,6 +85,24 @@ struct bmp280_calib { s8 H6; }; +/* See datasheet Section 3.11.1. */ +struct bmp380_calib { + u16 T1; + u16 T2; + s8 T3; + s16 P1; + s16 P2; + s8 P3; + s8 P4; + u16 P5; + u16 P6; + s8 P7; + s8 P8; + s16 P9; + s8 P10; + s8 P11; +}; + static const char *const bmp280_supply_names[] = { "vddd", "vdda" }; @@ -100,6 +119,7 @@ struct bmp280_data { union { struct bmp180_calib bmp180; struct bmp280_calib bmp280; + struct bmp380_calib bmp380; } calib; struct regulator_bulk_data supplies[BMP280_NUM_SUPPLIES]; unsigned int start_up_time; /* in microseconds */ @@ -125,6 +145,7 @@ struct bmp280_data { /* Calibration data buffers */ __le16 bmp280_cal_buf[BMP280_CONTIGUOUS_CALIB_REGS / 2]; __be16 bmp180_cal_buf[BMP180_REG_CALIB_COUNT / 2]; + u8 bmp380_cal_buf[BMP380_CALIB_REG_COUNT]; /* Miscellaneous, endianess-aware data buffers */ __le16 le16; __be16 be16; @@ -162,6 +183,25 @@ struct bmp280_chip_info { */ enum { T1, T2, T3, P1, P2, P3, P4, P5, P6, P7, P8, P9 }; +enum { + /* Temperature calib indexes */ + BMP380_T1 = 0, + BMP380_T2 = 2, + BMP380_T3 = 4, + /* Pressure calib indexes */ + BMP380_P1 = 5, + BMP380_P2 = 7, + BMP380_P3 = 9, + BMP380_P4 = 10, + BMP380_P5 = 11, + BMP380_P6 = 13, + BMP380_P7 = 15, + BMP380_P8 = 16, + BMP380_P9 = 17, + BMP380_P10 = 19, + BMP380_P11 = 20, +}; + static const struct iio_chan_spec bmp280_channels[] = { { .type = IIO_PRESSURE, @@ -745,6 +785,342 @@ static const struct bmp280_chip_info bme280_chip_info = { .read_calib = bme280_read_calib, }; +/* + * Helper function to send a command to BMP3XX sensors. + * + * Sensor processes commands written to the CMD register and signals + * execution result through "cmd_rdy" and "cmd_error" flags available on + * STATUS and ERROR registers. + */ +static int bmp380_cmd(struct bmp280_data *data, u8 cmd) +{ + unsigned int reg; + int ret; + + /* Check if device is ready to process a command */ + ret = regmap_read(data->regmap, BMP380_REG_STATUS, ®); + if (ret) { + dev_err(data->dev, "failed to read error register\n"); + return ret; + } + if (!(reg & BMP380_STATUS_CMD_RDY_MASK)) { + dev_err(data->dev, "device is not ready to accept commands\n"); + return -EBUSY; + } + + /* Send command to process */ + ret = regmap_write(data->regmap, BMP380_REG_CMD, cmd); + if (ret) { + dev_err(data->dev, "failed to send command to device\n"); + return ret; + } + /* Wait for 2ms for command to be processed */ + usleep_range(data->start_up_time, data->start_up_time + 100); + /* Check for command processing error */ + ret = regmap_read(data->regmap, BMP380_REG_ERROR, ®); + if (ret) { + dev_err(data->dev, "error reading ERROR reg\n"); + return ret; + } + if (reg & BMP380_ERR_CMD_MASK) { + dev_err(data->dev, "error processing command 0x%X\n", cmd); + return -EINVAL; + } + + return 0; +} + +/* + * Returns temperature in Celsius dregrees, resolution is 0.01º C. Output value of + * "5123" equals 51.2º C. t_fine carries fine temperature as global value. + * + * Taken from datasheet, Section Appendix 9, "Compensation formula" and repo + * https://github.com/BoschSensortec/BMP3-Sensor-API. + */ +static s32 bmp380_compensate_temp(struct bmp280_data *data, u32 adc_temp) +{ + s64 var1, var2, var3, var4, var5, var6, comp_temp; + struct bmp380_calib *calib = &data->calib.bmp380; + + var1 = ((s64) adc_temp) - (((s64) calib->T1) << 8); + var2 = var1 * ((s64) calib->T2); + var3 = var1 * var1; + var4 = var3 * ((s64) calib->T3); + var5 = (var2 << 18) + var4; + var6 = var5 >> 32; + data->t_fine = (s32) var6; + comp_temp = (var6 * 25) >> 14; + + comp_temp = clamp_val(comp_temp, BMP380_MIN_TEMP, BMP380_MAX_TEMP); + return (s32) comp_temp; +} + +/* + * Returns pressure in Pa as an unsigned 32 bit integer in fractional Pascal. + * Output value of "9528709" represents 9528709/100 = 95287.09 Pa = 952.8709 hPa. + * + * Taken from datasheet, Section 9.3. "Pressure compensation" and repository + * https://github.com/BoschSensortec/BMP3-Sensor-API. + */ +static u32 bmp380_compensate_press(struct bmp280_data *data, u32 adc_press) +{ + s64 var1, var2, var3, var4, var5, var6, offset, sensitivity; + struct bmp380_calib *calib = &data->calib.bmp380; + u32 comp_press; + + var1 = (s64)data->t_fine * (s64)data->t_fine; + var2 = var1 >> 6; + var3 = (var2 * ((s64) data->t_fine)) >> 8; + var4 = ((s64)calib->P8 * var3) >> 5; + var5 = ((s64)calib->P7 * var1) << 4; + var6 = ((s64)calib->P6 * (s64)data->t_fine) << 22; + offset = ((s64)calib->P5 << 47) + var4 + var5 + var6; + var2 = ((s64)calib->P4 * var3) >> 5; + var4 = ((s64)calib->P3 * var1) << 2; + var5 = ((s64)calib->P2 - ((s64)1 << 14)) * + ((s64)data->t_fine << 21); + sensitivity = (((s64) calib->P1 - ((s64) 1 << 14)) << 46) + + var2 + var4 + var5; + var1 = (sensitivity >> 24) * (s64)adc_press; + var2 = (s64)calib->P10 * (s64)data->t_fine; + var3 = var2 + ((s64)calib->P9 << 16); + var4 = (var3 * (s64)adc_press) >> 13; + + /* + * Dividing by 10 followed by multiplying by 10 to avoid + * possible overflow caused by (uncomp_data->pressure * partial_data4). + */ + var5 = ((s64)adc_press * div_s64(var4, 10)) >> 9; + var5 *= 10; + var6 = (s64)adc_press * (s64)adc_press; + var2 = ((s64)calib->P11 * var6) >> 16; + var3 = (var2 * (s64)adc_press) >> 7; + var4 = (offset >> 2) + var1 + var5 + var3; + comp_press = ((u64)var4 * 25) >> 40; + + comp_press = clamp_val(comp_press, BMP380_MIN_PRES, BMP380_MAX_PRES); + return comp_press; +} + +static int bmp380_read_temp(struct bmp280_data *data, int *val) +{ + s32 comp_temp; + u32 adc_temp; + int ret; + + ret = regmap_bulk_read(data->regmap, BMP380_REG_TEMP_XLSB, + data->buf, sizeof(data->buf)); + if (ret) { + dev_err(data->dev, "failed to read temperature\n"); + return ret; + } + + adc_temp = get_unaligned_le24(data->buf); + if (adc_temp == BMP380_TEMP_SKIPPED) { + dev_err(data->dev, "reading temperature skipped\n"); + return -EIO; + } + comp_temp = bmp380_compensate_temp(data, adc_temp); + + /* + * Val might be NULL if we're called by the read_press routine, + * who only cares about the carry over t_fine value. + */ + if (val) { + /* IIO reports temperatures in milli Celsius */ + *val = comp_temp * 10; + return IIO_VAL_INT; + } + + return 0; +} + +static int bmp380_read_press(struct bmp280_data *data, int *val, int *val2) +{ + s32 comp_press; + u32 adc_press; + int ret; + + /* Read and compensate for temperature so we get a reading of t_fine */ + ret = bmp380_read_temp(data, NULL); + if (ret) + return ret; + + ret = regmap_bulk_read(data->regmap, BMP380_REG_PRESS_XLSB, + data->buf, sizeof(data->buf)); + if (ret) { + dev_err(data->dev, "failed to read pressure\n"); + return ret; + } + + adc_press = get_unaligned_le24(data->buf); + if (adc_press == BMP380_PRESS_SKIPPED) { + dev_err(data->dev, "reading pressure skipped\n"); + return -EIO; + } + comp_press = bmp380_compensate_press(data, adc_press); + + *val = comp_press; + /* Compensated pressure is in cPa (centipascals) */ + *val2 = 100000; + + return IIO_VAL_FRACTIONAL; +} + +static int bmp380_read_calib(struct bmp280_data *data) +{ + struct bmp380_calib *calib = &data->calib.bmp380; + int ret; + + /* Read temperature and pressure calibration data */ + ret = regmap_bulk_read(data->regmap, BMP380_REG_CALIB_TEMP_START, + data->bmp380_cal_buf, sizeof(data->bmp380_cal_buf)); + if (ret) { + dev_err(data->dev, + "failed to read temperature calibration parameters\n"); + return ret; + } + + /* Toss the temperature calibration data into the entropy pool */ + add_device_randomness(data->bmp380_cal_buf, sizeof(data->bmp380_cal_buf)); + + /* Parse calibration values */ + calib->T1 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_T1]); + calib->T2 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_T2]); + calib->T3 = data->bmp380_cal_buf[BMP380_T3]; + calib->P1 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_P1]); + calib->P2 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_P2]); + calib->P3 = data->bmp380_cal_buf[BMP380_P3]; + calib->P4 = data->bmp380_cal_buf[BMP380_P4]; + calib->P5 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_P5]); + calib->P6 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_P6]); + calib->P7 = data->bmp380_cal_buf[BMP380_P7]; + calib->P8 = data->bmp380_cal_buf[BMP380_P8]; + calib->P9 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_P9]); + calib->P10 = data->bmp380_cal_buf[BMP380_P10]; + calib->P11 = data->bmp380_cal_buf[BMP380_P11]; + + return 0; +} + +static int bmp380_chip_config(struct bmp280_data *data) +{ + bool change = false, aux; + unsigned int tmp; + u8 osrs; + int ret; + + /* Configure power control register */ + ret = regmap_update_bits(data->regmap, BMP380_REG_POWER_CONTROL, + BMP380_CTRL_SENSORS_MASK, + BMP380_CTRL_SENSORS_PRESS_EN | + BMP380_CTRL_SENSORS_TEMP_EN); + if (ret) { + dev_err(data->dev, + "failed to write operation control register\n"); + return ret; + } + + /* Configure oversampling */ + osrs = FIELD_PREP(BMP380_OSRS_TEMP_MASK, data->oversampling_temp) | + FIELD_PREP(BMP380_OSRS_PRESS_MASK, data->oversampling_press); + + ret = regmap_update_bits_check(data->regmap, BMP380_REG_OSR, + BMP380_OSRS_TEMP_MASK | + BMP380_OSRS_PRESS_MASK, + osrs, &aux); + if (ret) { + dev_err(data->dev, "failed to write oversampling register\n"); + return ret; + } + change = change || aux; + + /* Configure output data rate */ + ret = regmap_update_bits(data->regmap, BMP380_REG_ODR, + BMP380_ODRS_MASK, BMP380_ODRS_50HZ); + if (ret) { + dev_err(data->dev, "failed to write ODR selection register\n"); + return ret; + } + + /* Set filter data */ + ret = regmap_update_bits(data->regmap, BMP380_REG_CONFIG, + BMP380_FILTER_MASK, + FIELD_PREP(BMP380_FILTER_MASK, BMP380_FILTER_3X)); + if (ret) { + dev_err(data->dev, "failed to write config register\n"); + return ret; + } + + if (change) { + /* + * The configurations errors are detected on the fly during a measurement + * cycle. If the sampling frequency is too low, it's faster to reset + * the measurement loop than wait until the next measurement is due. + * + * Resets sensor measurement loop toggling between sleep and normal + * operating modes. + */ + ret = regmap_write_bits(data->regmap, BMP380_REG_POWER_CONTROL, + BMP380_MODE_MASK, + FIELD_PREP(BMP380_MODE_MASK, BMP380_MODE_SLEEP)); + if (ret) { + dev_err(data->dev, "failed to set sleep mode\n"); + return ret; + } + usleep_range(2000, 2500); + ret = regmap_write_bits(data->regmap, BMP380_REG_POWER_CONTROL, + BMP380_MODE_MASK, + FIELD_PREP(BMP380_MODE_MASK, BMP380_MODE_NORMAL)); + if (ret) { + dev_err(data->dev, "failed to set normal mode\n"); + return ret; + } + /* + * Waits for measurement before checking configuration error flag. + * Selected longest measure time indicated in section 3.9.1 + * in the datasheet. + */ + msleep(80); + + /* Check config error flag */ + ret = regmap_read(data->regmap, BMP380_REG_ERROR, &tmp); + if (ret) { + dev_err(data->dev, + "failed to read error register\n"); + return ret; + } + if (tmp & BMP380_ERR_CONF_MASK) { + dev_warn(data->dev, + "sensor flagged configuration as incompatible\n"); + return -EINVAL; + } + } + + return 0; +} + +static const int bmp380_oversampling_avail[] = { 1, 2, 4, 8, 16, 32 }; + +static const struct bmp280_chip_info bmp380_chip_info = { + .id_reg = BMP380_REG_ID, + .start_up_time = 2000, + .num_channels = 2, + + .oversampling_temp_avail = bmp380_oversampling_avail, + .num_oversampling_temp_avail = ARRAY_SIZE(bmp380_oversampling_avail), + .oversampling_temp_default = ilog2(1), + + .oversampling_press_avail = bmp380_oversampling_avail, + .num_oversampling_press_avail = ARRAY_SIZE(bmp380_oversampling_avail), + .oversampling_press_default = ilog2(4), + + .chip_config = bmp380_chip_config, + .read_temp = bmp380_read_temp, + .read_press = bmp380_read_press, + .read_calib = bmp380_read_calib, +}; + static int bmp180_measure(struct bmp280_data *data, u8 ctrl_meas) { const int conversion_time_max[] = { 4500, 7500, 13500, 25500 }; @@ -1092,6 +1468,9 @@ int bmp280_common_probe(struct device *dev, case BME280_CHIP_ID: chip_info = &bme280_chip_info; break; + case BMP380_CHIP_ID: + chip_info = &bmp380_chip_info; + break; default: return -EINVAL; } @@ -1149,6 +1528,13 @@ int bmp280_common_probe(struct device *dev, return -EINVAL; } + /* BMP3xx requires soft-reset as part of initialization */ + if (chip_id == BMP380_CHIP_ID) { + ret = bmp380_cmd(data, BMP380_CMD_SOFT_RESET); + if (ret < 0) + return ret; + } + ret = data->chip_info->chip_config(data); if (ret < 0) return ret; diff --git a/drivers/iio/pressure/bmp280-i2c.c b/drivers/iio/pressure/bmp280-i2c.c index 5b51ebfc6f2b..0c27211f3ea0 100644 --- a/drivers/iio/pressure/bmp280-i2c.c +++ b/drivers/iio/pressure/bmp280-i2c.c @@ -19,6 +19,9 @@ static int bmp280_i2c_probe(struct i2c_client *client, case BME280_CHIP_ID: regmap_config = &bmp280_regmap_config; break; + case BMP380_CHIP_ID: + regmap_config = &bmp380_regmap_config; + break; default: return -EINVAL; } @@ -41,6 +44,7 @@ static const struct of_device_id bmp280_of_i2c_match[] = { { .compatible = "bosch,bmp180", .data = (void *)BMP180_CHIP_ID }, { .compatible = "bosch,bmp280", .data = (void *)BMP280_CHIP_ID }, { .compatible = "bosch,bme280", .data = (void *)BME280_CHIP_ID }, + { .compatible = "bosch,bmp380", .data = (void *)BMP380_CHIP_ID }, { }, }; MODULE_DEVICE_TABLE(of, bmp280_of_i2c_match); @@ -50,6 +54,7 @@ static const struct i2c_device_id bmp280_i2c_id[] = { {"bmp180", BMP180_CHIP_ID }, {"bmp280", BMP280_CHIP_ID }, {"bme280", BME280_CHIP_ID }, + {"bmp380", BMP380_CHIP_ID }, { }, }; MODULE_DEVICE_TABLE(i2c, bmp280_i2c_id); diff --git a/drivers/iio/pressure/bmp280-regmap.c b/drivers/iio/pressure/bmp280-regmap.c index 969698518984..c98c67970265 100644 --- a/drivers/iio/pressure/bmp280-regmap.c +++ b/drivers/iio/pressure/bmp280-regmap.c @@ -72,6 +72,49 @@ static bool bmp280_is_volatile_reg(struct device *dev, unsigned int reg) } } +static bool bmp380_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case BMP380_REG_CMD: + case BMP380_REG_CONFIG: + case BMP380_REG_FIFO_CONFIG_1: + case BMP380_REG_FIFO_CONFIG_2: + case BMP380_REG_FIFO_WATERMARK_LSB: + case BMP380_REG_FIFO_WATERMARK_MSB: + case BMP380_REG_POWER_CONTROL: + case BMP380_REG_INT_CONTROL: + case BMP380_REG_IF_CONFIG: + case BMP380_REG_ODR: + case BMP380_REG_OSR: + return true; + default: + return false; + } +} + +static bool bmp380_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case BMP380_REG_TEMP_XLSB: + case BMP380_REG_TEMP_LSB: + case BMP380_REG_TEMP_MSB: + case BMP380_REG_PRESS_XLSB: + case BMP380_REG_PRESS_LSB: + case BMP380_REG_PRESS_MSB: + case BMP380_REG_SENSOR_TIME_XLSB: + case BMP380_REG_SENSOR_TIME_LSB: + case BMP380_REG_SENSOR_TIME_MSB: + case BMP380_REG_INT_STATUS: + case BMP380_REG_FIFO_DATA: + case BMP380_REG_STATUS: + case BMP380_REG_ERROR: + case BMP380_REG_EVENT: + return true; + default: + return false; + } +} + const struct regmap_config bmp280_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -83,3 +126,15 @@ const struct regmap_config bmp280_regmap_config = { .volatile_reg = bmp280_is_volatile_reg, }; EXPORT_SYMBOL_NS(bmp280_regmap_config, IIO_BMP280); + +const struct regmap_config bmp380_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = BMP380_REG_CMD, + .cache_type = REGCACHE_RBTREE, + + .writeable_reg = bmp380_is_writeable_reg, + .volatile_reg = bmp380_is_volatile_reg, +}; +EXPORT_SYMBOL_NS(bmp380_regmap_config, IIO_BMP280); diff --git a/drivers/iio/pressure/bmp280-spi.c b/drivers/iio/pressure/bmp280-spi.c index 4cfaf3e869b8..011c68e07ebf 100644 --- a/drivers/iio/pressure/bmp280-spi.c +++ b/drivers/iio/pressure/bmp280-spi.c @@ -66,6 +66,9 @@ static int bmp280_spi_probe(struct spi_device *spi) case BME280_CHIP_ID: regmap_config = &bmp280_regmap_config; break; + case BMP380_CHIP_ID: + regmap_config = &bmp380_regmap_config; + break; default: return -EINVAL; } @@ -92,6 +95,7 @@ static const struct of_device_id bmp280_of_spi_match[] = { { .compatible = "bosch,bmp181", }, { .compatible = "bosch,bmp280", }, { .compatible = "bosch,bme280", }, + { .compatible = "bosch,bmp380", }, { }, }; MODULE_DEVICE_TABLE(of, bmp280_of_spi_match); @@ -101,6 +105,7 @@ static const struct spi_device_id bmp280_spi_id[] = { { "bmp181", BMP180_CHIP_ID }, { "bmp280", BMP280_CHIP_ID }, { "bme280", BME280_CHIP_ID }, + { "bmp380", BMP380_CHIP_ID }, { } }; MODULE_DEVICE_TABLE(spi, bmp280_spi_id); diff --git a/drivers/iio/pressure/bmp280.h b/drivers/iio/pressure/bmp280.h index c9214a7e5cc6..b546d7727a96 100644 --- a/drivers/iio/pressure/bmp280.h +++ b/drivers/iio/pressure/bmp280.h @@ -3,6 +3,105 @@ #include #include +/* BMP380 specific registers */ +#define BMP380_REG_CMD 0x7E +#define BMP380_REG_CONFIG 0x1F +#define BMP380_REG_ODR 0x1D +#define BMP380_REG_OSR 0x1C +#define BMP380_REG_POWER_CONTROL 0x1B +#define BMP380_REG_IF_CONFIG 0x1A +#define BMP380_REG_INT_CONTROL 0x19 +#define BMP380_REG_INT_STATUS 0x11 +#define BMP380_REG_EVENT 0x10 +#define BMP380_REG_STATUS 0x03 +#define BMP380_REG_ERROR 0x02 +#define BMP380_REG_ID 0x00 + +#define BMP380_REG_FIFO_CONFIG_1 0x18 +#define BMP380_REG_FIFO_CONFIG_2 0x17 +#define BMP380_REG_FIFO_WATERMARK_MSB 0x16 +#define BMP380_REG_FIFO_WATERMARK_LSB 0x15 +#define BMP380_REG_FIFO_DATA 0x14 +#define BMP380_REG_FIFO_LENGTH_MSB 0x13 +#define BMP380_REG_FIFO_LENGTH_LSB 0x12 + +#define BMP380_REG_SENSOR_TIME_MSB 0x0E +#define BMP380_REG_SENSOR_TIME_LSB 0x0D +#define BMP380_REG_SENSOR_TIME_XLSB 0x0C + +#define BMP380_REG_TEMP_MSB 0x09 +#define BMP380_REG_TEMP_LSB 0x08 +#define BMP380_REG_TEMP_XLSB 0x07 + +#define BMP380_REG_PRESS_MSB 0x06 +#define BMP380_REG_PRESS_LSB 0x05 +#define BMP380_REG_PRESS_XLSB 0x04 + +#define BMP380_REG_CALIB_TEMP_START 0x31 +#define BMP380_CALIB_REG_COUNT 21 + +#define BMP380_FILTER_MASK GENMASK(3, 1) +#define BMP380_FILTER_OFF 0 +#define BMP380_FILTER_1X 1 +#define BMP380_FILTER_3X 2 +#define BMP380_FILTER_7X 3 +#define BMP380_FILTER_15X 4 +#define BMP380_FILTER_31X 5 +#define BMP380_FILTER_63X 6 +#define BMP380_FILTER_127X 7 + +#define BMP380_OSRS_TEMP_MASK GENMASK(5, 3) +#define BMP380_OSRS_PRESS_MASK GENMASK(2, 0) + +#define BMP380_ODRS_MASK GENMASK(4, 0) +#define BMP380_ODRS_200HZ 0x00 +#define BMP380_ODRS_100HZ 0x01 +#define BMP380_ODRS_50HZ 0x02 +#define BMP380_ODRS_25HZ 0x03 +#define BMP380_ODRS_12_5HZ 0x04 +#define BMP380_ODRS_6_25HZ 0x05 +#define BMP380_ODRS_3_1HZ 0x06 +#define BMP380_ODRS_1_5HZ 0x07 +#define BMP380_ODRS_0_78HZ 0x08 +#define BMP380_ODRS_0_39HZ 0x09 +#define BMP380_ODRS_0_2HZ 0x0A +#define BMP380_ODRS_0_1HZ 0x0B +#define BMP380_ODRS_0_05HZ 0x0C +#define BMP380_ODRS_0_02HZ 0x0D +#define BMP380_ODRS_0_01HZ 0x0E +#define BMP380_ODRS_0_006HZ 0x0F +#define BMP380_ODRS_0_003HZ 0x10 +#define BMP380_ODRS_0_0015HZ 0x11 + +#define BMP380_CTRL_SENSORS_MASK GENMASK(1, 0) +#define BMP380_CTRL_SENSORS_PRESS_EN BIT(0) +#define BMP380_CTRL_SENSORS_TEMP_EN BIT(1) +#define BMP380_MODE_MASK GENMASK(5, 4) +#define BMP380_MODE_SLEEP 0 +#define BMP380_MODE_FORCED 1 +#define BMP380_MODE_NORMAL 3 + +#define BMP380_MIN_TEMP -4000 +#define BMP380_MAX_TEMP 8500 +#define BMP380_MIN_PRES 3000000 +#define BMP380_MAX_PRES 12500000 + +#define BMP380_CMD_NOOP 0x00 +#define BMP380_CMD_EXTMODE_EN_MID 0x34 +#define BMP380_CMD_FIFO_FLUSH 0xB0 +#define BMP380_CMD_SOFT_RESET 0xB6 + +#define BMP380_STATUS_CMD_RDY_MASK BIT(4) +#define BMP380_STATUS_DRDY_PRESS_MASK BIT(5) +#define BMP380_STATUS_DRDY_TEMP_MASK BIT(6) + +#define BMP380_ERR_FATAL_MASK BIT(0) +#define BMP380_ERR_CMD_MASK BIT(1) +#define BMP380_ERR_CONF_MASK BIT(2) + +#define BMP380_TEMP_SKIPPED 0x800000 +#define BMP380_PRESS_SKIPPED 0x800000 + /* BMP280 specific registers */ #define BMP280_REG_HUMIDITY_LSB 0xFE #define BMP280_REG_HUMIDITY_MSB 0xFD @@ -99,6 +198,7 @@ #define BMP280_REG_RESET 0xE0 #define BMP280_REG_ID 0xD0 +#define BMP380_CHIP_ID 0x50 #define BMP180_CHIP_ID 0x55 #define BMP280_CHIP_ID 0x58 #define BME280_CHIP_ID 0x60 @@ -112,6 +212,7 @@ /* Regmap configurations */ extern const struct regmap_config bmp180_regmap_config; extern const struct regmap_config bmp280_regmap_config; +extern const struct regmap_config bmp380_regmap_config; /* Probe called from different transports */ int bmp280_common_probe(struct device *dev, -- cgit v1.2.3 From 2a332dcd68aff25b5177b5e4196b4d46403ba364 Mon Sep 17 00:00:00 2001 From: Angel Iglesias Date: Tue, 13 Sep 2022 01:53:50 +0200 Subject: dt-bindings: iio: pressure: bmp085: Add BMP380 compatible string Add bosch,bmp380 compatible string for the new family of sensors. This family includes the BMP380, BMP384 and BMP388. The register map in this family changes substantially and introduces new features but core concepts and operations carryover from the previous iterations Signed-off-by: Angel Iglesias Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/67224da4ae308bb752fc8fdecc54345cedab6c21.1663025017.git.ang.iglesiasg@gmail.com Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/pressure/bmp085.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/iio/pressure/bmp085.yaml b/Documentation/devicetree/bindings/iio/pressure/bmp085.yaml index 49257f9251e8..72cd2c2d3f17 100644 --- a/Documentation/devicetree/bindings/iio/pressure/bmp085.yaml +++ b/Documentation/devicetree/bindings/iio/pressure/bmp085.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/iio/pressure/bmp085.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: BMP085/BMP180/BMP280/BME280 pressure iio sensors +title: BMP085/BMP180/BMP280/BME280/BMP380 pressure iio sensors maintainers: - Andreas Klinger @@ -16,6 +16,7 @@ description: | https://www.bosch-sensortec.com/bst/products/all_products/bmp180 https://www.bosch-sensortec.com/bst/products/all_products/bmp280 https://www.bosch-sensortec.com/bst/products/all_products/bme280 + https://www.bosch-sensortec.com/bst/products/all_products/bmp380 properties: compatible: @@ -24,6 +25,7 @@ properties: - bosch,bmp180 - bosch,bmp280 - bosch,bme280 + - bosch,bmp380 reg: maxItems: 1 -- cgit v1.2.3 From 10b40ffba2f95cdeed47b731c5ad5ecc73e140e8 Mon Sep 17 00:00:00 2001 From: Angel Iglesias Date: Tue, 13 Sep 2022 01:54:42 +0200 Subject: iio: pressure: bmp280: Add more tunable config parameters for BMP380 Allows sampling frequency and IIR filter coefficients configuration using sysfs ABI. The IIR filter coefficient is configurable using the sysfs attribute "filter_low_pass_3db_frequency". Signed-off-by: Angel Iglesias Link: https://lore.kernel.org/r/876f8a2277f71672488e99aa02aae4239d530f51.1663025017.git.ang.iglesiasg@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-core.c | 247 +++++++++++++++++++++++++++++++++++-- drivers/iio/pressure/bmp280.h | 18 --- 2 files changed, 238 insertions(+), 27 deletions(-) diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index 962f5d428257..c0aff78489b4 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -109,6 +109,27 @@ static const char *const bmp280_supply_names[] = { #define BMP280_NUM_SUPPLIES ARRAY_SIZE(bmp280_supply_names) +enum bmp380_odr { + BMP380_ODR_200HZ, + BMP380_ODR_100HZ, + BMP380_ODR_50HZ, + BMP380_ODR_25HZ, + BMP380_ODR_12_5HZ, + BMP380_ODR_6_25HZ, + BMP380_ODR_3_125HZ, + BMP380_ODR_1_5625HZ, + BMP380_ODR_0_78HZ, + BMP380_ODR_0_39HZ, + BMP380_ODR_0_2HZ, + BMP380_ODR_0_1HZ, + BMP380_ODR_0_05HZ, + BMP380_ODR_0_02HZ, + BMP380_ODR_0_01HZ, + BMP380_ODR_0_006HZ, + BMP380_ODR_0_003HZ, + BMP380_ODR_0_0015HZ, +}; + struct bmp280_data { struct device *dev; struct mutex lock; @@ -128,6 +149,17 @@ struct bmp280_data { u8 oversampling_press; u8 oversampling_temp; u8 oversampling_humid; + u8 iir_filter_coeff; + + /* + * BMP380 devices introduce sampling frequency configuration. See + * datasheet sections 3.3.3. and 4.3.19 for more details. + * + * BMx280 devices allowed indirect configuration of sampling frequency + * changing the t_standby duration between measurements, as detailed on + * section 3.6.3 of the datasheet. + */ + int sampling_freq; /* * Carryover value from temperature conversion, used in pressure @@ -155,6 +187,7 @@ struct bmp280_data { struct bmp280_chip_info { unsigned int id_reg; + const struct iio_chan_spec *channels; int num_channels; unsigned int start_up_time; @@ -170,6 +203,14 @@ struct bmp280_chip_info { int num_oversampling_humid_avail; int oversampling_humid_default; + const int *iir_filter_coeffs_avail; + int num_iir_filter_coeffs_avail; + int iir_filter_coeff_default; + + const int (*sampling_freq_avail)[2]; + int num_sampling_freq_avail; + int sampling_freq_default; + int (*chip_config)(struct bmp280_data *); int (*read_temp)(struct bmp280_data *, int *); int (*read_press)(struct bmp280_data *, int *, int *); @@ -220,6 +261,30 @@ static const struct iio_chan_spec bmp280_channels[] = { }, }; +static const struct iio_chan_spec bmp380_channels[] = { + { + .type = IIO_PRESSURE, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + }, + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + }, + { + .type = IIO_HUMIDITYRELATIVE, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + }, +}; + static int bmp280_read_calib(struct bmp280_data *data) { struct bmp280_calib *calib = &data->calib.bmp280; @@ -550,6 +615,25 @@ static int bmp280_read_raw(struct iio_dev *indio_dev, break; } break; + case IIO_CHAN_INFO_SAMP_FREQ: + if (!data->chip_info->sampling_freq_avail) { + ret = -EINVAL; + break; + } + + *val = data->chip_info->sampling_freq_avail[data->sampling_freq][0]; + *val2 = data->chip_info->sampling_freq_avail[data->sampling_freq][1]; + ret = IIO_VAL_INT_PLUS_MICRO; + break; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + if (!data->chip_info->iir_filter_coeffs_avail) { + ret = -EINVAL; + break; + } + + *val = (1 << data->iir_filter_coeff) - 1; + ret = IIO_VAL_INT; + break; default: ret = -EINVAL; break; @@ -567,13 +651,21 @@ static int bmp280_write_oversampling_ratio_humid(struct bmp280_data *data, { const int *avail = data->chip_info->oversampling_humid_avail; const int n = data->chip_info->num_oversampling_humid_avail; + int ret, prev; int i; for (i = 0; i < n; i++) { if (avail[i] == val) { + prev = data->oversampling_humid; data->oversampling_humid = ilog2(val); - return data->chip_info->chip_config(data); + ret = data->chip_info->chip_config(data); + if (ret) { + data->oversampling_humid = prev; + data->chip_info->chip_config(data); + return ret; + } + return 0; } } return -EINVAL; @@ -584,13 +676,21 @@ static int bmp280_write_oversampling_ratio_temp(struct bmp280_data *data, { const int *avail = data->chip_info->oversampling_temp_avail; const int n = data->chip_info->num_oversampling_temp_avail; + int ret, prev; int i; for (i = 0; i < n; i++) { if (avail[i] == val) { + prev = data->oversampling_temp; data->oversampling_temp = ilog2(val); - return data->chip_info->chip_config(data); + ret = data->chip_info->chip_config(data); + if (ret) { + data->oversampling_temp = prev; + data->chip_info->chip_config(data); + return ret; + } + return 0; } } return -EINVAL; @@ -601,13 +701,71 @@ static int bmp280_write_oversampling_ratio_press(struct bmp280_data *data, { const int *avail = data->chip_info->oversampling_press_avail; const int n = data->chip_info->num_oversampling_press_avail; + int ret, prev; int i; for (i = 0; i < n; i++) { if (avail[i] == val) { + prev = data->oversampling_press; data->oversampling_press = ilog2(val); - return data->chip_info->chip_config(data); + ret = data->chip_info->chip_config(data); + if (ret) { + data->oversampling_press = prev; + data->chip_info->chip_config(data); + return ret; + } + return 0; + } + } + return -EINVAL; +} + +static int bmp280_write_sampling_frequency(struct bmp280_data *data, + int val, int val2) +{ + const int (*avail)[2] = data->chip_info->sampling_freq_avail; + const int n = data->chip_info->num_sampling_freq_avail; + int ret, prev; + int i; + + for (i = 0; i < n; i++) { + if (avail[i][0] == val && avail[i][1] == val2) { + prev = data->sampling_freq; + data->sampling_freq = i; + + ret = data->chip_info->chip_config(data); + if (ret) { + data->sampling_freq = prev; + data->chip_info->chip_config(data); + return ret; + } + return 0; + } + } + return -EINVAL; +} + +static int bmp280_write_iir_filter_coeffs(struct bmp280_data *data, int val) +{ + const int *avail = data->chip_info->iir_filter_coeffs_avail; + const int n = data->chip_info->num_iir_filter_coeffs_avail; + int ret, prev; + int i; + + for (i = 0; i < n; i++) { + if (avail[i] - 1 == val) { + prev = data->iir_filter_coeff; + data->iir_filter_coeff = i; + + ret = data->chip_info->chip_config(data); + if (ret) { + data->iir_filter_coeff = prev; + data->chip_info->chip_config(data); + return ret; + + } + return 0; } } return -EINVAL; @@ -620,6 +778,12 @@ static int bmp280_write_raw(struct iio_dev *indio_dev, struct bmp280_data *data = iio_priv(indio_dev); int ret = 0; + /* + * Helper functions to update sensor running configuration. + * If an error happens applying new settings, will try restore + * previous parameters to ensure the sensor is left in a known + * working configuration. + */ switch (mask) { case IIO_CHAN_INFO_OVERSAMPLING_RATIO: pm_runtime_get_sync(data->dev); @@ -642,6 +806,22 @@ static int bmp280_write_raw(struct iio_dev *indio_dev, pm_runtime_mark_last_busy(data->dev); pm_runtime_put_autosuspend(data->dev); break; + case IIO_CHAN_INFO_SAMP_FREQ: + pm_runtime_get_sync(data->dev); + mutex_lock(&data->lock); + ret = bmp280_write_sampling_frequency(data, val, val2); + mutex_unlock(&data->lock); + pm_runtime_mark_last_busy(data->dev); + pm_runtime_put_autosuspend(data->dev); + break; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + pm_runtime_get_sync(data->dev); + mutex_lock(&data->lock); + ret = bmp280_write_iir_filter_coeffs(data, val); + mutex_unlock(&data->lock); + pm_runtime_mark_last_busy(data->dev); + pm_runtime_put_autosuspend(data->dev); + break; default: return -EINVAL; } @@ -672,6 +852,17 @@ static int bmp280_read_avail(struct iio_dev *indio_dev, } *type = IIO_VAL_INT; return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (const int *)data->chip_info->sampling_freq_avail; + *type = IIO_VAL_INT_PLUS_MICRO; + /* Values are stored in a 2D matrix */ + *length = data->chip_info->num_sampling_freq_avail; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + *vals = data->chip_info->iir_filter_coeffs_avail; + *type = IIO_VAL_INT; + *length = data->chip_info->num_iir_filter_coeffs_avail; + return IIO_AVAIL_LIST; default: return -EINVAL; } @@ -717,6 +908,7 @@ static const int bmp280_oversampling_avail[] = { 1, 2, 4, 8, 16 }; static const struct bmp280_chip_info bmp280_chip_info = { .id_reg = BMP280_REG_ID, .start_up_time = 2000, + .channels = bmp280_channels, .num_channels = 2, .oversampling_temp_avail = bmp280_oversampling_avail, @@ -764,6 +956,7 @@ static int bme280_chip_config(struct bmp280_data *data) static const struct bmp280_chip_info bme280_chip_info = { .id_reg = BMP280_REG_ID, .start_up_time = 2000, + .channels = bmp280_channels, .num_channels = 3, .oversampling_temp_avail = bmp280_oversampling_avail, @@ -1003,6 +1196,27 @@ static int bmp380_read_calib(struct bmp280_data *data) return 0; } +static const int bmp380_odr_table[][2] = { + [BMP380_ODR_200HZ] = {200, 0}, + [BMP380_ODR_100HZ] = {100, 0}, + [BMP380_ODR_50HZ] = {50, 0}, + [BMP380_ODR_25HZ] = {25, 0}, + [BMP380_ODR_12_5HZ] = {12, 500000}, + [BMP380_ODR_6_25HZ] = {6, 250000}, + [BMP380_ODR_3_125HZ] = {3, 125000}, + [BMP380_ODR_1_5625HZ] = {1, 562500}, + [BMP380_ODR_0_78HZ] = {0, 781250}, + [BMP380_ODR_0_39HZ] = {0, 390625}, + [BMP380_ODR_0_2HZ] = {0, 195313}, + [BMP380_ODR_0_1HZ] = {0, 97656}, + [BMP380_ODR_0_05HZ] = {0, 48828}, + [BMP380_ODR_0_02HZ] = {0, 24414}, + [BMP380_ODR_0_01HZ] = {0, 12207}, + [BMP380_ODR_0_006HZ] = {0, 6104}, + [BMP380_ODR_0_003HZ] = {0, 3052}, + [BMP380_ODR_0_0015HZ] = {0, 1526}, +}; + static int bmp380_chip_config(struct bmp280_data *data) { bool change = false, aux; @@ -1036,21 +1250,23 @@ static int bmp380_chip_config(struct bmp280_data *data) change = change || aux; /* Configure output data rate */ - ret = regmap_update_bits(data->regmap, BMP380_REG_ODR, - BMP380_ODRS_MASK, BMP380_ODRS_50HZ); + ret = regmap_update_bits_check(data->regmap, BMP380_REG_ODR, + BMP380_ODRS_MASK, data->sampling_freq, &aux); if (ret) { dev_err(data->dev, "failed to write ODR selection register\n"); return ret; } + change = change || aux; /* Set filter data */ - ret = regmap_update_bits(data->regmap, BMP380_REG_CONFIG, - BMP380_FILTER_MASK, - FIELD_PREP(BMP380_FILTER_MASK, BMP380_FILTER_3X)); + ret = regmap_update_bits_check(data->regmap, BMP380_REG_CONFIG, BMP380_FILTER_MASK, + FIELD_PREP(BMP380_FILTER_MASK, data->iir_filter_coeff), + &aux); if (ret) { dev_err(data->dev, "failed to write config register\n"); return ret; } + change = change || aux; if (change) { /* @@ -1101,10 +1317,12 @@ static int bmp380_chip_config(struct bmp280_data *data) } static const int bmp380_oversampling_avail[] = { 1, 2, 4, 8, 16, 32 }; +static const int bmp380_iir_filter_coeffs_avail[] = { 1, 2, 4, 8, 16, 32, 64, 128}; static const struct bmp280_chip_info bmp380_chip_info = { .id_reg = BMP380_REG_ID, .start_up_time = 2000, + .channels = bmp380_channels, .num_channels = 2, .oversampling_temp_avail = bmp380_oversampling_avail, @@ -1115,6 +1333,14 @@ static const struct bmp280_chip_info bmp380_chip_info = { .num_oversampling_press_avail = ARRAY_SIZE(bmp380_oversampling_avail), .oversampling_press_default = ilog2(4), + .sampling_freq_avail = bmp380_odr_table, + .num_sampling_freq_avail = ARRAY_SIZE(bmp380_odr_table) * 2, + .sampling_freq_default = BMP380_ODR_50HZ, + + .iir_filter_coeffs_avail = bmp380_iir_filter_coeffs_avail, + .num_iir_filter_coeffs_avail = ARRAY_SIZE(bmp380_iir_filter_coeffs_avail), + .iir_filter_coeff_default = 2, + .chip_config = bmp380_chip_config, .read_temp = bmp380_read_temp, .read_press = bmp380_read_press, @@ -1356,6 +1582,7 @@ static const int bmp180_oversampling_press_avail[] = { 1, 2, 4, 8 }; static const struct bmp280_chip_info bmp180_chip_info = { .id_reg = BMP280_REG_ID, .start_up_time = 2000, + .channels = bmp280_channels, .num_channels = 2, .oversampling_temp_avail = bmp180_oversampling_temp_avail, @@ -1454,7 +1681,6 @@ int bmp280_common_probe(struct device *dev, data->dev = dev; indio_dev->name = name; - indio_dev->channels = bmp280_channels; indio_dev->info = &bmp280_info; indio_dev->modes = INDIO_DIRECT_MODE; @@ -1477,10 +1703,13 @@ int bmp280_common_probe(struct device *dev, data->chip_info = chip_info; /* Apply initial values from chip info structure */ + indio_dev->channels = chip_info->channels; indio_dev->num_channels = chip_info->num_channels; data->oversampling_press = chip_info->oversampling_press_default; data->oversampling_humid = chip_info->oversampling_humid_default; data->oversampling_temp = chip_info->oversampling_temp_default; + data->iir_filter_coeff = chip_info->iir_filter_coeff_default; + data->sampling_freq = chip_info->sampling_freq_default; data->start_up_time = chip_info->start_up_time; /* Bring up regulators */ diff --git a/drivers/iio/pressure/bmp280.h b/drivers/iio/pressure/bmp280.h index b546d7727a96..c791325c7416 100644 --- a/drivers/iio/pressure/bmp280.h +++ b/drivers/iio/pressure/bmp280.h @@ -54,24 +54,6 @@ #define BMP380_OSRS_PRESS_MASK GENMASK(2, 0) #define BMP380_ODRS_MASK GENMASK(4, 0) -#define BMP380_ODRS_200HZ 0x00 -#define BMP380_ODRS_100HZ 0x01 -#define BMP380_ODRS_50HZ 0x02 -#define BMP380_ODRS_25HZ 0x03 -#define BMP380_ODRS_12_5HZ 0x04 -#define BMP380_ODRS_6_25HZ 0x05 -#define BMP380_ODRS_3_1HZ 0x06 -#define BMP380_ODRS_1_5HZ 0x07 -#define BMP380_ODRS_0_78HZ 0x08 -#define BMP380_ODRS_0_39HZ 0x09 -#define BMP380_ODRS_0_2HZ 0x0A -#define BMP380_ODRS_0_1HZ 0x0B -#define BMP380_ODRS_0_05HZ 0x0C -#define BMP380_ODRS_0_02HZ 0x0D -#define BMP380_ODRS_0_01HZ 0x0E -#define BMP380_ODRS_0_006HZ 0x0F -#define BMP380_ODRS_0_003HZ 0x10 -#define BMP380_ODRS_0_0015HZ 0x11 #define BMP380_CTRL_SENSORS_MASK GENMASK(1, 0) #define BMP380_CTRL_SENSORS_PRESS_EN BIT(0) -- cgit v1.2.3 From 7f4f1096d5921f5d90547596f9ce80e0b924f887 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 15 Aug 2022 09:16:47 +0000 Subject: iio: ltc2497: Fix reading conversion results MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After the result of the previous conversion is read the chip automatically starts a new conversion and doesn't accept new i2c transfers until this conversion is completed which makes the function return failure. So add an early return iff the programming of the new address isn't needed. Note this will not fix the problem in general, but all cases that are currently used. Once this changes we get the failure back, but this can be addressed when the need arises. Fixes: 69548b7c2c4f ("iio: adc: ltc2497: split protocol independent part in a separate module ") Reported-by: Meng Li Signed-off-by: Uwe Kleine-König Tested-by: Denys Zagorui Cc: Link: https://lore.kernel.org/r/20220815091647.1523532-1-dzagorui@cisco.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ltc2497.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c index f7c786f37ceb..78b93c99cc47 100644 --- a/drivers/iio/adc/ltc2497.c +++ b/drivers/iio/adc/ltc2497.c @@ -41,6 +41,19 @@ static int ltc2497_result_and_measure(struct ltc2497core_driverdata *ddata, } *val = (be32_to_cpu(st->buf) >> 14) - (1 << 17); + + /* + * The part started a new conversion at the end of the above i2c + * transfer, so if the address didn't change since the last call + * everything is fine and we can return early. + * If not (which should only happen when some sort of bulk + * conversion is implemented) we have to program the new + * address. Note that this probably fails as the conversion that + * was triggered above is like not complete yet and the two + * operations have to be done in a single transfer. + */ + if (ddata->addr_prev == address) + return 0; } ret = i2c_smbus_write_byte(st->client, -- cgit v1.2.3 From bb43bfedce47c22dfd4df38c9051e930fecd6da9 Mon Sep 17 00:00:00 2001 From: Ciprian Regus Date: Fri, 16 Sep 2022 17:09:18 +0300 Subject: MAINTAINERS: Remove duplicate matching entry Remove the specific entry for ad5758, since Documentation/devicetree/bindings/iio/*/adi,* already matches the path. Signed-off-by: Ciprian Regus Link: https://lore.kernel.org/r/20220916140922.2506248-2-ciprian.regus@analog.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 9f5c060473c6..653e4c267777 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1319,7 +1319,6 @@ W: https://ez.analog.com/linux-software-drivers F: Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523 F: Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 F: Documentation/devicetree/bindings/iio/*/adi,* -F: Documentation/devicetree/bindings/iio/dac/adi,ad5758.yaml F: drivers/iio/*/ad* F: drivers/iio/adc/ltc249* F: drivers/iio/amplifiers/hmc425a.c -- cgit v1.2.3 From 14446b4dc2780e86d93f20732c05a8e38eec0ff7 Mon Sep 17 00:00:00 2001 From: Ciprian Regus Date: Fri, 16 Sep 2022 17:09:19 +0300 Subject: dt-bindings: iio: adc: Add docs for LTC2499 Update the bindings documentation for ltc2497 to include the ltc2499. Signed-off-by: Ciprian Regus Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220916140922.2506248-3-ciprian.regus@analog.com Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml b/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml index c1772b568cd1..875f394576c2 100644 --- a/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml +++ b/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml @@ -13,10 +13,14 @@ description: | 16bit ADC supporting up to 16 single ended or 8 differential inputs. I2C interface. + https://www.analog.com/media/en/technical-documentation/data-sheets/2497fb.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/2499fe.pdf + properties: compatible: - const: - lltc,ltc2497 + enum: + - lltc,ltc2497 + - lltc,ltc2499 reg: true vref-supply: true -- cgit v1.2.3 From cc6fbf2672bba1c1042f8e5b8a61067f5d3a3214 Mon Sep 17 00:00:00 2001 From: Ciprian Regus Date: Fri, 16 Sep 2022 17:09:20 +0300 Subject: Add MAINTAINERS entries for LTC2497 and LTC2496 Update the MAINTAINERS file to include the path for the LTC2497 and LTC2496 devicetree bindings documentation. Signed-off-by: Ciprian Regus Link: https://lore.kernel.org/r/20220916140922.2506248-4-ciprian.regus@analog.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 653e4c267777..4e5d1d737cfb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1319,6 +1319,8 @@ W: https://ez.analog.com/linux-software-drivers F: Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523 F: Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 F: Documentation/devicetree/bindings/iio/*/adi,* +F: Documentation/devicetree/bindings/iio/adc/lltc,ltc2496.yaml +F: Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml F: drivers/iio/*/ad* F: drivers/iio/adc/ltc249* F: drivers/iio/amplifiers/hmc425a.c -- cgit v1.2.3 From 2187cfeb362678dae2f6235b20a6ef658836b3d4 Mon Sep 17 00:00:00 2001 From: Ciprian Regus Date: Fri, 16 Sep 2022 17:09:21 +0300 Subject: drivers: iio: adc: ltc2497: LTC2499 support The LTC2499 is a 16-channel (eight differential), 24-bit, ADC with Easy Drive technology and a 2-wire, I2C interface. Implement support for the LTC2499 ADC by extending the LTC2497 driver. A new chip_info struct is added to differentiate between chip types and resolutions when reading data from the device. Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/2499fe.pdf Signed-off-by: Ciprian Regus Link: https://lore.kernel.org/r/20220916140922.2506248-5-ciprian.regus@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ltc2496.c | 8 +++++- drivers/iio/adc/ltc2497-core.c | 2 +- drivers/iio/adc/ltc2497.c | 62 ++++++++++++++++++++++++++++++++++++++---- drivers/iio/adc/ltc2497.h | 5 ++++ 4 files changed, 70 insertions(+), 7 deletions(-) diff --git a/drivers/iio/adc/ltc2496.c b/drivers/iio/adc/ltc2496.c index dfb3bb5997e5..bf89d5ae19af 100644 --- a/drivers/iio/adc/ltc2496.c +++ b/drivers/iio/adc/ltc2496.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "ltc2497.h" @@ -74,6 +75,7 @@ static int ltc2496_probe(struct spi_device *spi) spi_set_drvdata(spi, indio_dev); st->spi = spi; st->common_ddata.result_and_measure = ltc2496_result_and_measure; + st->common_ddata.chip_info = device_get_match_data(dev); return ltc2497core_probe(dev, indio_dev); } @@ -85,8 +87,12 @@ static void ltc2496_remove(struct spi_device *spi) ltc2497core_remove(indio_dev); } +static const struct ltc2497_chip_info ltc2496_info = { + .resolution = 16, +}; + static const struct of_device_id ltc2496_of_match[] = { - { .compatible = "lltc,ltc2496", }, + { .compatible = "lltc,ltc2496", .data = <c2496_info, }, {}, }; MODULE_DEVICE_TABLE(of, ltc2496_of_match); diff --git a/drivers/iio/adc/ltc2497-core.c b/drivers/iio/adc/ltc2497-core.c index 2a485c8a1940..b2752399402c 100644 --- a/drivers/iio/adc/ltc2497-core.c +++ b/drivers/iio/adc/ltc2497-core.c @@ -95,7 +95,7 @@ static int ltc2497core_read_raw(struct iio_dev *indio_dev, return ret; *val = ret / 1000; - *val2 = 17; + *val2 = ddata->chip_info->resolution + 1; return IIO_VAL_FRACTIONAL_LOG2; diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c index 78b93c99cc47..bd6a65071711 100644 --- a/drivers/iio/adc/ltc2497.c +++ b/drivers/iio/adc/ltc2497.c @@ -12,18 +12,31 @@ #include #include #include +#include + +#include #include "ltc2497.h" +enum ltc2497_chip_type { + TYPE_LTC2497, + TYPE_LTC2499, +}; + struct ltc2497_driverdata { /* this must be the first member */ struct ltc2497core_driverdata common_ddata; struct i2c_client *client; + u32 recv_size; + u32 sub_lsb; /* * DMA (thus cache coherency maintenance) may require the * transfer buffers to live in their own cache lines. */ - __be32 buf __aligned(IIO_DMA_MINALIGN); + union { + __be32 d32; + u8 d8[3]; + } data __aligned(IIO_DMA_MINALIGN); }; static int ltc2497_result_and_measure(struct ltc2497core_driverdata *ddata, @@ -34,13 +47,30 @@ static int ltc2497_result_and_measure(struct ltc2497core_driverdata *ddata, int ret; if (val) { - ret = i2c_master_recv(st->client, (char *)&st->buf, 3); + if (st->recv_size == 3) + ret = i2c_master_recv(st->client, (char *)&st->data.d8, + st->recv_size); + else + ret = i2c_master_recv(st->client, (char *)&st->data.d32, + st->recv_size); if (ret < 0) { dev_err(&st->client->dev, "i2c_master_recv failed\n"); return ret; } - *val = (be32_to_cpu(st->buf) >> 14) - (1 << 17); + /* + * The data format is 16/24 bit 2s complement, but with an upper sign bit on the + * resolution + 1 position, which is set for positive values only. Given this + * bit's value, subtracting BIT(resolution + 1) from the ADC's result is + * equivalent to a sign extension. + */ + if (st->recv_size == 3) { + *val = (get_unaligned_be24(st->data.d8) >> st->sub_lsb) + - BIT(ddata->chip_info->resolution + 1); + } else { + *val = (be32_to_cpu(st->data.d32) >> st->sub_lsb) + - BIT(ddata->chip_info->resolution + 1); + } /* * The part started a new conversion at the end of the above i2c @@ -67,9 +97,11 @@ static int ltc2497_result_and_measure(struct ltc2497core_driverdata *ddata, static int ltc2497_probe(struct i2c_client *client, const struct i2c_device_id *id) { + const struct ltc2497_chip_info *chip_info; struct iio_dev *indio_dev; struct ltc2497_driverdata *st; struct device *dev = &client->dev; + u32 resolution; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE)) @@ -84,6 +116,15 @@ static int ltc2497_probe(struct i2c_client *client, st->client = client; st->common_ddata.result_and_measure = ltc2497_result_and_measure; + chip_info = device_get_match_data(dev); + if (!chip_info) + chip_info = (const struct ltc2497_chip_info *)id->driver_data; + st->common_ddata.chip_info = chip_info; + + resolution = chip_info->resolution; + st->sub_lsb = 31 - (resolution + 1); + st->recv_size = BITS_TO_BYTES(resolution) + 1; + return ltc2497core_probe(dev, indio_dev); } @@ -96,14 +137,25 @@ static int ltc2497_remove(struct i2c_client *client) return 0; } +static const struct ltc2497_chip_info ltc2497_info[] = { + [TYPE_LTC2497] = { + .resolution = 16, + }, + [TYPE_LTC2499] = { + .resolution = 24, + }, +}; + static const struct i2c_device_id ltc2497_id[] = { - { "ltc2497", 0 }, + { "ltc2497", (kernel_ulong_t)<c2497_info[TYPE_LTC2497] }, + { "ltc2499", (kernel_ulong_t)<c2497_info[TYPE_LTC2499] }, { } }; MODULE_DEVICE_TABLE(i2c, ltc2497_id); static const struct of_device_id ltc2497_of_match[] = { - { .compatible = "lltc,ltc2497", }, + { .compatible = "lltc,ltc2497", .data = <c2497_info[TYPE_LTC2497] }, + { .compatible = "lltc,ltc2499", .data = <c2497_info[TYPE_LTC2499] }, {}, }; MODULE_DEVICE_TABLE(of, ltc2497_of_match); diff --git a/drivers/iio/adc/ltc2497.h b/drivers/iio/adc/ltc2497.h index d0b42dd6b8ad..71957fc7e1ba 100644 --- a/drivers/iio/adc/ltc2497.h +++ b/drivers/iio/adc/ltc2497.h @@ -4,9 +4,14 @@ #define LTC2497_CONFIG_DEFAULT LTC2497_ENABLE #define LTC2497_CONVERSION_TIME_MS 150ULL +struct ltc2497_chip_info { + u32 resolution; +}; + struct ltc2497core_driverdata { struct regulator *ref; ktime_t time_prev; + const struct ltc2497_chip_info *chip_info; u8 addr_prev; int (*result_and_measure)(struct ltc2497core_driverdata *ddata, u8 address, int *val); -- cgit v1.2.3 From 1695c52a1260aee2a33e29add1adca6433d5eac7 Mon Sep 17 00:00:00 2001 From: Ciprian Regus Date: Fri, 16 Sep 2022 17:09:22 +0300 Subject: drivers: iio: adc: ltc2497: Rename the LTC2499 iio device Set the iio device's name based on the chip used for the LTC2499 only. The most common way for IIO clients to interact with a device is to address it based on it's name. By using the dev_name() function, the name will be set based on a i2c_client's kobj name, which has the format i2c_instance-i2c_address (1-0076 for example). This is not ideal, since it makes a requirement for userspace to have knowledge about the hardware connections of the device. The name field is set to NULL for the LTC2497 and LTC2496, so that the old name can kept as it is, since changing it will result in an ABI breakage. Signed-off-by: Ciprian Regus Link: https://lore.kernel.org/r/20220916140922.2506248-6-ciprian.regus@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ltc2496.c | 1 + drivers/iio/adc/ltc2497-core.c | 10 +++++++++- drivers/iio/adc/ltc2497.c | 2 ++ drivers/iio/adc/ltc2497.h | 1 + 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ltc2496.c b/drivers/iio/adc/ltc2496.c index bf89d5ae19af..2593fa4322eb 100644 --- a/drivers/iio/adc/ltc2496.c +++ b/drivers/iio/adc/ltc2496.c @@ -89,6 +89,7 @@ static void ltc2496_remove(struct spi_device *spi) static const struct ltc2497_chip_info ltc2496_info = { .resolution = 16, + .name = NULL, }; static const struct of_device_id ltc2496_of_match[] = { diff --git a/drivers/iio/adc/ltc2497-core.c b/drivers/iio/adc/ltc2497-core.c index b2752399402c..f52d37af4d1f 100644 --- a/drivers/iio/adc/ltc2497-core.c +++ b/drivers/iio/adc/ltc2497-core.c @@ -169,7 +169,15 @@ int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev) struct ltc2497core_driverdata *ddata = iio_priv(indio_dev); int ret; - indio_dev->name = dev_name(dev); + /* + * Keep using dev_name() for the iio_dev's name on some of the parts, + * since updating it would result in a ABI breakage. + */ + if (ddata->chip_info->name) + indio_dev->name = ddata->chip_info->name; + else + indio_dev->name = dev_name(dev); + indio_dev->info = <c2497core_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = ltc2497core_channel; diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c index bd6a65071711..4206f17e284c 100644 --- a/drivers/iio/adc/ltc2497.c +++ b/drivers/iio/adc/ltc2497.c @@ -140,9 +140,11 @@ static int ltc2497_remove(struct i2c_client *client) static const struct ltc2497_chip_info ltc2497_info[] = { [TYPE_LTC2497] = { .resolution = 16, + .name = NULL, }, [TYPE_LTC2499] = { .resolution = 24, + .name = "ltc2499", }, }; diff --git a/drivers/iio/adc/ltc2497.h b/drivers/iio/adc/ltc2497.h index 71957fc7e1ba..e023de0d88c4 100644 --- a/drivers/iio/adc/ltc2497.h +++ b/drivers/iio/adc/ltc2497.h @@ -6,6 +6,7 @@ struct ltc2497_chip_info { u32 resolution; + const char *name; }; struct ltc2497core_driverdata { -- cgit v1.2.3 From 186b9e3845b89df945bb9b370d61ca21888dfa52 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 15 Sep 2022 19:34:59 +0200 Subject: iio: imu: st_lsm6dsx: add support to LSM6DSTX Add support to STM LSM6DSTX (acc + gyro) Mems sensor. The LSM6DSTX sensor can use LSM6DST as fallback device since it implements all the LSM6DSTX features currently implemented in st_lsm6dsx. Datasheet: https://www.st.com/resource/en/datasheet/lsm6dstx.pdf Signed-off-by: Lorenzo Bianconi Link: https://lore.kernel.org/r/573a232b0f320b2ec92c56fa24393cfb275183fb.1663262890.git.lorenzo@kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/Kconfig | 2 +- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 2 ++ drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 2 +- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 7 ++++++- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c | 5 +++++ drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c | 5 +++++ 6 files changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/iio/imu/st_lsm6dsx/Kconfig b/drivers/iio/imu/st_lsm6dsx/Kconfig index fefd0b939100..2ed2b3f40c0b 100644 --- a/drivers/iio/imu/st_lsm6dsx/Kconfig +++ b/drivers/iio/imu/st_lsm6dsx/Kconfig @@ -12,7 +12,7 @@ config IIO_ST_LSM6DSX Say yes here to build support for STMicroelectronics LSM6DSx imu sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm, ism330dlc, lsm6dso, lsm6dsox, asm330lhh, asm330lhhx, lsm6dsr, - lsm6ds3tr-c, ism330dhcx, lsm6dsrx, lsm6ds0, lsm6dsop, + lsm6ds3tr-c, ism330dhcx, lsm6dsrx, lsm6ds0, lsm6dsop, lsm6dstx, the accelerometer/gyroscope of lsm9ds1 and lsm6dst. To compile this driver as a module, choose M here: the module diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index a86dd29a4738..6b57d47be69e 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -32,6 +32,7 @@ #define ST_LSM6DST_DEV_NAME "lsm6dst" #define ST_LSM6DSOP_DEV_NAME "lsm6dsop" #define ST_ASM330LHHX_DEV_NAME "asm330lhhx" +#define ST_LSM6DSTX_DEV_NAME "lsm6dstx" enum st_lsm6dsx_hw_id { ST_LSM6DS3_ID, @@ -51,6 +52,7 @@ enum st_lsm6dsx_hw_id { ST_LSM6DST_ID, ST_LSM6DSOP_ID, ST_ASM330LHHX_ID, + ST_LSM6DSTX_ID, ST_LSM6DSX_MAX_ID, }; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index c7d3730ab1c5..e49f2d120ed3 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -15,7 +15,7 @@ * value of the decimation factor and ODR set for each FIFO data set. * * LSM6DSO/LSM6DSOX/ASM330LHH/ASM330LHHX/LSM6DSR/LSM6DSRX/ISM330DHCX/ - * LSM6DST/LSM6DSOP: + * LSM6DST/LSM6DSOP/LSM6DSTX: * The FIFO buffer can be configured to store data from gyroscope and * accelerometer. Each sample is queued with a tag (1B) indicating data * source (gyroscope, accelerometer, hw timer). diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index b5e4a4113652..f8bbb005718e 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -26,7 +26,8 @@ * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 * - FIFO size: 4KB * - * - LSM6DSO/LSM6DSOX/ASM330LHH/ASM330LHHX/LSM6DSR/ISM330DHCX/LSM6DST/LSM6DSOP: + * - LSM6DSO/LSM6DSOX/ASM330LHH/ASM330LHHX/LSM6DSR/ISM330DHCX/LSM6DST/LSM6DSOP/ + * LSM6DSTX: * - Accelerometer/Gyroscope supported ODR [Hz]: 12.5, 26, 52, 104, 208, 416, * 833 * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 @@ -791,6 +792,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .hw_id = ST_ASM330LHHX_ID, .name = ST_ASM330LHHX_DEV_NAME, .wai = 0x6b, + }, { + .hw_id = ST_LSM6DSTX_ID, + .name = ST_LSM6DSTX_DEV_NAME, + .wai = 0x6d, }, }, .channels = { diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c index 2ea34c0d3a8c..307c8c436862 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c @@ -105,6 +105,10 @@ static const struct of_device_id st_lsm6dsx_i2c_of_match[] = { .compatible = "st,asm330lhhx", .data = (void *)ST_ASM330LHHX_ID, }, + { + .compatible = "st,lsm6dstx", + .data = (void *)ST_LSM6DSTX_ID, + }, {}, }; MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match); @@ -127,6 +131,7 @@ static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = { { ST_LSM6DST_DEV_NAME, ST_LSM6DST_ID }, { ST_LSM6DSOP_DEV_NAME, ST_LSM6DSOP_ID }, { ST_ASM330LHHX_DEV_NAME, ST_ASM330LHHX_ID }, + { ST_LSM6DSTX_DEV_NAME, ST_LSM6DSTX_ID }, {}, }; MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c index 6a8883f022a8..6a4eecf4bb05 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c @@ -105,6 +105,10 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = { .compatible = "st,asm330lhhx", .data = (void *)ST_ASM330LHHX_ID, }, + { + .compatible = "st,lsm6dstx", + .data = (void *)ST_LSM6DSTX_ID, + }, {}, }; MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match); @@ -127,6 +131,7 @@ static const struct spi_device_id st_lsm6dsx_spi_id_table[] = { { ST_LSM6DST_DEV_NAME, ST_LSM6DST_ID }, { ST_LSM6DSOP_DEV_NAME, ST_LSM6DSOP_ID }, { ST_ASM330LHHX_DEV_NAME, ST_ASM330LHHX_ID }, + { ST_LSM6DSTX_DEV_NAME, ST_LSM6DSTX_ID }, {}, }; MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table); -- cgit v1.2.3 From fd8059ec60193c11e3237bbf86dff4f51946c94e Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 15 Sep 2022 19:35:00 +0200 Subject: dt-bindings: iio: imu: st_lsm6dsx: add lsm6dstx device bindings Add device bindings for lsm6dstx IMU sensor. Use lsm6dst as fallback device for lsm6dstx since it implements all the features currently supported by lsm6dstx. Signed-off-by: Lorenzo Bianconi Link: https://lore.kernel.org/r/dadc2acf88b0b73feac9a8980ad9af1658297bc1.1663262890.git.lorenzo@kernel.org Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml b/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml index 0ceb29fb01b7..fe1e02e5d7b3 100644 --- a/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml +++ b/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml @@ -35,6 +35,9 @@ properties: - items: - const: st,asm330lhhx - const: st,lsm6dsr + - items: + - const: st,lsm6dstx + - const: st,lsm6dst reg: maxItems: 1 -- cgit v1.2.3 From 548616242fe025b8c071e8d760fba6c4f71b0bc6 Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Thu, 15 Sep 2022 15:54:52 +0200 Subject: iio: adc: stm32-adc: add id registers support Add support of identification registers to STM32 ADC. By default the ADC hardware instance number is retrieved from the compatible configuration data. Get the available ADC number per ADC block, from hardware configuration register, when this register exists. Signed-off-by: Olivier Moysan Reviewed-by: Fabrice Gasnier Link: https://lore.kernel.org/r/20220915135452.1712453-1-olivier.moysan@foss.st.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc-core.c | 59 ++++++++++++++++++++++++++++++++++++++-- drivers/iio/adc/stm32-adc-core.h | 31 +++++++++++++++++++++ 2 files changed, 87 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index 1ce52af3fe8b..81d5db91c67b 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -9,6 +9,7 @@ * */ +#include #include #include #include @@ -62,6 +63,7 @@ struct stm32_adc_priv; * @regs: common registers for all instances * @clk_sel: clock selection routine * @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet) + * @ipid: adc identification number * @has_syscfg: SYSCFG capability flags * @num_irqs: number of interrupt lines * @num_adcs: maximum number of ADC instances in the common registers @@ -70,6 +72,7 @@ struct stm32_adc_priv_cfg { const struct stm32_adc_common_regs *regs; int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *); u32 max_clk_rate_hz; + u32 ipid; unsigned int has_syscfg; unsigned int num_irqs; unsigned int num_adcs; @@ -78,6 +81,7 @@ struct stm32_adc_priv_cfg { /** * struct stm32_adc_priv - stm32 ADC core private data * @irq: irq(s) for ADC block + * @nb_adc_max: actual maximum number of instance per ADC block * @domain: irq domain reference * @aclk: clock reference for the analog circuitry * @bclk: bus clock common for all ADCs, depends on part used @@ -95,6 +99,7 @@ struct stm32_adc_priv_cfg { */ struct stm32_adc_priv { int irq[STM32_ADC_MAX_ADCS]; + unsigned int nb_adc_max; struct irq_domain *domain; struct clk *aclk; struct clk *bclk; @@ -354,7 +359,7 @@ static void stm32_adc_irq_handler(struct irq_desc *desc) * before invoking the interrupt handler (e.g. call ISR only for * IRQ-enabled ADCs). */ - for (i = 0; i < priv->cfg->num_adcs; i++) { + for (i = 0; i < priv->nb_adc_max; i++) { if ((status & priv->cfg->regs->eoc_msk[i] && stm32_adc_eoc_enabled(priv, i)) || (status & priv->cfg->regs->ovr_msk[i])) @@ -424,7 +429,7 @@ static void stm32_adc_irq_remove(struct platform_device *pdev, int hwirq; unsigned int i; - for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++) + for (hwirq = 0; hwirq < priv->nb_adc_max; hwirq++) irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq)); irq_domain_remove(priv->domain); @@ -642,6 +647,49 @@ static int stm32_adc_core_switches_probe(struct device *dev, return 0; } +static int stm32_adc_probe_identification(struct platform_device *pdev, + struct stm32_adc_priv *priv) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *child; + const char *compat; + int ret, count = 0; + u32 id, val; + + if (!priv->cfg->ipid) + return 0; + + id = FIELD_GET(STM32MP1_IPIDR_MASK, + readl_relaxed(priv->common.base + STM32MP1_ADC_IPDR)); + if (id != priv->cfg->ipid) { + dev_err(&pdev->dev, "Unexpected IP version: 0x%x", id); + return -EINVAL; + } + + for_each_child_of_node(np, child) { + ret = of_property_read_string(child, "compatible", &compat); + if (ret) + continue; + /* Count child nodes with stm32 adc compatible */ + if (strstr(compat, "st,stm32") && strstr(compat, "adc")) + count++; + } + + val = readl_relaxed(priv->common.base + STM32MP1_ADC_HWCFGR0); + priv->nb_adc_max = FIELD_GET(STM32MP1_ADCNUM_MASK, val); + if (count > priv->nb_adc_max) { + dev_err(&pdev->dev, "Unexpected child number: %d", count); + return -EINVAL; + } + + val = readl_relaxed(priv->common.base + STM32MP1_ADC_VERR); + dev_dbg(&pdev->dev, "ADC version: %lu.%lu\n", + FIELD_GET(STM32MP1_MAJREV_MASK, val), + FIELD_GET(STM32MP1_MINREV_MASK, val)); + + return 0; +} + static int stm32_adc_probe(struct platform_device *pdev) { struct stm32_adc_priv *priv; @@ -661,6 +709,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; + priv->nb_adc_max = priv->cfg->num_adcs; spin_lock_init(&priv->common.lock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -703,6 +752,10 @@ static int stm32_adc_probe(struct platform_device *pdev) if (ret) goto err_pm_stop; + ret = stm32_adc_probe_identification(pdev, priv); + if (ret < 0) + goto err_hw_stop; + ret = regulator_get_voltage(priv->vref); if (ret < 0) { dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret); @@ -811,8 +864,8 @@ static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = { .clk_sel = stm32h7_adc_clk_sel, .max_clk_rate_hz = 36000000, .has_syscfg = HAS_VBOOSTER | HAS_ANASWVDD, + .ipid = STM32MP15_IPIDR_NUMBER, .num_irqs = 2, - .num_adcs = 2, }; static const struct of_device_id stm32_adc_of_match[] = { diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h index faedf7a49555..2118ef63843d 100644 --- a/drivers/iio/adc/stm32-adc-core.h +++ b/drivers/iio/adc/stm32-adc-core.h @@ -24,6 +24,7 @@ * | 0x300 | Master & Slave common regs | * -------------------------------------------------------- */ +/* Maximum ADC instances number per ADC block for all supported SoCs */ #define STM32_ADC_MAX_ADCS 3 #define STM32_ADC_OFFSET 0x100 #define STM32_ADCX_COMN_OFFSET 0x300 @@ -105,6 +106,12 @@ /* STM32MP1 - ADC2 instance option register */ #define STM32MP1_ADC2_OR 0xD0 +/* STM32MP1 - Identification registers */ +#define STM32MP1_ADC_HWCFGR0 0x3F0 +#define STM32MP1_ADC_VERR 0x3F4 +#define STM32MP1_ADC_IPDR 0x3F8 +#define STM32MP1_ADC_SIDR 0x3FC + /* 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) @@ -181,6 +188,30 @@ enum stm32h7_adc_dmngt { /* STM32MP1_ADC2_OR - bit fields */ #define STM32MP1_VDDCOREEN BIT(0) +/* STM32MP1_ADC_HWCFGR0 - bit fields */ +#define STM32MP1_ADCNUM_SHIFT 0 +#define STM32MP1_ADCNUM_MASK GENMASK(3, 0) +#define STM32MP1_MULPIPE_SHIFT 4 +#define STM32MP1_MULPIPE_MASK GENMASK(7, 4) +#define STM32MP1_OPBITS_SHIFT 8 +#define STM32MP1_OPBITS_MASK GENMASK(11, 8) +#define STM32MP1_IDLEVALUE_SHIFT 12 +#define STM32MP1_IDLEVALUE_MASK GENMASK(15, 12) + +/* STM32MP1_ADC_VERR - bit fields */ +#define STM32MP1_MINREV_SHIFT 0 +#define STM32MP1_MINREV_MASK GENMASK(3, 0) +#define STM32MP1_MAJREV_SHIFT 4 +#define STM32MP1_MAJREV_MASK GENMASK(7, 4) + +/* STM32MP1_ADC_IPDR - bit fields */ +#define STM32MP1_IPIDR_MASK GENMASK(31, 0) + +/* STM32MP1_ADC_SIDR - bit fields */ +#define STM32MP1_SIDR_MASK GENMASK(31, 0) + +#define STM32MP15_IPIDR_NUMBER 0x00110005 + /** * struct stm32_adc_common - stm32 ADC driver common data (for all instances) * @base: control registers base cpu addr -- cgit v1.2.3 From f4f43f01cff2f29779343ade755191afd2581c77 Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Mon, 12 Sep 2022 10:12:21 +0200 Subject: iio: adc: ad7923: fix channel readings for some variants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some of the supported devices have 4 or 2 LSB trailing bits that should not be taken into account. Hence we need to shift these bits out which fits perfectly on the scan type shift property. This change fixes both raw and buffered reads. Fixes: f2f7a449707e ("iio:adc:ad7923: Add support for the ad7904/ad7914/ad7924") Fixes: 851644a60d20 ("iio: adc: ad7923: Add support for the ad7908/ad7918/ad7928") Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20220912081223.173584-2-nuno.sa@analog.com Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7923.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c index edad1f30121d..502253f53d96 100644 --- a/drivers/iio/adc/ad7923.c +++ b/drivers/iio/adc/ad7923.c @@ -93,6 +93,7 @@ enum ad7923_id { .sign = 'u', \ .realbits = (bits), \ .storagebits = 16, \ + .shift = 12 - (bits), \ .endianness = IIO_BE, \ }, \ } @@ -268,7 +269,8 @@ static int ad7923_read_raw(struct iio_dev *indio_dev, return ret; if (chan->address == EXTRACT(ret, 12, 4)) - *val = EXTRACT(ret, 0, 12); + *val = EXTRACT(ret, chan->scan_type.shift, + chan->scan_type.realbits); else return -EIO; -- cgit v1.2.3 From 096d2dac25f9947d1cb1e16a16468de8fda685e6 Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Mon, 12 Sep 2022 10:12:22 +0200 Subject: iio: adc: ad7923: support extended range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By default the driver was always setting the RANGE bit which means that the analog input goes from 0 to VREF. However, we might want to have 0 to 2xVREF. This change adds a new Firmware property to allow for the extended range while keeping the default behavior if nothing is provided. Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20220912081223.173584-3-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7923.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c index 502253f53d96..9d6bf6d0927a 100644 --- a/drivers/iio/adc/ad7923.c +++ b/drivers/iio/adc/ad7923.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -300,6 +301,7 @@ static void ad7923_regulator_disable(void *data) static int ad7923_probe(struct spi_device *spi) { + u32 ad7923_range = AD7923_RANGE; struct ad7923_state *st; struct iio_dev *indio_dev; const struct ad7923_chip_info *info; @@ -311,8 +313,11 @@ static int ad7923_probe(struct spi_device *spi) st = iio_priv(indio_dev); + if (device_property_read_bool(&spi->dev, "adi,range-double")) + ad7923_range = 0; + st->spi = spi; - st->settings = AD7923_CODING | AD7923_RANGE | + st->settings = AD7923_CODING | ad7923_range | AD7923_PM_MODE_WRITE(AD7923_PM_MODE_OPS); info = &ad7923_chip_info[spi_get_device_id(spi)->driver_data]; -- cgit v1.2.3 From 4b622a658c21d4de7f6fdb6b5e1c99bfb20b2d22 Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Mon, 12 Sep 2022 10:12:23 +0200 Subject: dt-bindings: iio: adi,ad7923: add adi,range-double property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document the new property to enable doubling the analog input range. Signed-off-by: Nuno Sá Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220912081223.173584-4-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml index 40b0a887db57..07f9d1c09c7d 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml @@ -36,6 +36,10 @@ properties: description: | The regulator supply for ADC reference voltage. + adi,range-double: + description: Sets the analog input range from 0 to 2xVREF. + type: boolean + '#address-cells': const: 1 -- cgit v1.2.3 From c2329717bdd3fa62f8a2f3d8d85ad0bee4556bd7 Mon Sep 17 00:00:00 2001 From: Eddie James Date: Thu, 15 Sep 2022 14:57:18 -0500 Subject: iio: pressure: dps310: Refactor startup procedure Move the startup procedure into a function, and correct a missing check on the return code for writing the PRS_CFG register. Cc: Signed-off-by: Eddie James Reviewed-by: Joel Stanley Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220915195719.136812-2-eajames@linux.ibm.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/dps310.c | 188 ++++++++++++++++++++++-------------------- 1 file changed, 99 insertions(+), 89 deletions(-) diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c index 36fb7ae0d0a9..c706a8b423b5 100644 --- a/drivers/iio/pressure/dps310.c +++ b/drivers/iio/pressure/dps310.c @@ -159,6 +159,102 @@ static int dps310_get_coefs(struct dps310_data *data) return 0; } +/* + * Some versions of the chip will read temperatures in the ~60C range when + * it's actually ~20C. This is the manufacturer recommended workaround + * to correct the issue. The registers used below are undocumented. + */ +static int dps310_temp_workaround(struct dps310_data *data) +{ + int rc; + int reg; + + rc = regmap_read(data->regmap, 0x32, ®); + if (rc) + return rc; + + /* + * If bit 1 is set then the device is okay, and the workaround does not + * need to be applied + */ + if (reg & BIT(1)) + return 0; + + rc = regmap_write(data->regmap, 0x0e, 0xA5); + if (rc) + return rc; + + rc = regmap_write(data->regmap, 0x0f, 0x96); + if (rc) + return rc; + + rc = regmap_write(data->regmap, 0x62, 0x02); + if (rc) + return rc; + + rc = regmap_write(data->regmap, 0x0e, 0x00); + if (rc) + return rc; + + return regmap_write(data->regmap, 0x0f, 0x00); +} + +static int dps310_startup(struct dps310_data *data) +{ + int rc; + int ready; + + /* + * Set up pressure sensor in single sample, one measurement per second + * mode + */ + rc = regmap_write(data->regmap, DPS310_PRS_CFG, 0); + if (rc) + return rc; + + /* + * Set up external (MEMS) temperature sensor in single sample, one + * measurement per second mode + */ + rc = regmap_write(data->regmap, DPS310_TMP_CFG, DPS310_TMP_EXT); + if (rc) + return rc; + + /* Temp and pressure shifts are disabled when PRC <= 8 */ + rc = regmap_write_bits(data->regmap, DPS310_CFG_REG, + DPS310_PRS_SHIFT_EN | DPS310_TMP_SHIFT_EN, 0); + if (rc) + return rc; + + /* MEAS_CFG doesn't update correctly unless first written with 0 */ + rc = regmap_write_bits(data->regmap, DPS310_MEAS_CFG, + DPS310_MEAS_CTRL_BITS, 0); + if (rc) + return rc; + + /* Turn on temperature and pressure measurement in the background */ + rc = regmap_write_bits(data->regmap, DPS310_MEAS_CFG, + DPS310_MEAS_CTRL_BITS, DPS310_PRS_EN | + DPS310_TEMP_EN | DPS310_BACKGROUND); + if (rc) + return rc; + + /* + * Calibration coefficients required for reporting temperature. + * They are available 40ms after the device has started + */ + rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready, + ready & DPS310_COEF_RDY, 10000, 40000); + if (rc) + return rc; + + rc = dps310_get_coefs(data); + if (rc) + return rc; + + return dps310_temp_workaround(data); +} + static int dps310_get_pres_precision(struct dps310_data *data) { int rc; @@ -677,52 +773,12 @@ static const struct iio_info dps310_info = { .write_raw = dps310_write_raw, }; -/* - * Some verions of chip will read temperatures in the ~60C range when - * its actually ~20C. This is the manufacturer recommended workaround - * to correct the issue. The registers used below are undocumented. - */ -static int dps310_temp_workaround(struct dps310_data *data) -{ - int rc; - int reg; - - rc = regmap_read(data->regmap, 0x32, ®); - if (rc < 0) - return rc; - - /* - * If bit 1 is set then the device is okay, and the workaround does not - * need to be applied - */ - if (reg & BIT(1)) - return 0; - - rc = regmap_write(data->regmap, 0x0e, 0xA5); - if (rc < 0) - return rc; - - rc = regmap_write(data->regmap, 0x0f, 0x96); - if (rc < 0) - return rc; - - rc = regmap_write(data->regmap, 0x62, 0x02); - if (rc < 0) - return rc; - - rc = regmap_write(data->regmap, 0x0e, 0x00); - if (rc < 0) - return rc; - - return regmap_write(data->regmap, 0x0f, 0x00); -} - static int dps310_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct dps310_data *data; struct iio_dev *iio; - int rc, ready; + int rc; iio = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!iio) @@ -747,54 +803,8 @@ static int dps310_probe(struct i2c_client *client, if (rc) return rc; - /* - * Set up pressure sensor in single sample, one measurement per second - * mode - */ - rc = regmap_write(data->regmap, DPS310_PRS_CFG, 0); - - /* - * Set up external (MEMS) temperature sensor in single sample, one - * measurement per second mode - */ - rc = regmap_write(data->regmap, DPS310_TMP_CFG, DPS310_TMP_EXT); - if (rc < 0) - return rc; - - /* Temp and pressure shifts are disabled when PRC <= 8 */ - rc = regmap_write_bits(data->regmap, DPS310_CFG_REG, - DPS310_PRS_SHIFT_EN | DPS310_TMP_SHIFT_EN, 0); - if (rc < 0) - return rc; - - /* MEAS_CFG doesn't update correctly unless first written with 0 */ - rc = regmap_write_bits(data->regmap, DPS310_MEAS_CFG, - DPS310_MEAS_CTRL_BITS, 0); - if (rc < 0) - return rc; - - /* Turn on temperature and pressure measurement in the background */ - rc = regmap_write_bits(data->regmap, DPS310_MEAS_CFG, - DPS310_MEAS_CTRL_BITS, DPS310_PRS_EN | - DPS310_TEMP_EN | DPS310_BACKGROUND); - if (rc < 0) - return rc; - - /* - * Calibration coefficients required for reporting temperature. - * They are available 40ms after the device has started - */ - rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready, - ready & DPS310_COEF_RDY, 10000, 40000); - if (rc < 0) - return rc; - - rc = dps310_get_coefs(data); - if (rc < 0) - return rc; - - rc = dps310_temp_workaround(data); - if (rc < 0) + rc = dps310_startup(data); + if (rc) return rc; rc = devm_iio_device_register(&client->dev, iio); -- cgit v1.2.3 From 7b4ab4abcea4c0c10b25187bf2569e5a07e9a20c Mon Sep 17 00:00:00 2001 From: Eddie James Date: Thu, 15 Sep 2022 14:57:19 -0500 Subject: iio: pressure: dps310: Reset chip after timeout The DPS310 chip has been observed to get "stuck" such that pressure and temperature measurements are never indicated as "ready" in the MEAS_CFG register. The only solution is to reset the device and try again. In order to avoid continual failures, use a boolean flag to only try the reset after timeout once if errors persist. Fixes: ba6ec48e76bc ("iio: Add driver for Infineon DPS310") Cc: Signed-off-by: Eddie James Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220915195719.136812-3-eajames@linux.ibm.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/dps310.c | 74 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 64 insertions(+), 10 deletions(-) diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c index c706a8b423b5..984a3f511a1a 100644 --- a/drivers/iio/pressure/dps310.c +++ b/drivers/iio/pressure/dps310.c @@ -89,6 +89,7 @@ struct dps310_data { s32 c00, c10, c20, c30, c01, c11, c21; s32 pressure_raw; s32 temp_raw; + bool timeout_recovery_failed; }; static const struct iio_chan_spec dps310_channels[] = { @@ -393,11 +394,69 @@ static int dps310_get_temp_k(struct dps310_data *data) return scale_factors[ilog2(rc)]; } +static int dps310_reset_wait(struct dps310_data *data) +{ + int rc; + + rc = regmap_write(data->regmap, DPS310_RESET, DPS310_RESET_MAGIC); + if (rc) + return rc; + + /* Wait for device chip access: 2.5ms in specification */ + usleep_range(2500, 12000); + return 0; +} + +static int dps310_reset_reinit(struct dps310_data *data) +{ + int rc; + + rc = dps310_reset_wait(data); + if (rc) + return rc; + + return dps310_startup(data); +} + +static int dps310_ready_status(struct dps310_data *data, int ready_bit, int timeout) +{ + int sleep = DPS310_POLL_SLEEP_US(timeout); + int ready; + + return regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready, ready & ready_bit, + sleep, timeout); +} + +static int dps310_ready(struct dps310_data *data, int ready_bit, int timeout) +{ + int rc; + + rc = dps310_ready_status(data, ready_bit, timeout); + if (rc) { + if (rc == -ETIMEDOUT && !data->timeout_recovery_failed) { + /* Reset and reinitialize the chip. */ + if (dps310_reset_reinit(data)) { + data->timeout_recovery_failed = true; + } else { + /* Try again to get sensor ready status. */ + if (dps310_ready_status(data, ready_bit, timeout)) + data->timeout_recovery_failed = true; + else + return 0; + } + } + + return rc; + } + + data->timeout_recovery_failed = false; + return 0; +} + static int dps310_read_pres_raw(struct dps310_data *data) { int rc; int rate; - int ready; int timeout; s32 raw; u8 val[3]; @@ -409,9 +468,7 @@ static int dps310_read_pres_raw(struct dps310_data *data) timeout = DPS310_POLL_TIMEOUT_US(rate); /* Poll for sensor readiness; base the timeout upon the sample rate. */ - rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready, - ready & DPS310_PRS_RDY, - DPS310_POLL_SLEEP_US(timeout), timeout); + rc = dps310_ready(data, DPS310_PRS_RDY, timeout); if (rc) goto done; @@ -448,7 +505,6 @@ static int dps310_read_temp_raw(struct dps310_data *data) { int rc; int rate; - int ready; int timeout; if (mutex_lock_interruptible(&data->lock)) @@ -458,10 +514,8 @@ static int dps310_read_temp_raw(struct dps310_data *data) timeout = DPS310_POLL_TIMEOUT_US(rate); /* Poll for sensor readiness; base the timeout upon the sample rate. */ - rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready, - ready & DPS310_TMP_RDY, - DPS310_POLL_SLEEP_US(timeout), timeout); - if (rc < 0) + rc = dps310_ready(data, DPS310_TMP_RDY, timeout); + if (rc) goto done; rc = dps310_read_temp_ready(data); @@ -756,7 +810,7 @@ static void dps310_reset(void *action_data) { struct dps310_data *data = action_data; - regmap_write(data->regmap, DPS310_RESET, DPS310_RESET_MAGIC); + dps310_reset_wait(data); } static const struct regmap_config dps310_regmap_config = { -- cgit v1.2.3 From bcc57a48eaee63a71983996c4c9d89ce7cbf55d9 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Wed, 7 Sep 2022 15:21:52 +0200 Subject: iio: add modifiers for linear acceleration Add IIO_MOD_LINEAR_X, IIO_MOD_LINEAR_Y and IIO_MOD_LINEAR_Z modifiers to te IIO core, which is preparatory for adding the Bosch BNO055 IMU driver. Bosch BNO055 IMU can report raw accelerations (among x, y and z axis) as well as the so called "linear accelerations" (again, among x, y and z axis) which is basically the acceleration after subtracting gravity and for which those new modifiers are for. Signed-off-by: Andrea Merello Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220907132205.28021-2-andrea.merello@iit.it Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 3 +++ include/uapi/linux/iio/types.h | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 583e0e5205a0..6f7b6fd0c6ef 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -134,6 +134,9 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_ETHANOL] = "ethanol", [IIO_MOD_H2] = "h2", [IIO_MOD_O2] = "o2", + [IIO_MOD_LINEAR_X] = "linear_x", + [IIO_MOD_LINEAR_Y] = "linear_y", + [IIO_MOD_LINEAR_Z] = "linear_z", }; /* relies on pairs of these shared then separate */ diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h index 913864221ac4..b7ba9861a24d 100644 --- a/include/uapi/linux/iio/types.h +++ b/include/uapi/linux/iio/types.h @@ -95,6 +95,9 @@ enum iio_modifier { IIO_MOD_ETHANOL, IIO_MOD_H2, IIO_MOD_O2, + IIO_MOD_LINEAR_X, + IIO_MOD_LINEAR_Y, + IIO_MOD_LINEAR_Z, }; enum iio_event_type { @@ -118,4 +121,3 @@ enum iio_event_direction { }; #endif /* _UAPI_IIO_TYPES_H_ */ - -- cgit v1.2.3 From 396882617d1087a0a3fb56d0a59d6dc216640680 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Wed, 7 Sep 2022 15:21:53 +0200 Subject: iio: document linear acceleration modifiers Introduce ABI documentation for new IIO modifiers used for reporting "linear acceleration" measures. Signed-off-by: Andrea Merello Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220907132205.28021-3-andrea.merello@iit.it Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 66e81c48ee21..3f2a92891f16 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -260,6 +260,15 @@ Description: Has all of the equivalent parameters as per voltageY. Units after application of scale and offset are m/s^2. +What: /sys/bus/iio/devices/iio:deviceX/in_accel_linear_x_raw +What: /sys/bus/iio/devices/iio:deviceX/in_accel_linear_y_raw +What: /sys/bus/iio/devices/iio:deviceX/in_accel_linear_z_raw +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + As per in_accel_X_raw attributes, but minus the + acceleration due to gravity. + What: /sys/bus/iio/devices/iio:deviceX/in_gravity_x_raw What: /sys/bus/iio/devices/iio:deviceX/in_gravity_y_raw What: /sys/bus/iio/devices/iio:deviceX/in_gravity_z_raw -- cgit v1.2.3 From 4db63c85462ed81eb3b27f805ed8571f9563dbd7 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Wed, 7 Sep 2022 15:21:54 +0200 Subject: iio: event_monitor: add linear acceleration modifiers Following the introduction of IIO linear acceleration modifiers, update the event_monitor tool accordingly. Signed-off-by: Andrea Merello Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220907132205.28021-4-andrea.merello@iit.it Signed-off-by: Jonathan Cameron --- tools/iio/iio_event_monitor.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index b3b3ea399f67..f88ffdc3eb40 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -125,6 +125,9 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_PM4] = "pm4", [IIO_MOD_PM10] = "pm10", [IIO_MOD_O2] = "o2", + [IIO_MOD_LINEAR_X] = "linear_x", + [IIO_MOD_LINEAR_Y] = "linear_y", + [IIO_MOD_LINEAR_Z] = "linear_z", }; static bool event_is_known(struct iio_event_data *event) -- cgit v1.2.3 From dcedf14553810cd6bbf7227c995beb4548e0859d Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Wed, 7 Sep 2022 15:21:55 +0200 Subject: iio: add modifers for pitch, yaw, roll Add modifiers for reporting rotations as euler angles (i.e. yaw, pitch and roll). Signed-off-by: Andrea Merello Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220907132205.28021-5-andrea.merello@iit.it Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 3 +++ include/uapi/linux/iio/types.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 6f7b6fd0c6ef..8f12993f87be 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -137,6 +137,9 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_LINEAR_X] = "linear_x", [IIO_MOD_LINEAR_Y] = "linear_y", [IIO_MOD_LINEAR_Z] = "linear_z", + [IIO_MOD_PITCH] = "pitch", + [IIO_MOD_YAW] = "yaw", + [IIO_MOD_ROLL] = "roll", }; /* relies on pairs of these shared then separate */ diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h index b7ba9861a24d..c79f2f046a0b 100644 --- a/include/uapi/linux/iio/types.h +++ b/include/uapi/linux/iio/types.h @@ -98,6 +98,9 @@ enum iio_modifier { IIO_MOD_LINEAR_X, IIO_MOD_LINEAR_Y, IIO_MOD_LINEAR_Z, + IIO_MOD_PITCH, + IIO_MOD_YAW, + IIO_MOD_ROLL, }; enum iio_event_type { -- cgit v1.2.3 From 246d178c78685fc6ffcd8339ef0e64771024d82f Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Wed, 7 Sep 2022 15:21:56 +0200 Subject: iio: document pitch, yaw, roll modifiers Introduce ABI documentation for new modifiers used for reporting rotations expressed as euler angles (i.e. yaw, pitch, roll). It looks like we have some unit inconsistency along various IIO modifiers: it seems that incli is in deg, angl is in radians and rot isn't documented, but at least the adis16209 driver has rot in deg. Here we use deg (so angl is the only one using radians). Signed-off-by: Andrea Merello Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220907132205.28021-6-andrea.merello@iit.it Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 3f2a92891f16..c79b3fde7668 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -2146,3 +2146,12 @@ Contact: linux-iio@vger.kernel.org Description: Lists all available time values between upper peak to lower peak. Units in seconds. + +What: /sys/bus/iio/devices/iio:deviceX/in_rot_yaw_raw +What: /sys/bus/iio/devices/iio:deviceX/in_rot_pitch_raw +What: /sys/bus/iio/devices/iio:deviceX/in_rot_roll_raw +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Raw (unscaled) euler angles readings. Units after + application of scale are deg. -- cgit v1.2.3 From ab9d343da3a43c8e28c6341cbf7fd30b6bc42664 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Wed, 7 Sep 2022 15:21:57 +0200 Subject: iio: event_monitor: add pitch, yaw and roll modifiers Following the introduction of pitch, yaw and roll IIO modifiers, update the event_monitor tool accordingly. Signed-off-by: Andrea Merello Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220907132205.28021-7-andrea.merello@iit.it Signed-off-by: Jonathan Cameron --- tools/iio/iio_event_monitor.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index f88ffdc3eb40..0a5c2bb60030 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -128,6 +128,9 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_LINEAR_X] = "linear_x", [IIO_MOD_LINEAR_Y] = "linear_y", [IIO_MOD_LINEAR_Z] = "linear_z", + [IIO_MOD_PITCH] = "pitch", + [IIO_MOD_YAW] = "yaw", + [IIO_MOD_ROLL] = "roll", }; static bool event_is_known(struct iio_event_data *event) -- cgit v1.2.3 From e0d27ea3ba1332c81060d9d52182953dbc835b40 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Wed, 7 Sep 2022 15:21:58 +0200 Subject: iio: add support for binary attributes When a IIO device is registered, the IIO core creates an attribute group on its own, where it puts the channel attributes, and where it copies the attributes in indio_dev->info->attrs. Unfortunately it doesn't take care of binary attributes (i.e. it only consider indio_dev->info->attrs->attrs, and it ignores indio_dev->info->attrs->bin_attrs). Fix this by making the IIO layer take care also of the binary attributes. Note that while it is necessary to copy the non-binary attributes because the IIO layer needs more room to add the channels attribute, it should be enough to assign the bin_attrs pointer to the binary attributes pointed by indio_dev->info->attrs->bin_attrs. Signed-off-by: Andrea Merello Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220907132205.28021-8-andrea.merello@iit.it Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 8f12993f87be..151ff3993354 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1576,7 +1576,7 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev) ret = -ENOMEM; goto error_clear_attrs; } - /* Copy across original attributes */ + /* Copy across original attributes, and point to original binary attributes */ if (indio_dev->info->attrs) { memcpy(iio_dev_opaque->chan_attr_group.attrs, indio_dev->info->attrs->attrs, @@ -1584,6 +1584,8 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev) *attrcount_orig); iio_dev_opaque->chan_attr_group.is_visible = indio_dev->info->attrs->is_visible; + iio_dev_opaque->chan_attr_group.bin_attrs = + indio_dev->info->attrs->bin_attrs; } attrn = attrcount_orig; /* Add all elements from the list. */ -- cgit v1.2.3 From 4aefe1c2bd0cb0223130671d459cd16efa3d3462 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Wed, 7 Sep 2022 15:21:59 +0200 Subject: iio: imu: add Bosch Sensortec BNO055 core driver Add the core driver for the BNO055 IMU from Bosch. This IMU can be connected via both serial and I2C busses; separate patches will add support for them. The driver supports "AMG" (Accelerometer, Magnetometer, Gyroscope) mode, that provides raw data from the said internal sensors, and a couple of "fusion" modes (i.e. the IMU also does calculations in order to provide euler angles, quaternions, linear acceleration and gravity measurements). In fusion modes the AMG data is still available (with some calibration refinements done by the IMU), but certain settings such as low pass filters cut-off frequency and sensors' ranges are fixed, while in AMG mode they can be customized; this is why AMG mode can still be interesting. Signed-off-by: Andrea Merello Link: https://lore.kernel.org/r/20220907132205.28021-9-andrea.merello@iit.it Signed-off-by: Jonathan Cameron --- drivers/iio/imu/Kconfig | 1 + drivers/iio/imu/Makefile | 1 + drivers/iio/imu/bno055/Kconfig | 4 + drivers/iio/imu/bno055/Makefile | 3 + drivers/iio/imu/bno055/bno055.c | 1685 +++++++++++++++++++++++++++++++++++++++ drivers/iio/imu/bno055/bno055.h | 13 + 6 files changed, 1707 insertions(+) create mode 100644 drivers/iio/imu/bno055/Kconfig create mode 100644 drivers/iio/imu/bno055/Makefile create mode 100644 drivers/iio/imu/bno055/bno055.c create mode 100644 drivers/iio/imu/bno055/bno055.h diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig index 001ca2c3ff95..f1d7d4b5e222 100644 --- a/drivers/iio/imu/Kconfig +++ b/drivers/iio/imu/Kconfig @@ -52,6 +52,7 @@ config ADIS16480 ADIS16485, ADIS16488 inertial sensors. source "drivers/iio/imu/bmi160/Kconfig" +source "drivers/iio/imu/bno055/Kconfig" config FXOS8700 tristate diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile index c82748096c77..6eb612034722 100644 --- a/drivers/iio/imu/Makefile +++ b/drivers/iio/imu/Makefile @@ -15,6 +15,7 @@ adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o obj-y += bmi160/ +obj-y += bno055/ obj-$(CONFIG_FXOS8700) += fxos8700_core.o obj-$(CONFIG_FXOS8700_I2C) += fxos8700_i2c.o diff --git a/drivers/iio/imu/bno055/Kconfig b/drivers/iio/imu/bno055/Kconfig new file mode 100644 index 000000000000..467bc0ed22f8 --- /dev/null +++ b/drivers/iio/imu/bno055/Kconfig @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 + +config BOSCH_BNO055 + tristate diff --git a/drivers/iio/imu/bno055/Makefile b/drivers/iio/imu/bno055/Makefile new file mode 100644 index 000000000000..0a682ade510c --- /dev/null +++ b/drivers/iio/imu/bno055/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_BOSCH_BNO055) += bno055.o diff --git a/drivers/iio/imu/bno055/bno055.c b/drivers/iio/imu/bno055/bno055.c new file mode 100644 index 000000000000..307557a609e3 --- /dev/null +++ b/drivers/iio/imu/bno055/bno055.c @@ -0,0 +1,1685 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IIO driver for Bosch BNO055 IMU + * + * Copyright (C) 2021-2022 Istituto Italiano di Tecnologia + * Electronic Design Laboratory + * Written by Andrea Merello + * + * Portions of this driver are taken from the BNO055 driver patch + * from Vlad Dogaru which is Copyright (c) 2016, Intel Corporation. + * + * This driver is also based on BMI160 driver, which is: + * Copyright (c) 2016, Intel Corporation. + * Copyright (c) 2019, Martin Kelly. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "bno055.h" + +#define BNO055_FW_UID_FMT "bno055-caldata-%*phN.dat" +#define BNO055_FW_GENERIC_NAME "bno055-caldata.dat" + +/* common registers */ +#define BNO055_PAGESEL_REG 0x7 + +/* page 0 registers */ +#define BNO055_CHIP_ID_REG 0x0 +#define BNO055_CHIP_ID_MAGIC 0xA0 +#define BNO055_SW_REV_LSB_REG 0x4 +#define BNO055_SW_REV_MSB_REG 0x5 +#define BNO055_ACC_DATA_X_LSB_REG 0x8 +#define BNO055_ACC_DATA_Y_LSB_REG 0xA +#define BNO055_ACC_DATA_Z_LSB_REG 0xC +#define BNO055_MAG_DATA_X_LSB_REG 0xE +#define BNO055_MAG_DATA_Y_LSB_REG 0x10 +#define BNO055_MAG_DATA_Z_LSB_REG 0x12 +#define BNO055_GYR_DATA_X_LSB_REG 0x14 +#define BNO055_GYR_DATA_Y_LSB_REG 0x16 +#define BNO055_GYR_DATA_Z_LSB_REG 0x18 +#define BNO055_EUL_DATA_X_LSB_REG 0x1A +#define BNO055_EUL_DATA_Y_LSB_REG 0x1C +#define BNO055_EUL_DATA_Z_LSB_REG 0x1E +#define BNO055_QUAT_DATA_W_LSB_REG 0x20 +#define BNO055_LIA_DATA_X_LSB_REG 0x28 +#define BNO055_LIA_DATA_Y_LSB_REG 0x2A +#define BNO055_LIA_DATA_Z_LSB_REG 0x2C +#define BNO055_GRAVITY_DATA_X_LSB_REG 0x2E +#define BNO055_GRAVITY_DATA_Y_LSB_REG 0x30 +#define BNO055_GRAVITY_DATA_Z_LSB_REG 0x32 +#define BNO055_SCAN_CH_COUNT ((BNO055_GRAVITY_DATA_Z_LSB_REG - BNO055_ACC_DATA_X_LSB_REG) / 2) +#define BNO055_TEMP_REG 0x34 +#define BNO055_CALIB_STAT_REG 0x35 +#define BNO055_CALIB_STAT_MAGN_SHIFT 0 +#define BNO055_CALIB_STAT_ACCEL_SHIFT 2 +#define BNO055_CALIB_STAT_GYRO_SHIFT 4 +#define BNO055_CALIB_STAT_SYS_SHIFT 6 +#define BNO055_SYS_ERR_REG 0x3A +#define BNO055_POWER_MODE_REG 0x3E +#define BNO055_POWER_MODE_NORMAL 0 +#define BNO055_SYS_TRIGGER_REG 0x3F +#define BNO055_SYS_TRIGGER_RST_SYS BIT(5) +#define BNO055_SYS_TRIGGER_CLK_SEL BIT(7) +#define BNO055_OPR_MODE_REG 0x3D +#define BNO055_OPR_MODE_CONFIG 0x0 +#define BNO055_OPR_MODE_AMG 0x7 +#define BNO055_OPR_MODE_FUSION_FMC_OFF 0xB +#define BNO055_OPR_MODE_FUSION 0xC +#define BNO055_UNIT_SEL_REG 0x3B +/* Android orientation mode means: pitch value decreases turning clockwise */ +#define BNO055_UNIT_SEL_ANDROID BIT(7) +#define BNO055_UNIT_SEL_GYR_RPS BIT(1) +#define BNO055_CALDATA_START 0x55 +#define BNO055_CALDATA_END 0x6A +#define BNO055_CALDATA_LEN 22 + +/* + * The difference in address between the register that contains the + * value and the register that contains the offset. This applies for + * accel, gyro and magn channels. + */ +#define BNO055_REG_OFFSET_ADDR 0x4D + +/* page 1 registers */ +#define BNO055_PG1(x) ((x) | 0x80) +#define BNO055_ACC_CONFIG_REG BNO055_PG1(0x8) +#define BNO055_ACC_CONFIG_LPF_MASK GENMASK(4, 2) +#define BNO055_ACC_CONFIG_RANGE_MASK GENMASK(1, 0) +#define BNO055_MAG_CONFIG_REG BNO055_PG1(0x9) +#define BNO055_MAG_CONFIG_HIGHACCURACY 0x18 +#define BNO055_MAG_CONFIG_ODR_MASK GENMASK(2, 0) +#define BNO055_GYR_CONFIG_REG BNO055_PG1(0xA) +#define BNO055_GYR_CONFIG_RANGE_MASK GENMASK(2, 0) +#define BNO055_GYR_CONFIG_LPF_MASK GENMASK(5, 3) +#define BNO055_GYR_AM_SET_REG BNO055_PG1(0x1F) +#define BNO055_UID_LOWER_REG BNO055_PG1(0x50) +#define BNO055_UID_HIGHER_REG BNO055_PG1(0x5F) +#define BNO055_UID_LEN 16 + +struct bno055_sysfs_attr { + int *vals; + int len; + int *fusion_vals; + int *hw_xlate; + int type; +}; + +static int bno055_acc_lpf_vals[] = { + 7, 810000, 15, 630000, 31, 250000, 62, 500000, + 125, 0, 250, 0, 500, 0, 1000, 0, +}; + +static struct bno055_sysfs_attr bno055_acc_lpf = { + .vals = bno055_acc_lpf_vals, + .len = ARRAY_SIZE(bno055_acc_lpf_vals), + .fusion_vals = (int[]){62, 500000}, + .type = IIO_VAL_INT_PLUS_MICRO, +}; + +static int bno055_acc_range_vals[] = { + /* G: 2, 4, 8, 16 */ + 1962, 3924, 7848, 15696 +}; + +static struct bno055_sysfs_attr bno055_acc_range = { + .vals = bno055_acc_range_vals, + .len = ARRAY_SIZE(bno055_acc_range_vals), + .fusion_vals = (int[]){3924}, /* 4G */ + .type = IIO_VAL_INT, +}; + +/* + * Theoretically the IMU should return data in a given (i.e. fixed) unit + * regardless of the range setting. This happens for the accelerometer, but not + * for the gyroscope; the gyroscope range setting affects the scale. + * This is probably due to this[0] bug. + * For this reason we map the internal range setting onto the standard IIO scale + * attribute for gyro. + * Since the bug[0] may be fixed in future, we check for the IMU FW version and + * eventually warn the user. + * Currently we just don't care about "range" attributes for gyro. + * + * [0] https://community.bosch-sensortec.com/t5/MEMS-sensors-forum/BNO055-Wrong-sensitivity-resolution-in-datasheet/td-p/10266 + */ + +/* + * dps = hwval * (dps_range/2^15) + * rps = hwval * (rps_range/2^15) + * = hwval * (dps_range/(2^15 * k)) + * where k is rad-to-deg factor + */ +static int bno055_gyr_scale_vals[] = { + 125, 1877467, 250, 1877467, 500, 1877467, + 1000, 1877467, 2000, 1877467, +}; + +static struct bno055_sysfs_attr bno055_gyr_scale = { + .vals = bno055_gyr_scale_vals, + .len = ARRAY_SIZE(bno055_gyr_scale_vals), + .fusion_vals = (int[]){1, 900}, + .hw_xlate = (int[]){4, 3, 2, 1, 0}, + .type = IIO_VAL_FRACTIONAL, +}; + +static int bno055_gyr_lpf_vals[] = {12, 23, 32, 47, 64, 116, 230, 523}; +static struct bno055_sysfs_attr bno055_gyr_lpf = { + .vals = bno055_gyr_lpf_vals, + .len = ARRAY_SIZE(bno055_gyr_lpf_vals), + .fusion_vals = (int[]){32}, + .hw_xlate = (int[]){5, 4, 7, 3, 6, 2, 1, 0}, + .type = IIO_VAL_INT, +}; + +static int bno055_mag_odr_vals[] = {2, 6, 8, 10, 15, 20, 25, 30}; +static struct bno055_sysfs_attr bno055_mag_odr = { + .vals = bno055_mag_odr_vals, + .len = ARRAY_SIZE(bno055_mag_odr_vals), + .fusion_vals = (int[]){20}, + .type = IIO_VAL_INT, +}; + +struct bno055_priv { + struct regmap *regmap; + struct device *dev; + struct clk *clk; + int operation_mode; + int xfer_burst_break_thr; + struct mutex lock; + u8 uid[BNO055_UID_LEN]; + struct gpio_desc *reset_gpio; + bool sw_reset; + struct { + __le16 chans[BNO055_SCAN_CH_COUNT]; + s64 timestamp __aligned(8); + } buf; + struct dentry *debugfs; +}; + +static bool bno055_regmap_volatile(struct device *dev, unsigned int reg) +{ + /* data and status registers */ + if (reg >= BNO055_ACC_DATA_X_LSB_REG && reg <= BNO055_SYS_ERR_REG) + return true; + + /* when in fusion mode, config is updated by chip */ + if (reg == BNO055_MAG_CONFIG_REG || + reg == BNO055_ACC_CONFIG_REG || + reg == BNO055_GYR_CONFIG_REG) + return true; + + /* calibration data may be updated by the IMU */ + if (reg >= BNO055_CALDATA_START && reg <= BNO055_CALDATA_END) + return true; + + return false; +} + +static bool bno055_regmap_readable(struct device *dev, unsigned int reg) +{ + /* unnamed PG0 reserved areas */ + if ((reg < BNO055_PG1(0) && reg > BNO055_CALDATA_END) || + reg == 0x3C) + return false; + + /* unnamed PG1 reserved areas */ + if (reg > BNO055_PG1(BNO055_UID_HIGHER_REG) || + (reg < BNO055_PG1(BNO055_UID_LOWER_REG) && reg > BNO055_PG1(BNO055_GYR_AM_SET_REG)) || + reg == BNO055_PG1(0xE) || + (reg < BNO055_PG1(BNO055_PAGESEL_REG) && reg >= BNO055_PG1(0x0))) + return false; + return true; +} + +static bool bno055_regmap_writeable(struct device *dev, unsigned int reg) +{ + /* + * Unreadable registers are indeed reserved; there are no WO regs + * (except for a single bit in SYS_TRIGGER register) + */ + if (!bno055_regmap_readable(dev, reg)) + return false; + + /* data and status registers */ + if (reg >= BNO055_ACC_DATA_X_LSB_REG && reg <= BNO055_SYS_ERR_REG) + return false; + + /* ID areas */ + if (reg < BNO055_PAGESEL_REG || + (reg <= BNO055_UID_HIGHER_REG && reg >= BNO055_UID_LOWER_REG)) + return false; + + return true; +} + +static const struct regmap_range_cfg bno055_regmap_ranges[] = { + { + .range_min = 0, + .range_max = 0x7f * 2, + .selector_reg = BNO055_PAGESEL_REG, + .selector_mask = GENMASK(7, 0), + .selector_shift = 0, + .window_start = 0, + .window_len = 0x80, + }, +}; + +const struct regmap_config bno055_regmap_config = { + .name = "bno055", + .reg_bits = 8, + .val_bits = 8, + .ranges = bno055_regmap_ranges, + .num_ranges = 1, + .volatile_reg = bno055_regmap_volatile, + .max_register = 0x80 * 2, + .writeable_reg = bno055_regmap_writeable, + .readable_reg = bno055_regmap_readable, + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_NS_GPL(bno055_regmap_config, IIO_BNO055); + +/* must be called in configuration mode */ +static int bno055_calibration_load(struct bno055_priv *priv, const u8 *data, int len) +{ + if (len != BNO055_CALDATA_LEN) { + dev_dbg(priv->dev, "Invalid calibration file size %d (expected %d)", + len, BNO055_CALDATA_LEN); + return -EINVAL; + } + + dev_dbg(priv->dev, "loading cal data: %*ph", BNO055_CALDATA_LEN, data); + return regmap_bulk_write(priv->regmap, BNO055_CALDATA_START, + data, BNO055_CALDATA_LEN); +} + +static int bno055_operation_mode_do_set(struct bno055_priv *priv, + int operation_mode) +{ + int ret; + + ret = regmap_write(priv->regmap, BNO055_OPR_MODE_REG, + operation_mode); + if (ret) + return ret; + + /* Following datasheet specifications: sensor takes 7mS up to 19 mS to switch mode */ + msleep(20); + + return 0; +} + +static int bno055_system_reset(struct bno055_priv *priv) +{ + int ret; + + if (priv->reset_gpio) { + gpiod_set_value_cansleep(priv->reset_gpio, 0); + usleep_range(5000, 10000); + gpiod_set_value_cansleep(priv->reset_gpio, 1); + } else if (priv->sw_reset) { + ret = regmap_write(priv->regmap, BNO055_SYS_TRIGGER_REG, + BNO055_SYS_TRIGGER_RST_SYS); + if (ret) + return ret; + } else { + return 0; + } + + regcache_drop_region(priv->regmap, 0x0, 0xff); + usleep_range(650000, 700000); + + return 0; +} + +static int bno055_init(struct bno055_priv *priv, const u8 *caldata, int len) +{ + int ret; + + ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG); + if (ret) + return ret; + + ret = regmap_write(priv->regmap, BNO055_POWER_MODE_REG, + BNO055_POWER_MODE_NORMAL); + if (ret) + return ret; + + ret = regmap_write(priv->regmap, BNO055_SYS_TRIGGER_REG, + priv->clk ? BNO055_SYS_TRIGGER_CLK_SEL : 0); + if (ret) + return ret; + + /* use standard SI units */ + ret = regmap_write(priv->regmap, BNO055_UNIT_SEL_REG, + BNO055_UNIT_SEL_ANDROID | BNO055_UNIT_SEL_GYR_RPS); + if (ret) + return ret; + + if (caldata) { + ret = bno055_calibration_load(priv, caldata, len); + if (ret) + dev_warn(priv->dev, "failed to load calibration data with error %d\n", + ret); + } + + return 0; +} + +static ssize_t bno055_operation_mode_set(struct bno055_priv *priv, + int operation_mode) +{ + u8 caldata[BNO055_CALDATA_LEN]; + int ret; + + mutex_lock(&priv->lock); + + ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG); + if (ret) + goto exit_unlock; + + if (operation_mode == BNO055_OPR_MODE_FUSION || + operation_mode == BNO055_OPR_MODE_FUSION_FMC_OFF) { + /* for entering fusion mode, reset the chip to clear the algo state */ + ret = regmap_bulk_read(priv->regmap, BNO055_CALDATA_START, caldata, + BNO055_CALDATA_LEN); + if (ret) + goto exit_unlock; + + ret = bno055_system_reset(priv); + if (ret) + goto exit_unlock; + + ret = bno055_init(priv, caldata, BNO055_CALDATA_LEN); + if (ret) + goto exit_unlock; + } + + ret = bno055_operation_mode_do_set(priv, operation_mode); + if (ret) + goto exit_unlock; + + priv->operation_mode = operation_mode; + +exit_unlock: + mutex_unlock(&priv->lock); + return ret; +} + +static void bno055_uninit(void *arg) +{ + struct bno055_priv *priv = arg; + + /* stop the IMU */ + bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG); +} + +#define BNO055_CHANNEL(_type, _axis, _index, _address, _sep, _sh, _avail) { \ + .address = _address, \ + .type = _type, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | (_sep), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | (_sh), \ + .info_mask_shared_by_type_available = _avail, \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + .repeat = IIO_MOD_##_axis == IIO_MOD_QUATERNION ? 4 : 0, \ + }, \ +} + +/* scan indexes follow DATA register order */ +enum bno055_scan_axis { + BNO055_SCAN_ACCEL_X, + BNO055_SCAN_ACCEL_Y, + BNO055_SCAN_ACCEL_Z, + BNO055_SCAN_MAGN_X, + BNO055_SCAN_MAGN_Y, + BNO055_SCAN_MAGN_Z, + BNO055_SCAN_GYRO_X, + BNO055_SCAN_GYRO_Y, + BNO055_SCAN_GYRO_Z, + BNO055_SCAN_YAW, + BNO055_SCAN_ROLL, + BNO055_SCAN_PITCH, + BNO055_SCAN_QUATERNION, + BNO055_SCAN_LIA_X, + BNO055_SCAN_LIA_Y, + BNO055_SCAN_LIA_Z, + BNO055_SCAN_GRAVITY_X, + BNO055_SCAN_GRAVITY_Y, + BNO055_SCAN_GRAVITY_Z, + BNO055_SCAN_TIMESTAMP, + _BNO055_SCAN_MAX +}; + +static const struct iio_chan_spec bno055_channels[] = { + /* accelerometer */ + BNO055_CHANNEL(IIO_ACCEL, X, BNO055_SCAN_ACCEL_X, + BNO055_ACC_DATA_X_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY)), + BNO055_CHANNEL(IIO_ACCEL, Y, BNO055_SCAN_ACCEL_Y, + BNO055_ACC_DATA_Y_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY)), + BNO055_CHANNEL(IIO_ACCEL, Z, BNO055_SCAN_ACCEL_Z, + BNO055_ACC_DATA_Z_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY)), + /* gyroscope */ + BNO055_CHANNEL(IIO_ANGL_VEL, X, BNO055_SCAN_GYRO_X, + BNO055_GYR_DATA_X_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | + BIT(IIO_CHAN_INFO_SCALE)), + BNO055_CHANNEL(IIO_ANGL_VEL, Y, BNO055_SCAN_GYRO_Y, + BNO055_GYR_DATA_Y_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | + BIT(IIO_CHAN_INFO_SCALE)), + BNO055_CHANNEL(IIO_ANGL_VEL, Z, BNO055_SCAN_GYRO_Z, + BNO055_GYR_DATA_Z_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | + BIT(IIO_CHAN_INFO_SCALE)), + /* magnetometer */ + BNO055_CHANNEL(IIO_MAGN, X, BNO055_SCAN_MAGN_X, + BNO055_MAG_DATA_X_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), + BIT(IIO_CHAN_INFO_SAMP_FREQ), BIT(IIO_CHAN_INFO_SAMP_FREQ)), + BNO055_CHANNEL(IIO_MAGN, Y, BNO055_SCAN_MAGN_Y, + BNO055_MAG_DATA_Y_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), + BIT(IIO_CHAN_INFO_SAMP_FREQ), BIT(IIO_CHAN_INFO_SAMP_FREQ)), + BNO055_CHANNEL(IIO_MAGN, Z, BNO055_SCAN_MAGN_Z, + BNO055_MAG_DATA_Z_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET), + BIT(IIO_CHAN_INFO_SAMP_FREQ), BIT(IIO_CHAN_INFO_SAMP_FREQ)), + /* euler angle */ + BNO055_CHANNEL(IIO_ROT, YAW, BNO055_SCAN_YAW, + BNO055_EUL_DATA_X_LSB_REG, 0, 0, 0), + BNO055_CHANNEL(IIO_ROT, ROLL, BNO055_SCAN_ROLL, + BNO055_EUL_DATA_Y_LSB_REG, 0, 0, 0), + BNO055_CHANNEL(IIO_ROT, PITCH, BNO055_SCAN_PITCH, + BNO055_EUL_DATA_Z_LSB_REG, 0, 0, 0), + /* quaternion */ + BNO055_CHANNEL(IIO_ROT, QUATERNION, BNO055_SCAN_QUATERNION, + BNO055_QUAT_DATA_W_LSB_REG, 0, 0, 0), + + /* linear acceleration */ + BNO055_CHANNEL(IIO_ACCEL, LINEAR_X, BNO055_SCAN_LIA_X, + BNO055_LIA_DATA_X_LSB_REG, 0, 0, 0), + BNO055_CHANNEL(IIO_ACCEL, LINEAR_Y, BNO055_SCAN_LIA_Y, + BNO055_LIA_DATA_Y_LSB_REG, 0, 0, 0), + BNO055_CHANNEL(IIO_ACCEL, LINEAR_Z, BNO055_SCAN_LIA_Z, + BNO055_LIA_DATA_Z_LSB_REG, 0, 0, 0), + + /* gravity vector */ + BNO055_CHANNEL(IIO_GRAVITY, X, BNO055_SCAN_GRAVITY_X, + BNO055_GRAVITY_DATA_X_LSB_REG, 0, 0, 0), + BNO055_CHANNEL(IIO_GRAVITY, Y, BNO055_SCAN_GRAVITY_Y, + BNO055_GRAVITY_DATA_Y_LSB_REG, 0, 0, 0), + BNO055_CHANNEL(IIO_GRAVITY, Z, BNO055_SCAN_GRAVITY_Z, + BNO055_GRAVITY_DATA_Z_LSB_REG, 0, 0, 0), + + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .scan_index = -1, + }, + IIO_CHAN_SOFT_TIMESTAMP(BNO055_SCAN_TIMESTAMP), +}; + +static int bno055_get_regmask(struct bno055_priv *priv, int *val, int *val2, + int reg, int mask, struct bno055_sysfs_attr *attr) +{ + const int shift = __ffs(mask); + int hwval, idx; + int ret; + int i; + + ret = regmap_read(priv->regmap, reg, &hwval); + if (ret) + return ret; + + idx = (hwval & mask) >> shift; + if (attr->hw_xlate) + for (i = 0; i < attr->len; i++) + if (attr->hw_xlate[i] == idx) { + idx = i; + break; + } + if (attr->type == IIO_VAL_INT) { + *val = attr->vals[idx]; + } else { /* IIO_VAL_INT_PLUS_MICRO or IIO_VAL_FRACTIONAL */ + *val = attr->vals[idx * 2]; + *val2 = attr->vals[idx * 2 + 1]; + } + + return attr->type; +} + +static int bno055_set_regmask(struct bno055_priv *priv, int val, int val2, + int reg, int mask, struct bno055_sysfs_attr *attr) +{ + const int shift = __ffs(mask); + int best_delta; + int req_val; + int tbl_val; + bool first; + int delta; + int hwval; + int ret; + int len; + int i; + + /* + * The closest value the HW supports is only one in fusion mode, + * and it is autoselected, so don't do anything, just return OK, + * as the closest possible value has been (virtually) selected + */ + if (priv->operation_mode != BNO055_OPR_MODE_AMG) + return 0; + + len = attr->len; + + /* + * We always get a request in INT_PLUS_MICRO, but we + * take care of the micro part only when we really have + * non-integer tables. This prevents 32-bit overflow with + * larger integers contained in integer tables. + */ + req_val = val; + if (attr->type != IIO_VAL_INT) { + len /= 2; + req_val = min(val, 2147) * 1000000 + val2; + } + + first = true; + for (i = 0; i < len; i++) { + switch (attr->type) { + case IIO_VAL_INT: + tbl_val = attr->vals[i]; + break; + case IIO_VAL_INT_PLUS_MICRO: + WARN_ON(attr->vals[i * 2] > 2147); + tbl_val = attr->vals[i * 2] * 1000000 + + attr->vals[i * 2 + 1]; + break; + case IIO_VAL_FRACTIONAL: + WARN_ON(attr->vals[i * 2] > 4294); + tbl_val = attr->vals[i * 2] * 1000000 / + attr->vals[i * 2 + 1]; + break; + default: + return -EINVAL; + } + delta = abs(tbl_val - req_val); + if (delta < best_delta || first) { + best_delta = delta; + hwval = i; + first = false; + } + } + + if (attr->hw_xlate) + hwval = attr->hw_xlate[hwval]; + + ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG); + if (ret) + return ret; + + ret = regmap_update_bits(priv->regmap, reg, mask, hwval << shift); + if (ret) + return ret; + + return bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_AMG); +} + +static int bno055_read_simple_chan(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct bno055_priv *priv = iio_priv(indio_dev); + __le16 raw_val; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = regmap_bulk_read(priv->regmap, chan->address, + &raw_val, sizeof(raw_val)); + if (ret < 0) + return ret; + *val = sign_extend32(le16_to_cpu(raw_val), 15); + return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + if (priv->operation_mode != BNO055_OPR_MODE_AMG) { + *val = 0; + } else { + ret = regmap_bulk_read(priv->regmap, + chan->address + + BNO055_REG_OFFSET_ADDR, + &raw_val, sizeof(raw_val)); + if (ret < 0) + return ret; + /* + * IMU reports sensor offsets; IIO wants correction + * offsets, thus we need the 'minus' here. + */ + *val = -sign_extend32(le16_to_cpu(raw_val), 15); + } + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 1; + switch (chan->type) { + case IIO_GRAVITY: + /* Table 3-35: 1 m/s^2 = 100 LSB */ + case IIO_ACCEL: + /* Table 3-17: 1 m/s^2 = 100 LSB */ + *val2 = 100; + break; + case IIO_MAGN: + /* + * Table 3-19: 1 uT = 16 LSB. But we need + * Gauss: 1G = 0.1 uT. + */ + *val2 = 160; + break; + case IIO_ANGL_VEL: + /* + * Table 3-22: 1 Rps = 900 LSB + * .. but this is not exactly true. See comment at the + * beginning of this file. + */ + if (priv->operation_mode != BNO055_OPR_MODE_AMG) { + *val = bno055_gyr_scale.fusion_vals[0]; + *val2 = bno055_gyr_scale.fusion_vals[1]; + return IIO_VAL_FRACTIONAL; + } + + return bno055_get_regmask(priv, val, val2, + BNO055_GYR_CONFIG_REG, + BNO055_GYR_CONFIG_RANGE_MASK, + &bno055_gyr_scale); + break; + case IIO_ROT: + /* Table 3-28: 1 degree = 16 LSB */ + *val2 = 16; + break; + default: + return -EINVAL; + } + return IIO_VAL_FRACTIONAL; + + case IIO_CHAN_INFO_SAMP_FREQ: + if (chan->type != IIO_MAGN) + return -EINVAL; + + return bno055_get_regmask(priv, val, val2, + BNO055_MAG_CONFIG_REG, + BNO055_MAG_CONFIG_ODR_MASK, + &bno055_mag_odr); + + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + switch (chan->type) { + case IIO_ANGL_VEL: + return bno055_get_regmask(priv, val, val2, + BNO055_GYR_CONFIG_REG, + BNO055_GYR_CONFIG_LPF_MASK, + &bno055_gyr_lpf); + case IIO_ACCEL: + return bno055_get_regmask(priv, val, val2, + BNO055_ACC_CONFIG_REG, + BNO055_ACC_CONFIG_LPF_MASK, + &bno055_acc_lpf); + default: + return -EINVAL; + } + + default: + return -EINVAL; + } +} + +static int bno055_sysfs_attr_avail(struct bno055_priv *priv, struct bno055_sysfs_attr *attr, + const int **vals, int *length) +{ + if (priv->operation_mode != BNO055_OPR_MODE_AMG) { + /* locked when fusion enabled */ + *vals = attr->fusion_vals; + if (attr->type == IIO_VAL_INT) + *length = 1; + else + *length = 2; /* IIO_VAL_INT_PLUS_MICRO or IIO_VAL_FRACTIONAL*/ + } else { + *vals = attr->vals; + *length = attr->len; + } + + return attr->type; +} + +static int bno055_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct bno055_priv *priv = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_ANGL_VEL: + *type = bno055_sysfs_attr_avail(priv, &bno055_gyr_scale, + vals, length); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + switch (chan->type) { + case IIO_ANGL_VEL: + *type = bno055_sysfs_attr_avail(priv, &bno055_gyr_lpf, + vals, length); + return IIO_AVAIL_LIST; + case IIO_ACCEL: + *type = bno055_sysfs_attr_avail(priv, &bno055_acc_lpf, + vals, length); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + + break; + case IIO_CHAN_INFO_SAMP_FREQ: + switch (chan->type) { + case IIO_MAGN: + *type = bno055_sysfs_attr_avail(priv, &bno055_mag_odr, + vals, length); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int bno055_read_temp_chan(struct iio_dev *indio_dev, int *val) +{ + struct bno055_priv *priv = iio_priv(indio_dev); + unsigned int raw_val; + int ret; + + ret = regmap_read(priv->regmap, BNO055_TEMP_REG, &raw_val); + if (ret < 0) + return ret; + + /* + * Tables 3-36 and 3-37: one byte of priv, signed, 1 LSB = 1C. + * ABI wants milliC. + */ + *val = raw_val * 1000; + + return IIO_VAL_INT; +} + +static int bno055_read_quaternion(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int size, int *vals, int *val_len, + long mask) +{ + struct bno055_priv *priv = iio_priv(indio_dev); + __le16 raw_vals[4]; + int i, ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (size < 4) + return -EINVAL; + ret = regmap_bulk_read(priv->regmap, + BNO055_QUAT_DATA_W_LSB_REG, + raw_vals, sizeof(raw_vals)); + if (ret < 0) + return ret; + for (i = 0; i < 4; i++) + vals[i] = sign_extend32(le16_to_cpu(raw_vals[i]), 15); + *val_len = 4; + return IIO_VAL_INT_MULTIPLE; + case IIO_CHAN_INFO_SCALE: + /* Table 3-31: 1 quaternion = 2^14 LSB */ + if (size < 2) + return -EINVAL; + vals[0] = 1; + vals[1] = 14; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static bool bno055_is_chan_readable(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + struct bno055_priv *priv = iio_priv(indio_dev); + + if (priv->operation_mode != BNO055_OPR_MODE_AMG) + return true; + + switch (chan->type) { + case IIO_GRAVITY: + case IIO_ROT: + return false; + case IIO_ACCEL: + if (chan->channel2 == IIO_MOD_LINEAR_X || + chan->channel2 == IIO_MOD_LINEAR_Y || + chan->channel2 == IIO_MOD_LINEAR_Z) + return false; + return true; + default: + return true; + } +} + +static int _bno055_read_raw_multi(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int size, int *vals, int *val_len, + long mask) +{ + if (!bno055_is_chan_readable(indio_dev, chan)) + return -EBUSY; + + switch (chan->type) { + case IIO_MAGN: + case IIO_ACCEL: + case IIO_ANGL_VEL: + case IIO_GRAVITY: + if (size < 2) + return -EINVAL; + *val_len = 2; + return bno055_read_simple_chan(indio_dev, chan, + &vals[0], &vals[1], + mask); + case IIO_TEMP: + *val_len = 1; + return bno055_read_temp_chan(indio_dev, &vals[0]); + case IIO_ROT: + /* + * Rotation is exposed as either a quaternion or three + * Euler angles. + */ + if (chan->channel2 == IIO_MOD_QUATERNION) + return bno055_read_quaternion(indio_dev, chan, + size, vals, + val_len, mask); + if (size < 2) + return -EINVAL; + *val_len = 2; + return bno055_read_simple_chan(indio_dev, chan, + &vals[0], &vals[1], + mask); + default: + return -EINVAL; + } +} + +static int bno055_read_raw_multi(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int size, int *vals, int *val_len, + long mask) +{ + struct bno055_priv *priv = iio_priv(indio_dev); + int ret; + + mutex_lock(&priv->lock); + ret = _bno055_read_raw_multi(indio_dev, chan, size, + vals, val_len, mask); + mutex_unlock(&priv->lock); + return ret; +} + +static int _bno055_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct bno055_priv *priv = iio_priv(iio_dev); + + switch (chan->type) { + case IIO_MAGN: + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + return bno055_set_regmask(priv, val, val2, + BNO055_MAG_CONFIG_REG, + BNO055_MAG_CONFIG_ODR_MASK, + &bno055_mag_odr); + default: + return -EINVAL; + } + case IIO_ACCEL: + switch (mask) { + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + return bno055_set_regmask(priv, val, val2, + BNO055_ACC_CONFIG_REG, + BNO055_ACC_CONFIG_LPF_MASK, + &bno055_acc_lpf); + + default: + return -EINVAL; + } + case IIO_ANGL_VEL: + switch (mask) { + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + return bno055_set_regmask(priv, val, val2, + BNO055_GYR_CONFIG_REG, + BNO055_GYR_CONFIG_LPF_MASK, + &bno055_gyr_lpf); + case IIO_CHAN_INFO_SCALE: + return bno055_set_regmask(priv, val, val2, + BNO055_GYR_CONFIG_REG, + BNO055_GYR_CONFIG_RANGE_MASK, + &bno055_gyr_scale); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int bno055_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct bno055_priv *priv = iio_priv(iio_dev); + int ret; + + mutex_lock(&priv->lock); + ret = _bno055_write_raw(iio_dev, chan, val, val2, mask); + mutex_unlock(&priv->lock); + + return ret; +} + +static ssize_t in_accel_range_raw_available_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev)); + int len = 0; + int i; + + if (priv->operation_mode != BNO055_OPR_MODE_AMG) + return sysfs_emit(buf, "%d\n", bno055_acc_range.fusion_vals[0]); + + for (i = 0; i < bno055_acc_range.len; i++) + len += sysfs_emit_at(buf, len, "%d ", bno055_acc_range.vals[i]); + buf[len - 1] = '\n'; + + return len; +} + +static ssize_t fusion_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev)); + + return sysfs_emit(buf, "%d\n", + priv->operation_mode != BNO055_OPR_MODE_AMG); +} + +static ssize_t fusion_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 bno055_priv *priv = iio_priv(indio_dev); + bool en; + int ret; + + if (indio_dev->active_scan_mask && + !bitmap_empty(indio_dev->active_scan_mask, _BNO055_SCAN_MAX)) + return -EBUSY; + + ret = kstrtobool(buf, &en); + if (ret) + return -EINVAL; + + if (!en) + return bno055_operation_mode_set(priv, BNO055_OPR_MODE_AMG) ?: len; + + /* + * Coming from AMG means the FMC was off, just switch to fusion but + * don't change anything that doesn't belong to us (i.e let FMC stay off). + * Coming from any other fusion mode means we don't need to do anything. + */ + if (priv->operation_mode == BNO055_OPR_MODE_AMG) + return bno055_operation_mode_set(priv, BNO055_OPR_MODE_FUSION_FMC_OFF) ?: len; + + return len; +} + +static ssize_t in_magn_calibration_fast_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev)); + + return sysfs_emit(buf, "%d\n", + priv->operation_mode == BNO055_OPR_MODE_FUSION); +} + +static ssize_t in_magn_calibration_fast_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 bno055_priv *priv = iio_priv(indio_dev); + int ret; + + if (indio_dev->active_scan_mask && + !bitmap_empty(indio_dev->active_scan_mask, _BNO055_SCAN_MAX)) + return -EBUSY; + + if (sysfs_streq(buf, "0")) { + if (priv->operation_mode == BNO055_OPR_MODE_FUSION) { + ret = bno055_operation_mode_set(priv, BNO055_OPR_MODE_FUSION_FMC_OFF); + if (ret) + return ret; + } + } else { + if (priv->operation_mode == BNO055_OPR_MODE_AMG) + return -EINVAL; + + if (priv->operation_mode != BNO055_OPR_MODE_FUSION) { + ret = bno055_operation_mode_set(priv, BNO055_OPR_MODE_FUSION); + if (ret) + return ret; + } + } + + return len; +} + +static ssize_t in_accel_range_raw_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev)); + int val; + int ret; + + ret = bno055_get_regmask(priv, &val, NULL, + BNO055_ACC_CONFIG_REG, + BNO055_ACC_CONFIG_RANGE_MASK, + &bno055_acc_range); + if (ret < 0) + return ret; + + return sysfs_emit(buf, "%d\n", val); +} + +static ssize_t in_accel_range_raw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev)); + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + mutex_lock(&priv->lock); + ret = bno055_set_regmask(priv, val, 0, + BNO055_ACC_CONFIG_REG, + BNO055_ACC_CONFIG_RANGE_MASK, + &bno055_acc_range); + mutex_unlock(&priv->lock); + + return ret ?: len; +} + +static ssize_t bno055_get_calib_status(struct device *dev, char *buf, int which) +{ + struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev)); + int calib; + int ret; + int val; + + if (priv->operation_mode == BNO055_OPR_MODE_AMG || + (priv->operation_mode == BNO055_OPR_MODE_FUSION_FMC_OFF && + which == BNO055_CALIB_STAT_MAGN_SHIFT)) { + calib = 0; + } else { + mutex_lock(&priv->lock); + ret = regmap_read(priv->regmap, BNO055_CALIB_STAT_REG, &val); + mutex_unlock(&priv->lock); + + if (ret) + return -EIO; + + calib = ((val >> which) & GENMASK(1, 0)) + 1; + } + + return sysfs_emit(buf, "%d\n", calib); +} + +static ssize_t serialnumber_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev)); + + return sysfs_emit(buf, "%*ph\n", BNO055_UID_LEN, priv->uid); +} + +static ssize_t calibration_data_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t pos, size_t count) +{ + struct bno055_priv *priv = iio_priv(dev_to_iio_dev(kobj_to_dev(kobj))); + u8 data[BNO055_CALDATA_LEN]; + int ret; + + /* + * Calibration data is volatile; reading it in chunks will possibly + * results in inconsistent data. We require the user to read the whole + * blob in a single chunk + */ + if (count < BNO055_CALDATA_LEN || pos) + return -EINVAL; + + mutex_lock(&priv->lock); + ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG); + if (ret) + goto exit_unlock; + + ret = regmap_bulk_read(priv->regmap, BNO055_CALDATA_START, data, + BNO055_CALDATA_LEN); + if (ret) + goto exit_unlock; + + ret = bno055_operation_mode_do_set(priv, priv->operation_mode); + if (ret) + goto exit_unlock; + + memcpy(buf, data, BNO055_CALDATA_LEN); + + ret = BNO055_CALDATA_LEN; +exit_unlock: + mutex_unlock(&priv->lock); + return ret; +} + +static ssize_t sys_calibration_auto_status_show(struct device *dev, + struct device_attribute *a, + char *buf) +{ + return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_SYS_SHIFT); +} + +static ssize_t in_accel_calibration_auto_status_show(struct device *dev, + struct device_attribute *a, + char *buf) +{ + return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_ACCEL_SHIFT); +} + +static ssize_t in_gyro_calibration_auto_status_show(struct device *dev, + struct device_attribute *a, + char *buf) +{ + return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_GYRO_SHIFT); +} + +static ssize_t in_magn_calibration_auto_status_show(struct device *dev, + struct device_attribute *a, + char *buf) +{ + return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_MAGN_SHIFT); +} + +static int bno055_debugfs_reg_access(struct iio_dev *iio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct bno055_priv *priv = iio_priv(iio_dev); + + if (readval) + return regmap_read(priv->regmap, reg, readval); + else + return regmap_write(priv->regmap, reg, writeval); +} + +static ssize_t bno055_show_fw_version(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct bno055_priv *priv = file->private_data; + int rev, ver; + char *buf; + int ret; + + ret = regmap_read(priv->regmap, BNO055_SW_REV_LSB_REG, &rev); + if (ret) + return ret; + + ret = regmap_read(priv->regmap, BNO055_SW_REV_MSB_REG, &ver); + if (ret) + return ret; + + buf = kasprintf(GFP_KERNEL, "ver: 0x%x, rev: 0x%x\n", ver, rev); + if (!buf) + return -ENOMEM; + + ret = simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf)); + kfree(buf); + + return ret; +} + +static const struct file_operations bno055_fw_version_ops = { + .open = simple_open, + .read = bno055_show_fw_version, + .llseek = default_llseek, + .owner = THIS_MODULE, +}; + +static void bno055_debugfs_remove(void *_priv) +{ + struct bno055_priv *priv = _priv; + + debugfs_remove(priv->debugfs); + priv->debugfs = NULL; +} + +static void bno055_debugfs_init(struct iio_dev *iio_dev) +{ + struct bno055_priv *priv = iio_priv(iio_dev); + + priv->debugfs = debugfs_create_file("firmware_version", 0400, + iio_get_debugfs_dentry(iio_dev), + priv, &bno055_fw_version_ops); + if (!IS_ERR(priv->debugfs)) + devm_add_action_or_reset(priv->dev, bno055_debugfs_remove, + priv); + if (IS_ERR_OR_NULL(priv->debugfs)) + dev_warn(priv->dev, "failed to setup debugfs"); +} + +static IIO_DEVICE_ATTR_RW(fusion_enable, 0); +static IIO_DEVICE_ATTR_RW(in_magn_calibration_fast_enable, 0); +static IIO_DEVICE_ATTR_RW(in_accel_range_raw, 0); + +static IIO_DEVICE_ATTR_RO(in_accel_range_raw_available, 0); +static IIO_DEVICE_ATTR_RO(sys_calibration_auto_status, 0); +static IIO_DEVICE_ATTR_RO(in_accel_calibration_auto_status, 0); +static IIO_DEVICE_ATTR_RO(in_gyro_calibration_auto_status, 0); +static IIO_DEVICE_ATTR_RO(in_magn_calibration_auto_status, 0); +static IIO_DEVICE_ATTR_RO(serialnumber, 0); + +static struct attribute *bno055_attrs[] = { + &iio_dev_attr_in_accel_range_raw_available.dev_attr.attr, + &iio_dev_attr_in_accel_range_raw.dev_attr.attr, + &iio_dev_attr_fusion_enable.dev_attr.attr, + &iio_dev_attr_in_magn_calibration_fast_enable.dev_attr.attr, + &iio_dev_attr_sys_calibration_auto_status.dev_attr.attr, + &iio_dev_attr_in_accel_calibration_auto_status.dev_attr.attr, + &iio_dev_attr_in_gyro_calibration_auto_status.dev_attr.attr, + &iio_dev_attr_in_magn_calibration_auto_status.dev_attr.attr, + &iio_dev_attr_serialnumber.dev_attr.attr, + NULL +}; + +static BIN_ATTR_RO(calibration_data, BNO055_CALDATA_LEN); + +static struct bin_attribute *bno055_bin_attrs[] = { + &bin_attr_calibration_data, + NULL +}; + +static const struct attribute_group bno055_attrs_group = { + .attrs = bno055_attrs, + .bin_attrs = bno055_bin_attrs, +}; + +static const struct iio_info bno055_info = { + .read_raw_multi = bno055_read_raw_multi, + .read_avail = bno055_read_avail, + .write_raw = bno055_write_raw, + .attrs = &bno055_attrs_group, + .debugfs_reg_access = bno055_debugfs_reg_access, +}; + +/* + * Reads len samples from the HW, stores them in buf starting from buf_idx, + * and applies mask to cull (skip) unneeded samples. + * Updates buf_idx incrementing with the number of stored samples. + * Samples from HW are transferred into buf, then in-place copy on buf is + * performed in order to cull samples that need to be skipped. + * This avoids copies of the first samples until we hit the 1st sample to skip, + * and also avoids having an extra bounce buffer. + * buf must be able to contain len elements in spite of how many samples we are + * going to cull. + */ +static int bno055_scan_xfer(struct bno055_priv *priv, + int start_ch, int len, unsigned long mask, + __le16 *buf, int *buf_idx) +{ + const int base = BNO055_ACC_DATA_X_LSB_REG; + bool quat_in_read = false; + int buf_base = *buf_idx; + __le16 *dst, *src; + int offs_fixup = 0; + int xfer_len = len; + int ret; + int i, n; + + if (!mask) + return 0; + + /* + * All channels are made up 1 16-bit sample, except for quaternion that + * is made up 4 16-bit values. + * For us the quaternion CH is just like 4 regular CHs. + * If our read starts past the quaternion make sure to adjust the + * starting offset; if the quaternion is contained in our scan then make + * sure to adjust the read len. + */ + if (start_ch > BNO055_SCAN_QUATERNION) { + start_ch += 3; + } else if ((start_ch <= BNO055_SCAN_QUATERNION) && + ((start_ch + len) > BNO055_SCAN_QUATERNION)) { + quat_in_read = true; + xfer_len += 3; + } + + ret = regmap_bulk_read(priv->regmap, + base + start_ch * sizeof(__le16), + buf + buf_base, + xfer_len * sizeof(__le16)); + if (ret) + return ret; + + for_each_set_bit(i, &mask, len) { + if (quat_in_read && ((start_ch + i) > BNO055_SCAN_QUATERNION)) + offs_fixup = 3; + + dst = buf + *buf_idx; + src = buf + buf_base + offs_fixup + i; + + n = (start_ch + i == BNO055_SCAN_QUATERNION) ? 4 : 1; + + if (dst != src) + memcpy(dst, src, n * sizeof(__le16)); + + *buf_idx += n; + } + return 0; +} + +static irqreturn_t bno055_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *iio_dev = pf->indio_dev; + struct bno055_priv *priv = iio_priv(iio_dev); + int xfer_start, start, end, prev_end; + unsigned long mask; + int quat_extra_len; + bool first = true; + int buf_idx = 0; + bool thr_hit; + int ret; + + mutex_lock(&priv->lock); + + /* + * Walk the bitmap and eventually perform several transfers. + * Bitmap ones-fields that are separated by gaps <= xfer_burst_break_thr + * will be included in same transfer. + * Every time the bitmap contains a gap wider than xfer_burst_break_thr + * then we split the transfer, skipping the gap. + */ + for_each_set_bitrange(start, end, iio_dev->active_scan_mask, + iio_dev->masklength) { + /* + * First transfer will start from the beginning of the first + * ones-field in the bitmap + */ + if (first) { + xfer_start = start; + } else { + /* + * We found the next ones-field; check whether to + * include it in * the current transfer or not (i.e. + * let's perform the current * transfer and prepare for + * another one). + */ + + /* + * In case the zeros-gap contains the quaternion bit, + * then its length is actually 4 words instead of 1 + * (i.e. +3 wrt other channels). + */ + quat_extra_len = ((start > BNO055_SCAN_QUATERNION) && + (prev_end <= BNO055_SCAN_QUATERNION)) ? 3 : 0; + + /* If the gap is wider than xfer_burst_break_thr then.. */ + thr_hit = (start - prev_end + quat_extra_len) > + priv->xfer_burst_break_thr; + + /* + * .. transfer all the data up to the gap. Then set the + * next transfer start index at right after the gap + * (i.e. at the start of this ones-field). + */ + if (thr_hit) { + mask = *iio_dev->active_scan_mask >> xfer_start; + ret = bno055_scan_xfer(priv, xfer_start, + prev_end - xfer_start, + mask, priv->buf.chans, &buf_idx); + if (ret) + goto done; + xfer_start = start; + } + } + first = false; + prev_end = end; + } + + /* + * We finished walking the bitmap; no more gaps to check for. Just + * perform the current transfer. + */ + mask = *iio_dev->active_scan_mask >> xfer_start; + ret = bno055_scan_xfer(priv, xfer_start, + prev_end - xfer_start, + mask, priv->buf.chans, &buf_idx); + + if (!ret) + iio_push_to_buffers_with_timestamp(iio_dev, + &priv->buf, pf->timestamp); +done: + mutex_unlock(&priv->lock); + iio_trigger_notify_done(iio_dev->trig); + return IRQ_HANDLED; +} + +static int bno055_buffer_preenable(struct iio_dev *indio_dev) +{ + struct bno055_priv *priv = iio_priv(indio_dev); + const unsigned long fusion_mask = + BIT(BNO055_SCAN_YAW) | + BIT(BNO055_SCAN_ROLL) | + BIT(BNO055_SCAN_PITCH) | + BIT(BNO055_SCAN_QUATERNION) | + BIT(BNO055_SCAN_LIA_X) | + BIT(BNO055_SCAN_LIA_Y) | + BIT(BNO055_SCAN_LIA_Z) | + BIT(BNO055_SCAN_GRAVITY_X) | + BIT(BNO055_SCAN_GRAVITY_Y) | + BIT(BNO055_SCAN_GRAVITY_Z); + + if (priv->operation_mode == BNO055_OPR_MODE_AMG && + bitmap_intersects(indio_dev->active_scan_mask, &fusion_mask, + _BNO055_SCAN_MAX)) + return -EBUSY; + return 0; +} + +static const struct iio_buffer_setup_ops bno055_buffer_setup_ops = { + .preenable = bno055_buffer_preenable, +}; + +int bno055_probe(struct device *dev, struct regmap *regmap, + int xfer_burst_break_thr, bool sw_reset) +{ + const struct firmware *caldata = NULL; + struct bno055_priv *priv; + struct iio_dev *iio_dev; + char *fw_name_buf; + unsigned int val; + int rev, ver; + int ret; + + iio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!iio_dev) + return -ENOMEM; + + iio_dev->name = "bno055"; + priv = iio_priv(iio_dev); + mutex_init(&priv->lock); + priv->regmap = regmap; + priv->dev = dev; + priv->xfer_burst_break_thr = xfer_burst_break_thr; + priv->sw_reset = sw_reset; + + priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(priv->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), "Failed to get reset GPIO\n"); + + priv->clk = devm_clk_get_optional_enabled(dev, "clk"); + if (IS_ERR(priv->clk)) + return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to get CLK\n"); + + if (priv->reset_gpio) { + usleep_range(5000, 10000); + gpiod_set_value_cansleep(priv->reset_gpio, 1); + usleep_range(650000, 750000); + } else if (!sw_reset) { + dev_warn(dev, "No usable reset method; IMU may be unreliable\n"); + } + + ret = regmap_read(priv->regmap, BNO055_CHIP_ID_REG, &val); + if (ret) + return ret; + + if (val != BNO055_CHIP_ID_MAGIC) + dev_warn(dev, "Unrecognized chip ID 0x%x\n", val); + + /* + * In case we haven't a HW reset pin, we can still reset the chip via + * register write. This is probably nonsense in case we can't even + * communicate with the chip or the chip isn't the one we expect (i.e. + * we don't write to unknown chips), so we perform SW reset only after + * chip magic ID check + */ + if (!priv->reset_gpio) { + ret = bno055_system_reset(priv); + if (ret) + return ret; + } + + ret = regmap_read(priv->regmap, BNO055_SW_REV_LSB_REG, &rev); + if (ret) + return ret; + + ret = regmap_read(priv->regmap, BNO055_SW_REV_MSB_REG, &ver); + if (ret) + return ret; + + /* + * The stock FW version contains a bug (see comment at the beginning of + * this file) that causes the anglvel scale to be changed depending on + * the chip range setting. We workaround this, but we don't know what + * other FW versions might do. + */ + if (ver != 0x3 || rev != 0x11) + dev_warn(dev, "Untested firmware version. Anglvel scale may not work as expected\n"); + + ret = regmap_bulk_read(priv->regmap, BNO055_UID_LOWER_REG, + priv->uid, BNO055_UID_LEN); + if (ret) + return ret; + + /* Sensor calibration data */ + fw_name_buf = kasprintf(GFP_KERNEL, BNO055_FW_UID_FMT, + BNO055_UID_LEN, priv->uid); + if (!fw_name_buf) + return -ENOMEM; + + ret = request_firmware(&caldata, fw_name_buf, dev); + kfree(fw_name_buf); + if (ret) + ret = request_firmware(&caldata, BNO055_FW_GENERIC_NAME, dev); + if (ret) { + dev_notice(dev, "Calibration file load failed. See instruction in kernel Documentation/iio/bno055.rst\n"); + ret = bno055_init(priv, NULL, 0); + } else { + ret = bno055_init(priv, caldata->data, caldata->size); + release_firmware(caldata); + } + if (ret) + return ret; + + priv->operation_mode = BNO055_OPR_MODE_FUSION; + ret = bno055_operation_mode_do_set(priv, priv->operation_mode); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, bno055_uninit, priv); + if (ret) + return ret; + + iio_dev->channels = bno055_channels; + iio_dev->num_channels = ARRAY_SIZE(bno055_channels); + iio_dev->info = &bno055_info; + iio_dev->modes = INDIO_DIRECT_MODE; + + ret = devm_iio_triggered_buffer_setup(dev, iio_dev, + iio_pollfunc_store_time, + bno055_trigger_handler, + &bno055_buffer_setup_ops); + if (ret) + return ret; + + ret = devm_iio_device_register(dev, iio_dev); + if (ret) + return ret; + + bno055_debugfs_init(iio_dev); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(bno055_probe, IIO_BNO055); + +MODULE_AUTHOR("Andrea Merello "); +MODULE_DESCRIPTION("Bosch BNO055 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/imu/bno055/bno055.h b/drivers/iio/imu/bno055/bno055.h new file mode 100644 index 000000000000..64f9fc95cebc --- /dev/null +++ b/drivers/iio/imu/bno055/bno055.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef __BNO055_H__ +#define __BNO055_H__ + +#include +#include + +struct device; +int bno055_probe(struct device *dev, struct regmap *regmap, + int xfer_burst_break_thr, bool sw_reset); +extern const struct regmap_config bno055_regmap_config; + +#endif -- cgit v1.2.3 From 4a1728d27225c4cea6b46143fe0c7ec82ccfce54 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Wed, 7 Sep 2022 15:22:00 +0200 Subject: iio: document bno055 private sysfs attributes Add ABI documentation for bno055 driver private sysfs attributes. Signed-off-by: Andrea Merello Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220907132205.28021-10-andrea.merello@iit.it Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio-bno055 | 81 ++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-bno055 diff --git a/Documentation/ABI/testing/sysfs-bus-iio-bno055 b/Documentation/ABI/testing/sysfs-bus-iio-bno055 new file mode 100644 index 000000000000..f32b1644e986 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-bno055 @@ -0,0 +1,81 @@ +What: /sys/bus/iio/devices/iio:deviceX/in_accel_raw_range +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Raw (unscaled) range for acceleration readings. Unit after + application of scale is m/s^2. Note that this doesn't affects + the scale (which should be used when changing the maximum and + minimum readable value affects also the reading scaling factor). + +What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_raw_range +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Range for angular velocity readings in radians per second. Note + that this does not affects the scale (which should be used when + changing the maximum and minimum readable value affects also the + reading scaling factor). + +What: /sys/bus/iio/devices/iio:deviceX/in_accel_raw_range_available +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + List of allowed values for in_accel_raw_range attribute + +What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_raw_range_available +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + List of allowed values for in_anglvel_raw_range attribute + +What: /sys/bus/iio/devices/iio:deviceX/in_magn_calibration_fast_enable +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Can be 1 or 0. Enables/disables the "Fast Magnetometer + Calibration" HW function. + +What: /sys/bus/iio/devices/iio:deviceX/fusion_enable +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Can be 1 or 0. Enables/disables the "sensor fusion" (a.k.a. + NDOF) HW function. + +What: /sys/bus/iio/devices/iio:deviceX/calibration_data +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Reports the binary calibration data blob for the IMU sensors. + +What: /sys/bus/iio/devices/iio:deviceX/in_accel_calibration_auto_status +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Reports the autocalibration status for the accelerometer sensor. + Can be 0 (calibration non even enabled) or 1 to 5 where the greater + the number, the better the calibration status. + +What: /sys/bus/iio/devices/iio:deviceX/in_gyro_calibration_auto_status +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Reports the autocalibration status for the gyroscope sensor. + Can be 0 (calibration non even enabled) or 1 to 5 where the greater + the number, the better the calibration status. + +What: /sys/bus/iio/devices/iio:deviceX/in_magn_calibration_auto_status +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Reports the autocalibration status for the magnetometer sensor. + Can be 0 (calibration non even enabled) or 1 to 5 where the greater + the number, the better the calibration status. + +What: /sys/bus/iio/devices/iio:deviceX/sys_calibration_auto_status +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Reports the status for the IMU overall autocalibration. + Can be 0 (calibration non even enabled) or 1 to 5 where the greater + the number, the better the calibration status. -- cgit v1.2.3 From e4ea07cf89a1208090f596ad69976b0596e990b3 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Wed, 7 Sep 2022 15:22:01 +0200 Subject: iio: document "serialnumber" sysfs attribute Add ABI documentation for the new "serialnumber" sysfs attribute. The first user is the bno055 IIO driver. Signed-off-by: Andrea Merello Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220907132205.28021-11-andrea.merello@iit.it Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index c79b3fde7668..6ba34c0d9789 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -2155,3 +2155,10 @@ Contact: linux-iio@vger.kernel.org Description: Raw (unscaled) euler angles readings. Units after application of scale are deg. + +What: /sys/bus/iio/devices/iio:deviceX/serialnumber +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + An example format is 16-bytes, 2-digits-per-byte, HEX-string + representing the sensor unique ID number. -- cgit v1.2.3 From 21f95c75e0b7e77370177173c67d0931d4020e78 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Wed, 7 Sep 2022 15:22:02 +0200 Subject: dt-bindings: iio/imu: Add Bosch BNO055 Introduce new documentation file for the Bosch BNO055 IMU. Signed-off-by: Andrea Merello Reviewed-by: Rob Herring Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220907132205.28021-12-andrea.merello@iit.it Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/imu/bosch,bno055.yaml | 59 ++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/imu/bosch,bno055.yaml diff --git a/Documentation/devicetree/bindings/iio/imu/bosch,bno055.yaml b/Documentation/devicetree/bindings/iio/imu/bosch,bno055.yaml new file mode 100644 index 000000000000..e0d06db161a9 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/imu/bosch,bno055.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/imu/bosch,bno055.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Bosch BNO055 + +maintainers: + - Andrea Merello + +description: | + Inertial Measurement Unit with Accelerometer, Gyroscope, Magnetometer and + internal MCU for sensor fusion + https://www.bosch-sensortec.com/products/smart-sensors/bno055/ + +properties: + compatible: + enum: + - bosch,bno055 + + reg: + maxItems: 1 + + reset-gpios: + maxItems: 1 + + clocks: + maxItems: 1 + +required: + - compatible + +additionalProperties: false + +examples: + - | + #include + serial { + imu { + compatible = "bosch,bno055"; + reset-gpios = <&gpio0 54 GPIO_ACTIVE_LOW>; + clocks = <&imu_clk>; + }; + }; + + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + imu@28 { + compatible = "bosch,bno055"; + reg = <0x28>; + reset-gpios = <&gpio0 54 GPIO_ACTIVE_LOW>; + clocks = <&imu_clk>; + }; + }; -- cgit v1.2.3 From 2eef5a9cc6439a4c8eb4c741a498e72f9335febe Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Wed, 7 Sep 2022 15:22:03 +0200 Subject: iio: imu: add BNO055 serdev driver Add a serdev driver for communicating to a BNO055 IMU via serial bus, and enable the BNO055 core driver to work in this scenario. Signed-off-by: Andrea Merello Link: https://lore.kernel.org/r/20220907132205.28021-13-andrea.merello@iit.it Signed-off-by: Jonathan Cameron --- drivers/iio/imu/bno055/Kconfig | 10 + drivers/iio/imu/bno055/Makefile | 5 + drivers/iio/imu/bno055/bno055_ser_core.c | 560 ++++++++++++++++++++++++++++++ drivers/iio/imu/bno055/bno055_ser_trace.c | 14 + drivers/iio/imu/bno055/bno055_ser_trace.h | 104 ++++++ 5 files changed, 693 insertions(+) create mode 100644 drivers/iio/imu/bno055/bno055_ser_core.c create mode 100644 drivers/iio/imu/bno055/bno055_ser_trace.c create mode 100644 drivers/iio/imu/bno055/bno055_ser_trace.h diff --git a/drivers/iio/imu/bno055/Kconfig b/drivers/iio/imu/bno055/Kconfig index 467bc0ed22f8..b51d63b49ea4 100644 --- a/drivers/iio/imu/bno055/Kconfig +++ b/drivers/iio/imu/bno055/Kconfig @@ -2,3 +2,13 @@ config BOSCH_BNO055 tristate + +config BOSCH_BNO055_SERIAL + tristate "Bosch BNO055 attached via UART" + depends on SERIAL_DEV_BUS + select BOSCH_BNO055 + help + Enable this to support Bosch BNO055 IMUs attached via UART. + + This driver can also be built as a module. If so, the module will be + called bno055_sl. diff --git a/drivers/iio/imu/bno055/Makefile b/drivers/iio/imu/bno055/Makefile index 0a682ade510c..ef9219563782 100644 --- a/drivers/iio/imu/bno055/Makefile +++ b/drivers/iio/imu/bno055/Makefile @@ -1,3 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_BOSCH_BNO055) += bno055.o +obj-$(CONFIG_BOSCH_BNO055_SERIAL) += bno055_ser.o +bno055_ser-y := bno055_ser_core.o +# define_trace.h needs to know how to find our header +CFLAGS_bno055_ser_trace.o := -I$(src) +bno055_ser-$(CONFIG_TRACING) += bno055_ser_trace.o diff --git a/drivers/iio/imu/bno055/bno055_ser_core.c b/drivers/iio/imu/bno055/bno055_ser_core.c new file mode 100644 index 000000000000..57728a568471 --- /dev/null +++ b/drivers/iio/imu/bno055/bno055_ser_core.c @@ -0,0 +1,560 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Serial line interface for Bosh BNO055 IMU (via serdev). + * This file implements serial communication up to the register read/write + * level. + * + * Copyright (C) 2021-2022 Istituto Italiano di Tecnologia + * Electronic Design Laboratory + * Written by Andrea Merello + * + * This driver is based on + * Plantower PMS7003 particulate matter sensor driver + * Which is + * Copyright (c) Tomasz Duszynski + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bno055_ser_trace.h" +#include "bno055.h" + +/* + * Register writes cmd have the following format + * +------+------+-----+-----+----- ... ----+ + * | 0xAA | 0xOO | REG | LEN | payload[LEN] | + * +------+------+-----+-----+----- ... ----+ + * + * Register write responses have the following format + * +------+----------+ + * | 0xEE | ERROCODE | + * +------+----------+ + * + * .. except when writing the SYS_RST bit (i.e. triggering a system reset); in + * case the IMU accepts the command, then it resets without responding. We don't + * handle this (yet) here (so we inform the common bno055 code not to perform + * sw resets - bno055 on serial bus basically requires the hw reset pin). + * + * Register read have the following format + * +------+------+-----+-----+ + * | 0xAA | 0xO1 | REG | LEN | + * +------+------+-----+-----+ + * + * Successful register read response have the following format + * +------+-----+----- ... ----+ + * | 0xBB | LEN | payload[LEN] | + * +------+-----+----- ... ----+ + * + * Failed register read response have the following format + * +------+--------+ + * | 0xEE | ERRCODE| (ERRCODE always > 1) + * +------+--------+ + * + * Error codes are + * 01: OK + * 02: read/write FAIL + * 04: invalid address + * 05: write on RO + * 06: wrong start byte + * 07: bus overrun + * 08: len too high + * 09: len too low + * 10: bus RX byte timeout (timeout is 30mS) + * + * + * **WORKAROUND ALERT** + * + * Serial communication seems very fragile: the BNO055 buffer seems to overflow + * very easy; BNO055 seems able to sink few bytes, then it needs a brief pause. + * On the other hand, it is also picky on timeout: if there is a pause > 30mS in + * between two bytes then the transaction fails (IMU internal RX FSM resets). + * + * BNO055 has been seen also failing to process commands in case we send them + * too close each other (or if it is somehow busy?) + * + * In particular I saw these scenarios: + * 1) If we send 2 bytes per time, then the IMU never(?) overflows. + * 2) If we send 4 bytes per time (i.e. the full header), then the IMU could + * overflow, but it seem to sink all 4 bytes, then it returns error. + * 3) If we send more than 4 bytes, the IMU could overflow, and I saw it sending + * error after 4 bytes are sent; we have troubles in synchronizing again, + * because we are still sending data, and the IMU interprets it as the 1st + * byte of a new command. + * + * While we must avoid case 3, we could send 4 bytes per time and eventually + * retry in case of failure; this seemed convenient for reads (which requires + * TXing exactly 4 bytes), however it has been seen that, depending by the IMU + * settings (e.g. LPF), failures became less or more frequent; in certain IMU + * configurations they are very rare, but in certain others we keeps failing + * even after like 30 retries. + * + * So, we just split TXes in [2-bytes + delay] steps, and still keep an eye on + * the IMU response; in case it overflows (which is now unlikely), we retry. + */ + +/* + * Read operation overhead: + * 4 bytes req + 2byte resp hdr. + * 6 bytes = 60 bit (considering 1start + 1stop bits). + * 60/115200 = ~520uS + about 2500mS delay -> ~3mS + * In 3mS we could read back about 34 bytes that means 17 samples, this means + * that in case of scattered reads in which the gap is 17 samples or less it is + * still convenient to go for a burst. + * We have to take into account also IMU response time - IMU seems to be often + * reasonably quick to respond, but sometimes it seems to be in some "critical + * section" in which it delays handling of serial protocol. Because of this we + * round-up to 22, which is the max number of samples, always bursting indeed. + */ +#define BNO055_SER_XFER_BURST_BREAK_THRESHOLD 22 + +struct bno055_ser_priv { + enum { + CMD_NONE, + CMD_READ, + CMD_WRITE, + } expect_response; + int expected_data_len; + u8 *response_buf; + + /** + * enum cmd_status - represent the status of a command sent to the HW. + * @STATUS_CRIT: The command failed: the serial communication failed. + * @STATUS_OK: The command executed successfully. + * @STATUS_FAIL: The command failed: HW responded with an error. + */ + enum { + STATUS_CRIT = -1, + STATUS_OK = 0, + STATUS_FAIL = 1, + } cmd_status; + + /* + * Protects all the above fields, which are accessed in behalf of both + * the serdev RX callback and the regmap side + */ + struct mutex lock; + + /* Only accessed in serdev RX callback context*/ + struct { + enum { + RX_IDLE, + RX_START, + RX_DATA, + } state; + int databuf_count; + int expected_len; + int type; + } rx; + + /* Never accessed in behalf of serdev RX callback context */ + bool cmd_stale; + + struct completion cmd_complete; + struct serdev_device *serdev; +}; + +static int bno055_ser_send_chunk(struct bno055_ser_priv *priv, const u8 *data, int len) +{ + int ret; + + trace_send_chunk(len, data); + ret = serdev_device_write(priv->serdev, data, len, msecs_to_jiffies(25)); + if (ret < 0) + return ret; + + if (ret < len) + return -EIO; + + return 0; +} + +/* + * Send a read or write command. + * 'data' can be NULL (used in read case). 'len' parameter is always valid; in + * case 'data' is non-NULL then it must match 'data' size. + */ +static int bno055_ser_do_send_cmd(struct bno055_ser_priv *priv, + bool read, int addr, int len, const u8 *data) +{ + u8 hdr[] = {0xAA, read, addr, len}; + int chunk_len; + int ret; + + ret = bno055_ser_send_chunk(priv, hdr, 2); + if (ret) + goto fail; + usleep_range(2000, 3000); + ret = bno055_ser_send_chunk(priv, hdr + 2, 2); + if (ret) + goto fail; + + if (read) + return 0; + + while (len) { + chunk_len = min(len, 2); + usleep_range(2000, 3000); + ret = bno055_ser_send_chunk(priv, data, chunk_len); + if (ret) + goto fail; + data += chunk_len; + len -= chunk_len; + } + + return 0; +fail: + /* waiting more than 30mS should clear the BNO055 internal state */ + usleep_range(40000, 50000); + return ret; +} + +static int bno055_ser_send_cmd(struct bno055_ser_priv *priv, + bool read, int addr, int len, const u8 *data) +{ + const int retry_max = 5; + int retry = retry_max; + int ret = 0; + + /* + * In case previous command was interrupted we still need to wait it to + * complete before we can issue new commands + */ + if (priv->cmd_stale) { + ret = wait_for_completion_interruptible_timeout(&priv->cmd_complete, + msecs_to_jiffies(100)); + if (ret == -ERESTARTSYS) + return -ERESTARTSYS; + + priv->cmd_stale = false; + /* if serial protocol broke, bail out */ + if (priv->cmd_status == STATUS_CRIT) + return -EIO; + } + + /* + * Try to convince the IMU to cooperate.. as explained in the comments + * at the top of this file, the IMU could also refuse the command (i.e. + * it is not ready yet); retry in this case. + */ + do { + mutex_lock(&priv->lock); + priv->expect_response = read ? CMD_READ : CMD_WRITE; + reinit_completion(&priv->cmd_complete); + mutex_unlock(&priv->lock); + + if (retry != retry_max) + trace_cmd_retry(read, addr, retry_max - retry); + ret = bno055_ser_do_send_cmd(priv, read, addr, len, data); + if (ret) + continue; + + ret = wait_for_completion_interruptible_timeout(&priv->cmd_complete, + msecs_to_jiffies(100)); + if (ret == -ERESTARTSYS) { + priv->cmd_stale = true; + return -ERESTARTSYS; + } + + if (!ret) + return -ETIMEDOUT; + + if (priv->cmd_status == STATUS_OK) + return 0; + if (priv->cmd_status == STATUS_CRIT) + return -EIO; + + /* loop in case priv->cmd_status == STATUS_FAIL */ + } while (--retry); + + if (ret < 0) + return ret; + if (priv->cmd_status == STATUS_FAIL) + return -EINVAL; + return 0; +} + +static int bno055_ser_write_reg(void *context, const void *_data, size_t count) +{ + const u8 *data = _data; + struct bno055_ser_priv *priv = context; + + if (count < 2) { + dev_err(&priv->serdev->dev, "Invalid write count %zu", count); + return -EINVAL; + } + + trace_write_reg(data[0], data[1]); + return bno055_ser_send_cmd(priv, 0, data[0], count - 1, data + 1); +} + +static int bno055_ser_read_reg(void *context, + const void *_reg, size_t reg_size, + void *val, size_t val_size) +{ + int ret; + int reg_addr; + const u8 *reg = _reg; + struct bno055_ser_priv *priv = context; + + if (val_size > 128) { + dev_err(&priv->serdev->dev, "Invalid read valsize %zu", val_size); + return -EINVAL; + } + + reg_addr = *reg; + trace_read_reg(reg_addr, val_size); + mutex_lock(&priv->lock); + priv->expected_data_len = val_size; + priv->response_buf = val; + mutex_unlock(&priv->lock); + + ret = bno055_ser_send_cmd(priv, 1, reg_addr, val_size, NULL); + + mutex_lock(&priv->lock); + priv->response_buf = NULL; + mutex_unlock(&priv->lock); + + return ret; +} + +/* + * Handler for received data; this is called from the receiver callback whenever + * it got some packet from the serial bus. The status tells us whether the + * packet is valid (i.e. header ok && received payload len consistent wrt the + * header). It's now our responsibility to check whether this is what we + * expected, of whether we got some unexpected, yet valid, packet. + */ +static void bno055_ser_handle_rx(struct bno055_ser_priv *priv, int status) +{ + mutex_lock(&priv->lock); + switch (priv->expect_response) { + case CMD_NONE: + dev_warn(&priv->serdev->dev, "received unexpected, yet valid, data from sensor"); + mutex_unlock(&priv->lock); + return; + + case CMD_READ: + priv->cmd_status = status; + if (status == STATUS_OK && + priv->rx.databuf_count != priv->expected_data_len) { + /* + * If we got here, then the lower layer serial protocol + * seems consistent with itself; if we got an unexpected + * amount of data then signal it as a non critical error + */ + priv->cmd_status = STATUS_FAIL; + dev_warn(&priv->serdev->dev, + "received an unexpected amount of, yet valid, data from sensor"); + } + break; + + case CMD_WRITE: + priv->cmd_status = status; + break; + } + + priv->expect_response = CMD_NONE; + mutex_unlock(&priv->lock); + complete(&priv->cmd_complete); +} + +/* + * Serdev receiver FSM. This tracks the serial communication and parse the + * header. It pushes packets to bno055_ser_handle_rx(), eventually communicating + * failures (i.e. malformed packets). + * Ideally it doesn't know anything about upper layer (i.e. if this is the + * packet we were really expecting), but since we copies the payload into the + * receiver buffer (that is not valid when i.e. we don't expect data), we + * snoop a bit in the upper layer.. + * Also, we assume to RX one pkt per time (i.e. the HW doesn't send anything + * unless we require to AND we don't queue more than one request per time). + */ +static int bno055_ser_receive_buf(struct serdev_device *serdev, + const unsigned char *buf, size_t size) +{ + int status; + struct bno055_ser_priv *priv = serdev_device_get_drvdata(serdev); + int remaining = size; + + if (size == 0) + return 0; + + trace_recv(size, buf); + switch (priv->rx.state) { + case RX_IDLE: + /* + * New packet. + * Check for its 1st byte that identifies the pkt type. + */ + if (buf[0] != 0xEE && buf[0] != 0xBB) { + dev_err(&priv->serdev->dev, + "Invalid packet start %x", buf[0]); + bno055_ser_handle_rx(priv, STATUS_CRIT); + break; + } + priv->rx.type = buf[0]; + priv->rx.state = RX_START; + remaining--; + buf++; + priv->rx.databuf_count = 0; + fallthrough; + + case RX_START: + /* + * Packet RX in progress, we expect either 1-byte len or 1-byte + * status depending by the packet type. + */ + if (remaining == 0) + break; + + if (priv->rx.type == 0xEE) { + if (remaining > 1) { + dev_err(&priv->serdev->dev, "EE pkt. Extra data received"); + status = STATUS_CRIT; + } else { + status = (buf[0] == 1) ? STATUS_OK : STATUS_FAIL; + } + bno055_ser_handle_rx(priv, status); + priv->rx.state = RX_IDLE; + break; + + } else { + /*priv->rx.type == 0xBB */ + priv->rx.state = RX_DATA; + priv->rx.expected_len = buf[0]; + remaining--; + buf++; + } + fallthrough; + + case RX_DATA: + /* Header parsed; now receiving packet data payload */ + if (remaining == 0) + break; + + if (priv->rx.databuf_count + remaining > priv->rx.expected_len) { + /* + * This is an inconsistency in serial protocol, we lost + * sync and we don't know how to handle further data + */ + dev_err(&priv->serdev->dev, "BB pkt. Extra data received"); + bno055_ser_handle_rx(priv, STATUS_CRIT); + priv->rx.state = RX_IDLE; + break; + } + + mutex_lock(&priv->lock); + /* + * NULL e.g. when read cmd is stale or when no read cmd is + * actually pending. + */ + if (priv->response_buf && + /* + * Snoop on the upper layer protocol stuff to make sure not + * to write to an invalid memory. Apart for this, let's the + * upper layer manage any inconsistency wrt expected data + * len (as long as the serial protocol is consistent wrt + * itself (i.e. response header is consistent with received + * response len. + */ + (priv->rx.databuf_count + remaining <= priv->expected_data_len)) + memcpy(priv->response_buf + priv->rx.databuf_count, + buf, remaining); + mutex_unlock(&priv->lock); + + priv->rx.databuf_count += remaining; + + /* + * Reached expected len advertised by the IMU for the current + * packet. Pass it to the upper layer (for us it is just valid). + */ + if (priv->rx.databuf_count == priv->rx.expected_len) { + bno055_ser_handle_rx(priv, STATUS_OK); + priv->rx.state = RX_IDLE; + } + break; + } + + return size; +} + +static const struct serdev_device_ops bno055_ser_serdev_ops = { + .receive_buf = bno055_ser_receive_buf, + .write_wakeup = serdev_device_write_wakeup, +}; + +static struct regmap_bus bno055_ser_regmap_bus = { + .write = bno055_ser_write_reg, + .read = bno055_ser_read_reg, +}; + +static int bno055_ser_probe(struct serdev_device *serdev) +{ + struct bno055_ser_priv *priv; + struct regmap *regmap; + int ret; + + priv = devm_kzalloc(&serdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + serdev_device_set_drvdata(serdev, priv); + priv->serdev = serdev; + mutex_init(&priv->lock); + init_completion(&priv->cmd_complete); + + serdev_device_set_client_ops(serdev, &bno055_ser_serdev_ops); + ret = devm_serdev_device_open(&serdev->dev, serdev); + if (ret) + return ret; + + if (serdev_device_set_baudrate(serdev, 115200) != 115200) { + dev_err(&serdev->dev, "Cannot set required baud rate"); + return -EIO; + } + + ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE); + if (ret) { + dev_err(&serdev->dev, "Cannot set required parity setting"); + return ret; + } + serdev_device_set_flow_control(serdev, false); + + regmap = devm_regmap_init(&serdev->dev, &bno055_ser_regmap_bus, + priv, &bno055_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(&serdev->dev, PTR_ERR(regmap), + "Unable to init register map"); + + return bno055_probe(&serdev->dev, regmap, + BNO055_SER_XFER_BURST_BREAK_THRESHOLD, false); +} + +static const struct of_device_id bno055_ser_of_match[] = { + { .compatible = "bosch,bno055" }, + { } +}; +MODULE_DEVICE_TABLE(of, bno055_ser_of_match); + +static struct serdev_device_driver bno055_ser_driver = { + .driver = { + .name = "bno055-ser", + .of_match_table = bno055_ser_of_match, + }, + .probe = bno055_ser_probe, +}; +module_serdev_device_driver(bno055_ser_driver); + +MODULE_AUTHOR("Andrea Merello "); +MODULE_DESCRIPTION("Bosch BNO055 serdev interface"); +MODULE_IMPORT_NS(IIO_BNO055); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/imu/bno055/bno055_ser_trace.c b/drivers/iio/imu/bno055/bno055_ser_trace.c new file mode 100644 index 000000000000..48397b66daef --- /dev/null +++ b/drivers/iio/imu/bno055/bno055_ser_trace.c @@ -0,0 +1,14 @@ +//SPDX-License-Identifier: GPL-2.0 + +/* + * bno055_ser Trace Support + * Copyright (C) 2022 Istituto Italiano di Tecnologia + * Electronic Design Laboratory + * + * Based on: + * Device core Trace Support + * Copyright (C) 2021, Intel Corporation + */ + +#define CREATE_TRACE_POINTS +#include "bno055_ser_trace.h" diff --git a/drivers/iio/imu/bno055/bno055_ser_trace.h b/drivers/iio/imu/bno055/bno055_ser_trace.h new file mode 100644 index 000000000000..7d9eae166eec --- /dev/null +++ b/drivers/iio/imu/bno055/bno055_ser_trace.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#if !defined(__BNO055_SERDEV_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) +#define __BNO055_SERDEV_TRACE_H__ + +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM bno055_ser + +TRACE_EVENT(send_chunk, + TP_PROTO(int len, const u8 *data), + TP_ARGS(len, data), + TP_STRUCT__entry( + __field(int, len) + __dynamic_array(u8, chunk, len) + ), + TP_fast_assign( + __entry->len = len; + memcpy(__get_dynamic_array(chunk), + data, __entry->len); + ), + TP_printk("len: %d, data: = %*ph", + __entry->len, __entry->len, __get_dynamic_array(chunk) + ) +); + +TRACE_EVENT(cmd_retry, + TP_PROTO(bool read, int addr, int retry), + TP_ARGS(read, addr, retry), + TP_STRUCT__entry( + __field(bool, read) + __field(int, addr) + __field(int, retry) + ), + TP_fast_assign( + __entry->read = read; + __entry->addr = addr; + __entry->retry = retry; + ), + TP_printk("%s addr 0x%x retry #%d", + __entry->read ? "read" : "write", + __entry->addr, __entry->retry + ) +); + +TRACE_EVENT(write_reg, + TP_PROTO(u8 addr, u8 value), + TP_ARGS(addr, value), + TP_STRUCT__entry( + __field(u8, addr) + __field(u8, value) + ), + TP_fast_assign( + __entry->addr = addr; + __entry->value = value; + ), + TP_printk("reg 0x%x = 0x%x", + __entry->addr, __entry->value + ) +); + +TRACE_EVENT(read_reg, + TP_PROTO(int addr, size_t len), + TP_ARGS(addr, len), + TP_STRUCT__entry( + __field(int, addr) + __field(size_t, len) + ), + TP_fast_assign( + __entry->addr = addr; + __entry->len = len; + ), + TP_printk("reg 0x%x (len %zu)", + __entry->addr, __entry->len + ) +); + +TRACE_EVENT(recv, + TP_PROTO(size_t len, const unsigned char *buf), + TP_ARGS(len, buf), + TP_STRUCT__entry( + __field(size_t, len) + __dynamic_array(unsigned char, buf, len) + ), + TP_fast_assign( + __entry->len = len; + memcpy(__get_dynamic_array(buf), + buf, __entry->len); + ), + TP_printk("len: %zu, data: = %*ph", + __entry->len, (int)__entry->len, __get_dynamic_array(buf) + ) +); + +#endif /* __BNO055_SERDEV_TRACE_H__ || TRACE_HEADER_MULTI_READ */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE bno055_ser_trace + +/* This part must be outside protection */ +#include -- cgit v1.2.3 From 50fe984f376abb4a00f80e13f51465ef09c2bde7 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Wed, 7 Sep 2022 15:22:04 +0200 Subject: iio: imu: add BNO055 I2C driver Add an I2C driver for communicating to a BNO055 IMU via I2C bus and enable the BNO055 core driver to work in this scenario. Signed-off-by: Andrea Merello Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220907132205.28021-14-andrea.merello@iit.it Signed-off-by: Jonathan Cameron --- drivers/iio/imu/bno055/Kconfig | 11 +++++++ drivers/iio/imu/bno055/Makefile | 2 ++ drivers/iio/imu/bno055/bno055_i2c.c | 57 +++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 drivers/iio/imu/bno055/bno055_i2c.c diff --git a/drivers/iio/imu/bno055/Kconfig b/drivers/iio/imu/bno055/Kconfig index b51d63b49ea4..fa79b1ac4f85 100644 --- a/drivers/iio/imu/bno055/Kconfig +++ b/drivers/iio/imu/bno055/Kconfig @@ -12,3 +12,14 @@ config BOSCH_BNO055_SERIAL This driver can also be built as a module. If so, the module will be called bno055_sl. + +config BOSCH_BNO055_I2C + tristate "Bosch BNO055 attached via I2C bus" + depends on I2C + select REGMAP_I2C + select BOSCH_BNO055 + help + Enable this to support Bosch BNO055 IMUs attached via I2C bus. + + This driver can also be built as a module. If so, the module will be + called bno055_i2c. diff --git a/drivers/iio/imu/bno055/Makefile b/drivers/iio/imu/bno055/Makefile index ef9219563782..98c624730dae 100644 --- a/drivers/iio/imu/bno055/Makefile +++ b/drivers/iio/imu/bno055/Makefile @@ -6,3 +6,5 @@ bno055_ser-y := bno055_ser_core.o # define_trace.h needs to know how to find our header CFLAGS_bno055_ser_trace.o := -I$(src) bno055_ser-$(CONFIG_TRACING) += bno055_ser_trace.o + +obj-$(CONFIG_BOSCH_BNO055_I2C) += bno055_i2c.o diff --git a/drivers/iio/imu/bno055/bno055_i2c.c b/drivers/iio/imu/bno055/bno055_i2c.c new file mode 100644 index 000000000000..c1bbc0fe34f9 --- /dev/null +++ b/drivers/iio/imu/bno055/bno055_i2c.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Support for I2C-interfaced Bosch BNO055 IMU. + * + * Copyright (C) 2021-2022 Istituto Italiano di Tecnologia + * Electronic Design Laboratory + * Written by Andrea Merello + */ + +#include +#include +#include +#include + +#include "bno055.h" + +#define BNO055_I2C_XFER_BURST_BREAK_THRESHOLD 3 + +static int bno055_i2c_probe(struct i2c_client *client) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, &bno055_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(&client->dev, PTR_ERR(regmap), + "Unable to init register map"); + + return bno055_probe(&client->dev, regmap, + BNO055_I2C_XFER_BURST_BREAK_THRESHOLD, true); +} + +static const struct i2c_device_id bno055_i2c_id[] = { + {"bno055", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, bno055_i2c_id); + +static const struct of_device_id __maybe_unused bno055_i2c_of_match[] = { + { .compatible = "bosch,bno055" }, + { } +}; +MODULE_DEVICE_TABLE(of, bno055_i2c_of_match); + +static struct i2c_driver bno055_driver = { + .driver = { + .name = "bno055-i2c", + .of_match_table = bno055_i2c_of_match, + }, + .probe_new = bno055_i2c_probe, + .id_table = bno055_i2c_id, +}; +module_i2c_driver(bno055_driver); + +MODULE_AUTHOR("Andrea Merello"); +MODULE_DESCRIPTION("Bosch BNO055 I2C interface"); +MODULE_IMPORT_NS(IIO_BNO055); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 130476acfdc1663bc402faa3f2faab5a910f696a Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Wed, 7 Sep 2022 15:22:05 +0200 Subject: docs: iio: add documentation for BNO055 driver The bno055 driver is rather complex and have some oddities and not-obvious things that worth to document (e.g. calibration files). This patch also contains this [0] fix squashed in. [0] https://lore.kernel.org/lkml/20220704034041.15448-1-bagasdotme@gmail.com/ Signed-off-by: Andrea Merello Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220907132205.28021-15-andrea.merello@iit.it Signed-off-by: Jonathan Cameron --- Documentation/iio/bno055.rst | 51 ++++++++++++++++++++++++++++++++++++++++++++ Documentation/iio/index.rst | 2 ++ 2 files changed, 53 insertions(+) create mode 100644 Documentation/iio/bno055.rst diff --git a/Documentation/iio/bno055.rst b/Documentation/iio/bno055.rst new file mode 100644 index 000000000000..9a489a79d8f5 --- /dev/null +++ b/Documentation/iio/bno055.rst @@ -0,0 +1,51 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============================== +BNO055 driver +============================== + +1. Overview +=========== + +This driver supports Bosch BNO055 IMUs (on both serial and I2C busses). + +Accelerometer, magnetometer and gyroscope measures are always provided. +When "fusion_enable" sysfs attribute is set to 1, orientation (both Euler +angles and quaternion), linear velocity and gravity vector are also +provided, but some sensor settings (e.g. low pass filtering and range) +became locked (the IMU firmware controls them). + +This driver supports also IIO buffers. + +2. Calibration +============== + +The IMU continuously performs an autocalibration procedure if (and only if) +operating in fusion mode. The magnetometer autocalibration can however be +disabled writing 0 in the sysfs in_magn_calibration_fast_enable attribute. + +The driver provides access to autocalibration flags (i.e. you can known if +the IMU has successfully autocalibrated) and to the calibration data blob. + +The user can save this blob in a firmware file (i.e. in /lib/firmware) that +the driver looks for at probe time. If found, then the IMU is initialized +with this calibration data. This saves the user from performing the +calibration procedure every time (which consist of moving the IMU in +various way). + +The driver looks for calibration data file using two different names: first +a file whose name is suffixed with the IMU unique ID (exposed in sysfs as +serial_number) is searched for; this is useful when there is more than one +IMU instance. If this file is not found, then a "generic" calibration file +is searched for (which can be used when only one IMU is present, without +struggling with fancy names, that change on each device). + +Valid calibration file names would be e.g. + bno055-caldata-0e7c26a33541515120204a35342b04ff.dat + bno055-caldata.dat + +In non-fusion mode the IIO 'offset' attributes provide access to the +offsets from calibration data (if any), so that the user can apply them to +the accel, angvel and magn IIO attributes. In fusion mode they are not +needed (the IMU firmware internally applies those corrections) and they +read as zero. diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst index 58b7a4ebac51..1b7292c58cd0 100644 --- a/Documentation/iio/index.rst +++ b/Documentation/iio/index.rst @@ -10,3 +10,5 @@ Industrial I/O iio_configfs ep93xx_adc + + bno055 -- cgit v1.2.3 From 93176acee936d330b600ca93b3ca6e9c40053fa9 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 7 Aug 2022 19:56:13 +0100 Subject: iio: proximity: sx9310: Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() These new macros avoid the need for marking the callbacks __maybe_unused whilst ensuring both callbacks and structure may be dropped by the compiler if CONFIG_PM_SLEEP is not enabled. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Cc: Gwendal Grignou Link: https://lore.kernel.org/r/20220807185618.1038812-2-jic23@kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/sx9310.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/proximity/sx9310.c b/drivers/iio/proximity/sx9310.c index ea7318b508ea..0e4747ccd3cf 100644 --- a/drivers/iio/proximity/sx9310.c +++ b/drivers/iio/proximity/sx9310.c @@ -965,7 +965,7 @@ static int sx9310_probe(struct i2c_client *client) return sx_common_probe(client, &sx9310_chip_info, &sx9310_regmap_config); } -static int __maybe_unused sx9310_suspend(struct device *dev) +static int sx9310_suspend(struct device *dev) { struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); u8 ctrl0; @@ -991,7 +991,7 @@ out: return ret; } -static int __maybe_unused sx9310_resume(struct device *dev) +static int sx9310_resume(struct device *dev) { struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); int ret; @@ -1013,7 +1013,7 @@ out: return 0; } -static SIMPLE_DEV_PM_OPS(sx9310_pm_ops, sx9310_suspend, sx9310_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(sx9310_pm_ops, sx9310_suspend, sx9310_resume); static const struct acpi_device_id sx9310_acpi_match[] = { { "STH9310", SX9310_WHOAMI_VALUE }, @@ -1041,7 +1041,7 @@ static struct i2c_driver sx9310_driver = { .name = "sx9310", .acpi_match_table = sx9310_acpi_match, .of_match_table = sx9310_of_match, - .pm = &sx9310_pm_ops, + .pm = pm_sleep_ptr(&sx9310_pm_ops), /* * Lots of i2c transfers in probe + over 200 ms waiting in -- cgit v1.2.3 From d53f6cdc1e5a974d12172978a1f0f8c555d011cb Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 7 Aug 2022 19:56:14 +0100 Subject: iio: proximity: sx9324: Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() These new macros avoid the need for marking the callbacks __maybe_unused whilst ensuring both callbacks and structure may be dropped by the compiler if CONFIG_PM_SLEEP is not enabled. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Cc: Gwendal Grignou Link: https://lore.kernel.org/r/20220807185618.1038812-3-jic23@kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/sx9324.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/proximity/sx9324.c b/drivers/iio/proximity/sx9324.c index edb5a2ce4e27..977cf17cec52 100644 --- a/drivers/iio/proximity/sx9324.c +++ b/drivers/iio/proximity/sx9324.c @@ -1073,7 +1073,7 @@ static int sx9324_probe(struct i2c_client *client) return sx_common_probe(client, &sx9324_chip_info, &sx9324_regmap_config); } -static int __maybe_unused sx9324_suspend(struct device *dev) +static int sx9324_suspend(struct device *dev) { struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); unsigned int regval; @@ -1098,7 +1098,7 @@ out: return ret; } -static int __maybe_unused sx9324_resume(struct device *dev) +static int sx9324_resume(struct device *dev) { struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); int ret; @@ -1114,7 +1114,7 @@ static int __maybe_unused sx9324_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(sx9324_pm_ops, sx9324_suspend, sx9324_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(sx9324_pm_ops, sx9324_suspend, sx9324_resume); static const struct acpi_device_id sx9324_acpi_match[] = { { "STH9324", SX9324_WHOAMI_VALUE }, @@ -1139,7 +1139,7 @@ static struct i2c_driver sx9324_driver = { .name = "sx9324", .acpi_match_table = sx9324_acpi_match, .of_match_table = sx9324_of_match, - .pm = &sx9324_pm_ops, + .pm = pm_sleep_ptr(&sx9324_pm_ops), /* * Lots of i2c transfers in probe + over 200 ms waiting in -- cgit v1.2.3 From 5c682eeecf8a255974e1f8b0695e3de40cb518a6 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 7 Aug 2022 19:56:15 +0100 Subject: iio: proximity: sx9360: Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() These new macros avoid the need for marking the callbacks __maybe_unused whilst ensuring both callbacks and structure may be dropped by the compiler if CONFIG_PM_SLEEP is not enabled. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Cc: Gwendal Grignou Link: https://lore.kernel.org/r/20220807185618.1038812-4-jic23@kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/sx9360.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/proximity/sx9360.c b/drivers/iio/proximity/sx9360.c index d9a12e6be6ca..7fa2213d23ba 100644 --- a/drivers/iio/proximity/sx9360.c +++ b/drivers/iio/proximity/sx9360.c @@ -819,7 +819,7 @@ static int sx9360_probe(struct i2c_client *client) return sx_common_probe(client, &sx9360_chip_info, &sx9360_regmap_config); } -static int __maybe_unused sx9360_suspend(struct device *dev) +static int sx9360_suspend(struct device *dev) { struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); unsigned int regval; @@ -844,7 +844,7 @@ out: return ret; } -static int __maybe_unused sx9360_resume(struct device *dev) +static int sx9360_resume(struct device *dev) { struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); int ret; @@ -861,7 +861,7 @@ static int __maybe_unused sx9360_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(sx9360_pm_ops, sx9360_suspend, sx9360_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(sx9360_pm_ops, sx9360_suspend, sx9360_resume); static const struct acpi_device_id sx9360_acpi_match[] = { { "STH9360", SX9360_WHOAMI_VALUE }, @@ -886,7 +886,7 @@ static struct i2c_driver sx9360_driver = { .name = "sx9360", .acpi_match_table = sx9360_acpi_match, .of_match_table = sx9360_of_match, - .pm = &sx9360_pm_ops, + .pm = pm_sleep_ptr(&sx9360_pm_ops), /* * Lots of i2c transfers in probe + over 200 ms waiting in -- cgit v1.2.3 From 1364262c8a381dd929f0973f92402c70d4e81b9f Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 7 Aug 2022 19:56:16 +0100 Subject: iio: proximity: srf04: Use pm_ptr() to remove unused struct dev_pm_ops If CONFIG_PM is not set, the pm_ptr() will ensure that the struct dev_pm_ops and callbacks are removed without the need for __maybe_unused markings. In this case we can't simply use DEFINE_RUNTIME_DEV_PM_OPS() because that would provide suspend and resume functions without the checks the driver is doing before calling runtime_pm functions (whether the necessary GPIO is provided). It may be possible to clean that up in future by moving the checks into the callbacks. Signed-off-by: Jonathan Cameron Cc: Andreas Klinger Link: https://lore.kernel.org/r/20220807185618.1038812-5-jic23@kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/srf04.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/iio/proximity/srf04.c b/drivers/iio/proximity/srf04.c index 05015351a34a..faf2f806ce80 100644 --- a/drivers/iio/proximity/srf04.c +++ b/drivers/iio/proximity/srf04.c @@ -359,7 +359,7 @@ static int srf04_remove(struct platform_device *pdev) return 0; } -static int __maybe_unused srf04_pm_runtime_suspend(struct device *dev) +static int srf04_pm_runtime_suspend(struct device *dev) { struct platform_device *pdev = container_of(dev, struct platform_device, dev); @@ -371,7 +371,7 @@ static int __maybe_unused srf04_pm_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused srf04_pm_runtime_resume(struct device *dev) +static int srf04_pm_runtime_resume(struct device *dev) { struct platform_device *pdev = container_of(dev, struct platform_device, dev); @@ -385,8 +385,8 @@ static int __maybe_unused srf04_pm_runtime_resume(struct device *dev) } static const struct dev_pm_ops srf04_pm_ops = { - SET_RUNTIME_PM_OPS(srf04_pm_runtime_suspend, - srf04_pm_runtime_resume, NULL) + RUNTIME_PM_OPS(srf04_pm_runtime_suspend, + srf04_pm_runtime_resume, NULL) }; static struct platform_driver srf04_driver = { @@ -395,7 +395,7 @@ static struct platform_driver srf04_driver = { .driver = { .name = "srf04-gpio", .of_match_table = of_srf04_match, - .pm = &srf04_pm_ops, + .pm = pm_ptr(&srf04_pm_ops), }, }; -- cgit v1.2.3 From 66991b106a2c182c1b499d45d3025318d8774bd6 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 7 Aug 2022 19:56:17 +0100 Subject: iio: accel: bmi088: Use EXPORT_NS_GPL_RUNTIME_DEV_PM_OPS() and pm_ptr() These macros allow the compiler to remove unused pm ops functions without needing to mark them maybe unused. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Cc: LI Qingwu Cc: Mike Looijmans Link: https://lore.kernel.org/r/20220807185618.1038812-6-jic23@kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bmi088-accel-core.c | 15 ++++++--------- drivers/iio/accel/bmi088-accel-spi.c | 2 +- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/iio/accel/bmi088-accel-core.c b/drivers/iio/accel/bmi088-accel-core.c index bca4cf98bf4d..84edcc78d796 100644 --- a/drivers/iio/accel/bmi088-accel-core.c +++ b/drivers/iio/accel/bmi088-accel-core.c @@ -606,7 +606,7 @@ void bmi088_accel_core_remove(struct device *dev) } EXPORT_SYMBOL_NS_GPL(bmi088_accel_core_remove, IIO_BMI088); -static int __maybe_unused bmi088_accel_runtime_suspend(struct device *dev) +static int bmi088_accel_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct bmi088_accel_data *data = iio_priv(indio_dev); @@ -614,7 +614,7 @@ static int __maybe_unused bmi088_accel_runtime_suspend(struct device *dev) return bmi088_accel_power_down(data); } -static int __maybe_unused bmi088_accel_runtime_resume(struct device *dev) +static int bmi088_accel_runtime_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct bmi088_accel_data *data = iio_priv(indio_dev); @@ -622,13 +622,10 @@ static int __maybe_unused bmi088_accel_runtime_resume(struct device *dev) return bmi088_accel_power_up(data); } -const struct dev_pm_ops bmi088_accel_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(bmi088_accel_runtime_suspend, - bmi088_accel_runtime_resume, NULL) -}; -EXPORT_SYMBOL_NS_GPL(bmi088_accel_pm_ops, IIO_BMI088); +EXPORT_NS_GPL_RUNTIME_DEV_PM_OPS(bmi088_accel_pm_ops, + bmi088_accel_runtime_suspend, + bmi088_accel_runtime_resume, NULL, + IIO_BMI088); MODULE_AUTHOR("Niek van Agt "); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/bmi088-accel-spi.c b/drivers/iio/accel/bmi088-accel-spi.c index 9e2ed3bd5661..ee540edd8412 100644 --- a/drivers/iio/accel/bmi088-accel-spi.c +++ b/drivers/iio/accel/bmi088-accel-spi.c @@ -80,7 +80,7 @@ MODULE_DEVICE_TABLE(spi, bmi088_accel_id); static struct spi_driver bmi088_accel_driver = { .driver = { .name = "bmi088_accel_spi", - .pm = &bmi088_accel_pm_ops, + .pm = pm_ptr(&bmi088_accel_pm_ops), .of_match_table = bmi088_of_match, }, .probe = bmi088_accel_probe, -- cgit v1.2.3 From 3259b99e0afc9b7b245b5aaa121a94a2b5d637ce Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 7 Aug 2022 19:56:18 +0100 Subject: iio: light: st_uvis25: Use EXPORT_NS_SIMPLE_DEV_PM_OPS() Using this new macro removes the need to mark the callbacks __maybe_unused. One slightly complexity in this case is that the export will exist if CONFIG_PM is set, but only be used if CONFIG_PM_SLEEP is also set. This is harmless. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Cc: Lorenzo Bianconi Link: https://lore.kernel.org/r/20220807185618.1038812-7-jic23@kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/light/st_uvis25_core.c | 9 +++------ drivers/iio/light/st_uvis25_i2c.c | 2 +- drivers/iio/light/st_uvis25_spi.c | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/iio/light/st_uvis25_core.c b/drivers/iio/light/st_uvis25_core.c index 3d4cc1180b6a..c737d3e193ae 100644 --- a/drivers/iio/light/st_uvis25_core.c +++ b/drivers/iio/light/st_uvis25_core.c @@ -325,7 +325,7 @@ int st_uvis25_probe(struct device *dev, int irq, struct regmap *regmap) } EXPORT_SYMBOL_NS(st_uvis25_probe, IIO_UVIS25); -static int __maybe_unused st_uvis25_suspend(struct device *dev) +static int st_uvis25_suspend(struct device *dev) { struct iio_dev *iio_dev = dev_get_drvdata(dev); struct st_uvis25_hw *hw = iio_priv(iio_dev); @@ -334,7 +334,7 @@ static int __maybe_unused st_uvis25_suspend(struct device *dev) ST_UVIS25_REG_ODR_MASK, 0); } -static int __maybe_unused st_uvis25_resume(struct device *dev) +static int st_uvis25_resume(struct device *dev) { struct iio_dev *iio_dev = dev_get_drvdata(dev); struct st_uvis25_hw *hw = iio_priv(iio_dev); @@ -346,10 +346,7 @@ static int __maybe_unused st_uvis25_resume(struct device *dev) return 0; } -const struct dev_pm_ops st_uvis25_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(st_uvis25_suspend, st_uvis25_resume) -}; -EXPORT_SYMBOL_NS(st_uvis25_pm_ops, IIO_UVIS25); +EXPORT_NS_SIMPLE_DEV_PM_OPS(st_uvis25_pm_ops, st_uvis25_suspend, st_uvis25_resume, IIO_UVIS25); MODULE_AUTHOR("Lorenzo Bianconi "); MODULE_DESCRIPTION("STMicroelectronics uvis25 sensor driver"); diff --git a/drivers/iio/light/st_uvis25_i2c.c b/drivers/iio/light/st_uvis25_i2c.c index b06d09af28a3..c982b0b255cf 100644 --- a/drivers/iio/light/st_uvis25_i2c.c +++ b/drivers/iio/light/st_uvis25_i2c.c @@ -55,7 +55,7 @@ MODULE_DEVICE_TABLE(i2c, st_uvis25_i2c_id_table); static struct i2c_driver st_uvis25_driver = { .driver = { .name = "st_uvis25_i2c", - .pm = &st_uvis25_pm_ops, + .pm = pm_sleep_ptr(&st_uvis25_pm_ops), .of_match_table = st_uvis25_i2c_of_match, }, .probe = st_uvis25_i2c_probe, diff --git a/drivers/iio/light/st_uvis25_spi.c b/drivers/iio/light/st_uvis25_spi.c index 3a4dc6d7180c..86a232320d7d 100644 --- a/drivers/iio/light/st_uvis25_spi.c +++ b/drivers/iio/light/st_uvis25_spi.c @@ -55,7 +55,7 @@ MODULE_DEVICE_TABLE(spi, st_uvis25_spi_id_table); static struct spi_driver st_uvis25_driver = { .driver = { .name = "st_uvis25_spi", - .pm = &st_uvis25_pm_ops, + .pm = pm_sleep_ptr(&st_uvis25_pm_ops), .of_match_table = st_uvis25_spi_of_match, }, .probe = st_uvis25_spi_probe, -- cgit v1.2.3 From 6ee2a7058fea5d42087045250c667ac02f1a4e20 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 17 Sep 2022 14:14:01 +0100 Subject: iio: accel: bma400: Fix smatch warning based on use of unintialized value. Only specific bits in this value are ever used and those are initialized, but that is complex to reason about in a checker. Hence, initialize the value to zero and avoid the complexity. Smatch warning: drivers/iio/accel/bma400_core.c:1287 bma400_tap_event_en() error: uninitialized symbol 'field_value'. Reported-by: Dan Carpenter Cc: Jagath Jog J Cc: Alexander Potapenko Signed-off-by: Jonathan Cameron Acked-by: Jagath Jog J Fixes: 961db2da159d ("iio: accel: bma400: Add support for single and double tap events") Link: https://lore.kernel.org/r/20220917131401.2815486-1-jic23@kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bma400_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c index eceb1f8d338d..ad8fce3e08cd 100644 --- a/drivers/iio/accel/bma400_core.c +++ b/drivers/iio/accel/bma400_core.c @@ -1184,7 +1184,8 @@ static int bma400_activity_event_en(struct bma400_data *data, enum iio_event_direction dir, int state) { - int ret, reg, msk, value, field_value; + int ret, reg, msk, value; + int field_value = 0; switch (dir) { case IIO_EV_DIR_RISING: -- cgit v1.2.3 From 0e0a07adaff9718f22a55148c764a3d9dd2e1c5c Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Mon, 15 Aug 2022 08:16:20 +0200 Subject: iio: adc: mcp3911: use resource-managed version of iio_device_register Keep using managed resources as much as possible. Signed-off-by: Marcus Folkesson Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220815061625.35568-5-marcus.folkesson@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mcp3911.c | 51 +++++++++++++---------------------------------- 1 file changed, 14 insertions(+), 37 deletions(-) diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c index 890af7dca62d..15b3e5bc85ee 100644 --- a/drivers/iio/adc/mcp3911.c +++ b/drivers/iio/adc/mcp3911.c @@ -258,6 +258,11 @@ static int mcp3911_config(struct mcp3911 *adc) return mcp3911_write(adc, MCP3911_REG_CONFIG, configreg, 2); } +static void mcp3911_cleanup_regulator(void *vref) +{ + regulator_disable(vref); +} + static int mcp3911_probe(struct spi_device *spi) { struct iio_dev *indio_dev; @@ -286,9 +291,14 @@ static int mcp3911_probe(struct spi_device *spi) ret = regulator_enable(adc->vref); if (ret) return ret; + + ret = devm_add_action_or_reset(&spi->dev, + mcp3911_cleanup_regulator, adc->vref); + if (ret) + return ret; } - adc->clki = devm_clk_get(&adc->spi->dev, NULL); + adc->clki = devm_clk_get_enabled(&adc->spi->dev, NULL); if (IS_ERR(adc->clki)) { if (PTR_ERR(adc->clki) == -ENOENT) { adc->clki = NULL; @@ -296,21 +306,13 @@ static int mcp3911_probe(struct spi_device *spi) dev_err(&adc->spi->dev, "failed to get adc clk (%ld)\n", PTR_ERR(adc->clki)); - ret = PTR_ERR(adc->clki); - goto reg_disable; - } - } else { - ret = clk_prepare_enable(adc->clki); - if (ret < 0) { - dev_err(&adc->spi->dev, - "Failed to enable clki: %d\n", ret); - goto reg_disable; + return PTR_ERR(adc->clki); } } ret = mcp3911_config(adc); if (ret) - goto clk_disable; + return ret; indio_dev->name = spi_get_device_id(spi)->name; indio_dev->modes = INDIO_DIRECT_MODE; @@ -322,31 +324,7 @@ static int mcp3911_probe(struct spi_device *spi) mutex_init(&adc->lock); - ret = iio_device_register(indio_dev); - if (ret) - goto clk_disable; - - return ret; - -clk_disable: - clk_disable_unprepare(adc->clki); -reg_disable: - if (adc->vref) - regulator_disable(adc->vref); - - return ret; -} - -static void mcp3911_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct mcp3911 *adc = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - clk_disable_unprepare(adc->clki); - if (adc->vref) - regulator_disable(adc->vref); + return devm_iio_device_register(&adc->spi->dev, indio_dev); } static const struct of_device_id mcp3911_dt_ids[] = { @@ -367,7 +345,6 @@ static struct spi_driver mcp3911_driver = { .of_match_table = mcp3911_dt_ids, }, .probe = mcp3911_probe, - .remove = mcp3911_remove, .id_table = mcp3911_id, }; module_spi_driver(mcp3911_driver); -- cgit v1.2.3 From 5db9f38d394f1204f55934e27fe023de1064d317 Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Mon, 15 Aug 2022 08:16:21 +0200 Subject: iio: adc: mcp3911: add support for buffers Add support for buffers to make the driver fit for more use cases. Signed-off-by: Marcus Folkesson Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220815061625.35568-6-marcus.folkesson@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 2 + drivers/iio/adc/mcp3911.c | 103 ++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 97 insertions(+), 8 deletions(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 56e3ca3b4a5d..791612ca6012 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -732,6 +732,8 @@ config MCP3422 config MCP3911 tristate "Microchip Technology MCP3911 driver" depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER help Say yes here to build support for Microchip Technology's MCP3911 analog to digital converter. diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c index 15b3e5bc85ee..15290a6f9b01 100644 --- a/drivers/iio/adc/mcp3911.c +++ b/drivers/iio/adc/mcp3911.c @@ -5,16 +5,25 @@ * Copyright (C) 2018 Marcus Folkesson * Copyright (C) 2018 Kent Gustavsson */ +#include +#include #include #include #include -#include #include #include #include #include #include +#include +#include +#include +#include +#include + +#include + #define MCP3911_REG_CHANNEL0 0x00 #define MCP3911_REG_CHANNEL1 0x03 #define MCP3911_REG_MOD 0x06 @@ -22,6 +31,7 @@ #define MCP3911_REG_GAIN 0x09 #define MCP3911_REG_STATUSCOM 0x0a +#define MCP3911_STATUSCOM_READ GENMASK(7, 6) #define MCP3911_STATUSCOM_CH1_24WIDTH BIT(4) #define MCP3911_STATUSCOM_CH0_24WIDTH BIT(3) #define MCP3911_STATUSCOM_EN_OFFCAL BIT(2) @@ -54,6 +64,13 @@ struct mcp3911 { struct regulator *vref; struct clk *clki; u32 dev_addr; + struct { + u32 channels[MCP3911_NUM_CHANNELS]; + s64 ts __aligned(8); + } scan; + + u8 tx_buf __aligned(IIO_DMA_MINALIGN); + u8 rx_buf[MCP3911_NUM_CHANNELS * 3]; }; static int mcp3911_read(struct mcp3911 *adc, u8 reg, u32 *val, u8 len) @@ -196,16 +213,66 @@ out: .type = IIO_VOLTAGE, \ .indexed = 1, \ .channel = idx, \ + .scan_index = idx, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_OFFSET) | \ BIT(IIO_CHAN_INFO_SCALE), \ + .scan_type = { \ + .sign = 's', \ + .realbits = 24, \ + .storagebits = 32, \ + .endianness = IIO_BE, \ + }, \ } static const struct iio_chan_spec mcp3911_channels[] = { MCP3911_CHAN(0), MCP3911_CHAN(1), + IIO_CHAN_SOFT_TIMESTAMP(2), }; +static irqreturn_t mcp3911_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct mcp3911 *adc = iio_priv(indio_dev); + struct spi_transfer xfer[] = { + { + .tx_buf = &adc->tx_buf, + .len = 1, + }, { + .rx_buf = adc->rx_buf, + .len = sizeof(adc->rx_buf), + }, + }; + int scan_index; + int i = 0; + int ret; + + mutex_lock(&adc->lock); + adc->tx_buf = MCP3911_REG_READ(MCP3911_CHANNEL(0), adc->dev_addr); + ret = spi_sync_transfer(adc->spi, xfer, ARRAY_SIZE(xfer)); + if (ret < 0) { + dev_warn(&adc->spi->dev, + "failed to get conversion data\n"); + goto out; + } + + for_each_set_bit(scan_index, indio_dev->active_scan_mask, indio_dev->masklength) { + const struct iio_chan_spec *scan_chan = &indio_dev->channels[scan_index]; + + adc->scan.channels[i] = get_unaligned_be24(&adc->rx_buf[scan_chan->channel * 3]); + i++; + } + iio_push_to_buffers_with_timestamp(indio_dev, &adc->scan, + iio_get_time_ns(indio_dev)); +out: + mutex_unlock(&adc->lock); + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + static const struct iio_info mcp3911_info = { .read_raw = mcp3911_read_raw, .write_raw = mcp3911_write_raw, @@ -214,7 +281,7 @@ static const struct iio_info mcp3911_info = { static int mcp3911_config(struct mcp3911 *adc) { struct device *dev = &adc->spi->dev; - u32 configreg; + u32 regval; int ret; ret = device_property_read_u32(dev, "microchip,device-addr", &adc->dev_addr); @@ -233,29 +300,43 @@ static int mcp3911_config(struct mcp3911 *adc) } dev_dbg(&adc->spi->dev, "use device address %i\n", adc->dev_addr); - ret = mcp3911_read(adc, MCP3911_REG_CONFIG, &configreg, 2); + ret = mcp3911_read(adc, MCP3911_REG_CONFIG, ®val, 2); if (ret) return ret; + regval &= ~MCP3911_CONFIG_VREFEXT; if (adc->vref) { dev_dbg(&adc->spi->dev, "use external voltage reference\n"); - configreg |= MCP3911_CONFIG_VREFEXT; + regval |= FIELD_PREP(MCP3911_CONFIG_VREFEXT, 1); } else { dev_dbg(&adc->spi->dev, "use internal voltage reference (1.2V)\n"); - configreg &= ~MCP3911_CONFIG_VREFEXT; + regval |= FIELD_PREP(MCP3911_CONFIG_VREFEXT, 0); } + regval &= ~MCP3911_CONFIG_CLKEXT; if (adc->clki) { dev_dbg(&adc->spi->dev, "use external clock as clocksource\n"); - configreg |= MCP3911_CONFIG_CLKEXT; + regval |= FIELD_PREP(MCP3911_CONFIG_CLKEXT, 1); } else { dev_dbg(&adc->spi->dev, "use crystal oscillator as clocksource\n"); - configreg &= ~MCP3911_CONFIG_CLKEXT; + regval |= FIELD_PREP(MCP3911_CONFIG_CLKEXT, 0); } - return mcp3911_write(adc, MCP3911_REG_CONFIG, configreg, 2); + ret = mcp3911_write(adc, MCP3911_REG_CONFIG, regval, 2); + if (ret) + return ret; + + ret = mcp3911_read(adc, MCP3911_REG_STATUSCOM, ®val, 2); + if (ret) + return ret; + + /* Address counter incremented, cycle through register types */ + regval &= ~MCP3911_STATUSCOM_READ; + regval |= FIELD_PREP(MCP3911_STATUSCOM_READ, 0x02); + + return mcp3911_write(adc, MCP3911_REG_STATUSCOM, regval, 2); } static void mcp3911_cleanup_regulator(void *vref) @@ -324,6 +405,12 @@ static int mcp3911_probe(struct spi_device *spi) mutex_init(&adc->lock); + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, + NULL, + mcp3911_trigger_handler, NULL); + if (ret) + return ret; + return devm_iio_device_register(&adc->spi->dev, indio_dev); } -- cgit v1.2.3 From 08a65f61db69ae4850c3c15e8fbe6293a09a998d Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Mon, 15 Aug 2022 08:16:22 +0200 Subject: iio: adc: mcp3911: add support for interrupts Make it possible to read values upon interrupts. Configure Data Ready Signal Output Pin to either HiZ or push-pull and use it as interrupt source. Signed-off-by: Marcus Folkesson Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220815061625.35568-7-marcus.folkesson@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mcp3911.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c index 15290a6f9b01..4e45cccef085 100644 --- a/drivers/iio/adc/mcp3911.c +++ b/drivers/iio/adc/mcp3911.c @@ -31,6 +31,7 @@ #define MCP3911_REG_GAIN 0x09 #define MCP3911_REG_STATUSCOM 0x0a +#define MCP3911_STATUSCOM_DRHIZ BIT(12) #define MCP3911_STATUSCOM_READ GENMASK(7, 6) #define MCP3911_STATUSCOM_CH1_24WIDTH BIT(4) #define MCP3911_STATUSCOM_CH0_24WIDTH BIT(3) @@ -64,6 +65,7 @@ struct mcp3911 { struct regulator *vref; struct clk *clki; u32 dev_addr; + struct iio_trigger *trig; struct { u32 channels[MCP3911_NUM_CHANNELS]; s64 ts __aligned(8); @@ -344,6 +346,23 @@ static void mcp3911_cleanup_regulator(void *vref) regulator_disable(vref); } +static int mcp3911_set_trigger_state(struct iio_trigger *trig, bool enable) +{ + struct mcp3911 *adc = iio_trigger_get_drvdata(trig); + + if (enable) + enable_irq(adc->spi->irq); + else + disable_irq(adc->spi->irq); + + return 0; +} + +static const struct iio_trigger_ops mcp3911_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, + .set_trigger_state = mcp3911_set_trigger_state, +}; + static int mcp3911_probe(struct spi_device *spi) { struct iio_dev *indio_dev; @@ -395,6 +414,15 @@ static int mcp3911_probe(struct spi_device *spi) if (ret) return ret; + if (device_property_read_bool(&adc->spi->dev, "microchip,data-ready-hiz")) + ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM, MCP3911_STATUSCOM_DRHIZ, + 0, 2); + else + ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM, MCP3911_STATUSCOM_DRHIZ, + MCP3911_STATUSCOM_DRHIZ, 2); + if (ret) + return ret; + indio_dev->name = spi_get_device_id(spi)->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &mcp3911_info; @@ -405,6 +433,31 @@ static int mcp3911_probe(struct spi_device *spi) mutex_init(&adc->lock); + if (spi->irq > 0) { + adc->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!adc->trig) + return PTR_ERR(adc->trig); + + adc->trig->ops = &mcp3911_trigger_ops; + iio_trigger_set_drvdata(adc->trig, adc); + ret = devm_iio_trigger_register(&spi->dev, adc->trig); + if (ret) + return ret; + + /* + * The device generates interrupts as long as it is powered up. + * Some platforms might not allow the option to power it down so + * don't enable the interrupt to avoid extra load on the system. + */ + ret = devm_request_irq(&spi->dev, spi->irq, + &iio_trigger_generic_data_rdy_poll, IRQF_NO_AUTOEN | IRQF_ONESHOT, + indio_dev->name, adc->trig); + if (ret) + return ret; + } + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, mcp3911_trigger_handler, NULL); -- cgit v1.2.3 From 1a84fa695c7385b4f5547795149cb0bdd1073be8 Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Mon, 15 Aug 2022 08:16:23 +0200 Subject: dt-bindings: iio: adc: mcp3911: add microchip,data-ready-hiz entry The Data Ready Output Pin is either hard wired to work as high impedance or push-pull. Make it configurable. Signed-off-by: Marcus Folkesson Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220815061625.35568-8-marcus.folkesson@gmail.com Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml b/Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml index 067a7bbadab8..2c93fb41f172 100644 --- a/Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml +++ b/Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml @@ -36,6 +36,13 @@ properties: description: IRQ line of the ADC maxItems: 1 + microchip,data-ready-hiz: + description: + Data Ready Pin Inactive State Control + true = The DR pin state is high-impedance + false = The DR pin state is logic high + type: boolean + microchip,device-addr: description: Device address when multiple MCP3911 chips are present on the same SPI bus. $ref: /schemas/types.yaml#/definitions/uint32 -- cgit v1.2.3 From 6d965885f4ea82f916d31f17f67b2ad771bdabac Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Mon, 15 Aug 2022 08:16:24 +0200 Subject: iio: adc: mcp3911: add support for oversampling ratio The chip supports oversampling ratio, so expose it to userspace. Signed-off-by: Marcus Folkesson Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220815061625.35568-9-marcus.folkesson@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mcp3911.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c index 4e45cccef085..b35fd2c9c3c0 100644 --- a/drivers/iio/adc/mcp3911.c +++ b/drivers/iio/adc/mcp3911.c @@ -41,6 +41,7 @@ #define MCP3911_REG_CONFIG 0x0c #define MCP3911_CONFIG_CLKEXT BIT(1) #define MCP3911_CONFIG_VREFEXT BIT(2) +#define MCP3911_CONFIG_OSR GENMASK(13, 11) #define MCP3911_REG_OFFCAL_CH0 0x0e #define MCP3911_REG_GAINCAL_CH0 0x11 @@ -59,6 +60,8 @@ #define MCP3911_NUM_CHANNELS 2 +static const int mcp3911_osr_table[] = { 32, 64, 128, 256, 512, 1024, 2048, 4096 }; + struct mcp3911 { struct spi_device *spi; struct mutex lock; @@ -117,6 +120,36 @@ static int mcp3911_update(struct mcp3911 *adc, u8 reg, u32 mask, return mcp3911_write(adc, reg, val, len); } +static int mcp3911_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return IIO_VAL_INT; + default: + return IIO_VAL_INT_PLUS_NANO; + } +} + +static int mcp3911_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *type = IIO_VAL_INT; + *vals = mcp3911_osr_table; + *length = ARRAY_SIZE(mcp3911_osr_table); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + static int mcp3911_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *channel, int *val, int *val2, long mask) @@ -145,6 +178,15 @@ static int mcp3911_read_raw(struct iio_dev *indio_dev, ret = IIO_VAL_INT; break; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + ret = mcp3911_read(adc, MCP3911_REG_CONFIG, val, 2); + if (ret) + goto out; + + *val = FIELD_GET(MCP3911_CONFIG_OSR, *val); + *val = 32 << *val; + ret = IIO_VAL_INT; + break; case IIO_CHAN_INFO_SCALE: if (adc->vref) { @@ -204,6 +246,17 @@ static int mcp3911_write_raw(struct iio_dev *indio_dev, MCP3911_STATUSCOM_EN_OFFCAL, MCP3911_STATUSCOM_EN_OFFCAL, 2); break; + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + for (int i = 0; i < sizeof(mcp3911_osr_table); i++) { + if (val == mcp3911_osr_table[i]) { + val = FIELD_PREP(MCP3911_CONFIG_OSR, i); + ret = mcp3911_update(adc, MCP3911_REG_CONFIG, MCP3911_CONFIG_OSR, + val, 2); + break; + } + } + break; } out: @@ -216,9 +269,12 @@ out: .indexed = 1, \ .channel = idx, \ .scan_index = idx, \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_OFFSET) | \ BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .scan_type = { \ .sign = 's', \ .realbits = 24, \ @@ -278,6 +334,8 @@ out: static const struct iio_info mcp3911_info = { .read_raw = mcp3911_read_raw, .write_raw = mcp3911_write_raw, + .read_avail = mcp3911_read_avail, + .write_raw_get_fmt = mcp3911_write_raw_get_fmt, }; static int mcp3911_config(struct mcp3911 *adc) -- cgit v1.2.3 From cca1fd41ab2862465d75443822d751e4f9a112ee Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Thu, 22 Sep 2022 07:20:57 -0400 Subject: counter: Realign counter_comp comment block to 80 characters The member documentation comment lines for struct counter_comp extend past the 80-characters column boundary due to extra identation at the start of each section. This patch realigns the comment block within the 80-characters boundary by removing these superfluous indents. Reviewed-by: Yanteng Si Link: https://lore.kernel.org/r/20220902120839.4260-1-william.gray@linaro.org/ Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/8294b04153c33602e9c3dd21ac90c1e99bd0fdaf.1663844776.git.william.gray@linaro.org Signed-off-by: Greg Kroah-Hartman --- include/linux/counter.h | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/include/linux/counter.h b/include/linux/counter.h index 1fe17f5adb09..a81234bc8ea8 100644 --- a/include/linux/counter.h +++ b/include/linux/counter.h @@ -38,64 +38,64 @@ enum counter_comp_type { * @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 + * @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 + * @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 + * @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 + * @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 + * @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 + * @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 + * @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 + * @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 + * @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 + * @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 + * @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 + * @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 + * @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 + * @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 + * @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 + * @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 + * @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 + * @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 + * @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 + * @signal_u64_write: Signal u64 component write callback. The write value of * the respective Signal u64 component is passed via the * val parameter. */ -- cgit v1.2.3 From 0e8bf26c777a7da6e085ff1f0e31640a042dae5c Mon Sep 17 00:00:00 2001 From: Yihao Han Date: Mon, 12 Sep 2022 20:02:57 -0700 Subject: misc: microchip: pci1xxxx: Remove duplicate include Remove duplicate include in mchp_pci1xxxx_gpio.c Fixes: 7d3e4d807df2 ("misc: microchip: pci1xxxx: load gpio driver for the gpio controller auxiliary device enumerated by the auxiliary bus driver.") Reviewed-by: Kumaravel Thiagarajan Signed-off-by: Yihao Han Link: https://lore.kernel.org/r/20220913030257.22352-1-hanyihao@vivo.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c index 9cc771c604ed..3cda6e679cc7 100644 --- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From dc2c96a39da197da137a797f22bb408b5865514d Mon Sep 17 00:00:00 2001 From: Kumaravel Thiagarajan Date: Thu, 15 Sep 2022 15:17:29 +0530 Subject: misc: microchip: pci1xxxx: use DEFINE_SIMPLE_DEV_PM_OPS() in place of the SIMPLE_DEV_PM_OPS() in pci1xxxx's gpio driver build errors listed below and reported by Sudip Mukherjee for the builds of riscv, s390, csky, alpha and loongarch allmodconfig are fixed in this patch. drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c:311:12: error: 'pci1xxxx_gpio_resume' defined but not used [-Werror=unused-function] 311 | static int pci1xxxx_gpio_resume(struct device *dev) | ^~~~~~~~~~~~~~~~~~~~ drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c:295:12: error: 'pci1xxxx_gpio_suspend' defined but not used [-Werror=unused-function] 295 | static int pci1xxxx_gpio_suspend(struct device *dev) | ^~~~~~~~~~~~~~~~~~~~~ Fixes: 4ec7ac90ff39 ("misc: microchip: pci1xxxx: Add power management functions - suspend & resume handlers.") Reported-by: Sudip Mukherjee Signed-off-by: Kumaravel Thiagarajan Link: https://lore.kernel.org/r/20220915094729.646185-1-kumaravel.thiagarajan@microchip.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c index 3cda6e679cc7..3389803cb281 100644 --- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c @@ -404,7 +404,7 @@ static int pci1xxxx_gpio_probe(struct auxiliary_device *aux_dev, return devm_gpiochip_add_data(&aux_dev->dev, &priv->gpio, priv); } -static SIMPLE_DEV_PM_OPS(pci1xxxx_gpio_pm_ops, pci1xxxx_gpio_suspend, pci1xxxx_gpio_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(pci1xxxx_gpio_pm_ops, pci1xxxx_gpio_suspend, pci1xxxx_gpio_resume); static const struct auxiliary_device_id pci1xxxx_gpio_auxiliary_id_table[] = { {.name = "mchp_pci1xxxx_gp.gp_gpio"}, -- cgit v1.2.3 From c8b4747569eba7e5f4835e027d294486534ed0d3 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 18 Sep 2022 08:27:24 +0200 Subject: misc: microchip: pci1xxxx: Do not disable the pci device twice in gp_aux_bus_remove() gp_aux_bus_probe() uses pcim_enable_device(), so there is no point in calling pci_disable_device() explicitly in the remove function. Fixes: 393fc2f5948f ("misc: microchip: pci1xxxx: load auxiliary bus driver for the PIO function in the multi-function endpoint of pci1xxxx device.") Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/8a3a385b3ae15ee7497469ec3250302b626a018b.1663482259.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c index edff3ee73f6f..6c4f8384aa09 100644 --- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c @@ -139,7 +139,6 @@ static void gp_aux_bus_remove(struct pci_dev *pdev) auxiliary_device_delete(&aux_bus->aux_device_wrapper[1]->aux_dev); auxiliary_device_uninit(&aux_bus->aux_device_wrapper[1]->aux_dev); kfree(aux_bus); - pci_disable_device(pdev); } static const struct pci_device_id pci1xxxx_tbl[] = { -- cgit v1.2.3 From 62e5d00684ef81b4be35d23e54eec92ee38db7b8 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 18 Sep 2022 08:27:33 +0200 Subject: misc: microchip: pci1xxxx: Fix a memory leak in the error handling of gp_aux_bus_probe() 'aux_bus' is freed in the remove function but not in the error handling path of the probe. Use devm_kzalloc() to simplify the remove function and fix the leak in the probe. Fixes: 393fc2f5948f ("misc: microchip: pci1xxxx: load auxiliary bus driver for the PIO function in the multi-function endpoint of pci1xxxx device.") Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/17e19926669a1654e5f2495bf3b289581183d02e.1663482259.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c index 6c4f8384aa09..32af2b14ff34 100644 --- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c @@ -38,7 +38,7 @@ static int gp_aux_bus_probe(struct pci_dev *pdev, const struct pci_device_id *id if (retval) return retval; - aux_bus = kzalloc(sizeof(*aux_bus), GFP_KERNEL); + aux_bus = devm_kzalloc(&pdev->dev, sizeof(*aux_bus), GFP_KERNEL); if (!aux_bus) return -ENOMEM; @@ -138,7 +138,6 @@ static void gp_aux_bus_remove(struct pci_dev *pdev) auxiliary_device_uninit(&aux_bus->aux_device_wrapper[0]->aux_dev); auxiliary_device_delete(&aux_bus->aux_device_wrapper[1]->aux_dev); auxiliary_device_uninit(&aux_bus->aux_device_wrapper[1]->aux_dev); - kfree(aux_bus); } static const struct pci_device_id pci1xxxx_tbl[] = { -- cgit v1.2.3 From 5038d21dde818fe74ba1fcb6f2cee35b8c2ebbf2 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 16 Sep 2022 13:29:07 +0100 Subject: slimbus: qcom-ngd: use correct error in message of pdr_add_lookup() failure Use correct error code, instead of previous 'ret' value, when printing error from pdr_add_lookup() failure. Fixes: e1ae85e1830e ("slimbus: qcom-ngd-ctrl: add Protection Domain Restart Support") Cc: Signed-off-by: Krzysztof Kozlowski Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220916122910.170730-2-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/slimbus/qcom-ngd-ctrl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c index 0aa8408464ad..f4f330b9fa72 100644 --- a/drivers/slimbus/qcom-ngd-ctrl.c +++ b/drivers/slimbus/qcom-ngd-ctrl.c @@ -1581,8 +1581,9 @@ static int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev) pds = pdr_add_lookup(ctrl->pdr, "avs/audio", "msm/adsp/audio_pd"); if (IS_ERR(pds) && PTR_ERR(pds) != -EALREADY) { + ret = PTR_ERR(pds); dev_err(dev, "pdr add lookup failed: %d\n", ret); - return PTR_ERR(pds); + return ret; } platform_driver_register(&qcom_slim_ngd_driver); -- cgit v1.2.3 From 16f14551d0df9e7cd283545d7d748829594d912f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 16 Sep 2022 13:29:08 +0100 Subject: slimbus: qcom-ngd: cleanup in probe error path Add proper error path in probe() to cleanup resources previously acquired/allocated to fix warnings visible during probe deferral: notifier callback qcom_slim_ngd_ssr_notify already registered WARNING: CPU: 6 PID: 70 at kernel/notifier.c:28 notifier_chain_register+0x5c/0x90 Modules linked in: CPU: 6 PID: 70 Comm: kworker/u16:1 Not tainted 6.0.0-rc3-next-20220830 #380 Call trace: notifier_chain_register+0x5c/0x90 srcu_notifier_chain_register+0x44/0x90 qcom_register_ssr_notifier+0x38/0x4c qcom_slim_ngd_ctrl_probe+0xd8/0x400 platform_probe+0x6c/0xe0 really_probe+0xbc/0x2d4 __driver_probe_device+0x78/0xe0 driver_probe_device+0x3c/0x12c __device_attach_driver+0xb8/0x120 bus_for_each_drv+0x78/0xd0 __device_attach+0xa8/0x1c0 device_initial_probe+0x18/0x24 bus_probe_device+0xa0/0xac deferred_probe_work_func+0x88/0xc0 process_one_work+0x1d4/0x320 worker_thread+0x2cc/0x44c kthread+0x110/0x114 ret_from_fork+0x10/0x20 Fixes: e1ae85e1830e ("slimbus: qcom-ngd-ctrl: add Protection Domain Restart Support") Cc: Signed-off-by: Krzysztof Kozlowski Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220916122910.170730-3-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/slimbus/qcom-ngd-ctrl.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c index f4f330b9fa72..bacc6af1d51e 100644 --- a/drivers/slimbus/qcom-ngd-ctrl.c +++ b/drivers/slimbus/qcom-ngd-ctrl.c @@ -1576,18 +1576,27 @@ static int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev) ctrl->pdr = pdr_handle_alloc(slim_pd_status, ctrl); if (IS_ERR(ctrl->pdr)) { dev_err(dev, "Failed to init PDR handle\n"); - return PTR_ERR(ctrl->pdr); + ret = PTR_ERR(ctrl->pdr); + goto err_pdr_alloc; } pds = pdr_add_lookup(ctrl->pdr, "avs/audio", "msm/adsp/audio_pd"); if (IS_ERR(pds) && PTR_ERR(pds) != -EALREADY) { ret = PTR_ERR(pds); dev_err(dev, "pdr add lookup failed: %d\n", ret); - return ret; + goto err_pdr_lookup; } platform_driver_register(&qcom_slim_ngd_driver); return of_qcom_slim_ngd_register(dev, ctrl); + +err_pdr_alloc: + qcom_unregister_ssr_notifier(ctrl->notifier, &ctrl->nb); + +err_pdr_lookup: + pdr_handle_release(ctrl->pdr); + + return ret; } static int qcom_slim_ngd_ctrl_remove(struct platform_device *pdev) -- cgit v1.2.3 From e7a3c8b9b1c212462332170a81353eea992762aa Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 16 Sep 2022 13:29:09 +0100 Subject: slimbus: qcom-ngd: simplify error paths with dev_err_probe Use dev_err_probe to skip printing of deferred probe errors and to simplify error paths. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220916122910.170730-4-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/slimbus/qcom-ngd-ctrl.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c index bacc6af1d51e..cec11aa106bf 100644 --- a/drivers/slimbus/qcom-ngd-ctrl.c +++ b/drivers/slimbus/qcom-ngd-ctrl.c @@ -1543,10 +1543,8 @@ static int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev) ret = devm_request_irq(dev, ret, qcom_slim_ngd_interrupt, IRQF_TRIGGER_HIGH, "slim-ngd", ctrl); - if (ret) { - dev_err(&pdev->dev, "request IRQ failed\n"); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "request IRQ failed\n"); ctrl->nb.notifier_call = qcom_slim_ngd_ssr_notify; ctrl->notifier = qcom_register_ssr_notifier("lpass", &ctrl->nb); @@ -1575,15 +1573,14 @@ static int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev) ctrl->pdr = pdr_handle_alloc(slim_pd_status, ctrl); if (IS_ERR(ctrl->pdr)) { - dev_err(dev, "Failed to init PDR handle\n"); - ret = PTR_ERR(ctrl->pdr); + ret = dev_err_probe(dev, PTR_ERR(ctrl->pdr), + "Failed to init PDR handle\n"); goto err_pdr_alloc; } pds = pdr_add_lookup(ctrl->pdr, "avs/audio", "msm/adsp/audio_pd"); if (IS_ERR(pds) && PTR_ERR(pds) != -EALREADY) { - ret = PTR_ERR(pds); - dev_err(dev, "pdr add lookup failed: %d\n", ret); + ret = dev_err_probe(dev, PTR_ERR(pds), "pdr add lookup failed\n"); goto err_pdr_lookup; } -- cgit v1.2.3 From e291691c69776ad278cd39dec2306dd39d681a9f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 16 Sep 2022 13:29:10 +0100 Subject: slimbus: qcom-ngd-ctrl: allow compile testing without QCOM_RPROC_COMMON The Qualcomm common remote-proc code (CONFIG_QCOM_RPROC_COMMON) has necessary stubs, so it is not needed for compile testing. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220916122910.170730-5-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/slimbus/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/slimbus/Kconfig b/drivers/slimbus/Kconfig index 1235b7dc8496..2ed821f75816 100644 --- a/drivers/slimbus/Kconfig +++ b/drivers/slimbus/Kconfig @@ -22,7 +22,8 @@ config SLIM_QCOM_CTRL config SLIM_QCOM_NGD_CTRL tristate "Qualcomm SLIMbus Satellite Non-Generic Device Component" - depends on HAS_IOMEM && DMA_ENGINE && NET && QCOM_RPROC_COMMON + depends on HAS_IOMEM && DMA_ENGINE && NET + depends on QCOM_RPROC_COMMON || COMPILE_TEST depends on ARCH_QCOM || COMPILE_TEST select QCOM_QMI_HELPERS select QCOM_PDR_HELPERS -- cgit v1.2.3 From bd1244561fa2a4531ded40dbf09c9599084f8b29 Mon Sep 17 00:00:00 2001 From: Gaosheng Cui Date: Fri, 16 Sep 2022 13:04:02 +0100 Subject: nvmem: core: Fix memleak in nvmem_register() dev_set_name will alloc memory for nvmem->dev.kobj.name in nvmem_register, when nvmem_validate_keepouts failed, nvmem's memory will be freed and return, but nobody will free memory for nvmem->dev.kobj.name, there will be memleak, so moving nvmem_validate_keepouts() after device_register() and let the device core deal with cleaning name in error cases. Fixes: de0534df9347 ("nvmem: core: fix error handling while validating keepout regions") Cc: stable@vger.kernel.org Signed-off-by: Gaosheng Cui Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220916120402.38753-1-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 1e3c754efd0d..2164efd12ba9 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -829,21 +829,18 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) nvmem->dev.groups = nvmem_dev_groups; #endif - if (nvmem->nkeepout) { - rval = nvmem_validate_keepouts(nvmem); - if (rval) { - ida_free(&nvmem_ida, nvmem->id); - kfree(nvmem); - return ERR_PTR(rval); - } - } - dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name); rval = device_register(&nvmem->dev); if (rval) goto err_put_device; + if (nvmem->nkeepout) { + rval = nvmem_validate_keepouts(nvmem); + if (rval) + goto err_device_del; + } + if (config->compat) { rval = nvmem_sysfs_setup_compat(nvmem, config); if (rval) -- cgit v1.2.3 From 42992cf187e4e4bcfe3c58f8fc7b1832c5652d9f Mon Sep 17 00:00:00 2001 From: Lin Yujun Date: Wed, 14 Sep 2022 11:19:53 +0800 Subject: slimbus: qcom-ngd: Add error handling in of_qcom_slim_ngd_register No error handling is performed when platform_device_add() return fails. Refer to the error handling of driver_set_override(), add error handling for platform_device_add(). Fixes: 917809e2280b ("slimbus: ngd: Add qcom SLIMBus NGD driver") Reviewed-by: Neil Armstrong Signed-off-by: Lin Yujun Link: https://lore.kernel.org/r/20220914031953.94061-1-linyujun809@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/slimbus/qcom-ngd-ctrl.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c index cec11aa106bf..76c5e446d243 100644 --- a/drivers/slimbus/qcom-ngd-ctrl.c +++ b/drivers/slimbus/qcom-ngd-ctrl.c @@ -1470,7 +1470,13 @@ static int of_qcom_slim_ngd_register(struct device *parent, ngd->pdev->dev.of_node = node; ctrl->ngd = ngd; - platform_device_add(ngd->pdev); + ret = platform_device_add(ngd->pdev); + if (ret) { + platform_device_put(ngd->pdev); + kfree(ngd); + of_node_put(node); + return ret; + } ngd->base = ctrl->base + ngd->id * data->offset + (ngd->id - 1) * data->size; -- cgit v1.2.3 From d5542923f200f95bddf524f36fd495f78aa28e3c Mon Sep 17 00:00:00 2001 From: RafaÅ‚ MiÅ‚ecki Date: Fri, 16 Sep 2022 13:20:48 +0100 Subject: nvmem: add driver handling U-Boot environment variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit U-Boot stores its setup as environment variables. It's a list of key-value pairs stored on flash device with a custom header. This commit adds an NVMEM driver that: 1. Provides NVMEM access to environment vars binary data 2. Extracts variables as NVMEM cells Current Linux's NVMEM sysfs API allows reading whole NVMEM data block. It can be used by user-space tools for reading U-Boot env vars block without the hassle of finding its location. Parsing will still need to be re-done there. Kernel-parsed NVMEM cells can be read however by Linux drivers. This may be useful for Ethernet drivers for reading device MAC address which is often stored as U-Boot env variable. Reviewed-by: Ahmad Fatoum Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220916122100.170016-2-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + drivers/nvmem/Kconfig | 13 +++ drivers/nvmem/Makefile | 2 + drivers/nvmem/u-boot-env.c | 218 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 234 insertions(+) create mode 100644 drivers/nvmem/u-boot-env.c diff --git a/MAINTAINERS b/MAINTAINERS index 747781ec4aa5..750e62bab132 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20754,6 +20754,7 @@ U-BOOT ENVIRONMENT VARIABLES M: RafaÅ‚ MiÅ‚ecki S: Maintained F: Documentation/devicetree/bindings/nvmem/u-boot,env.yaml +F: drivers/nvmem/u-boot-env.c UACCE ACCELERATOR FRAMEWORK M: Zhangfei Gao diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index d72d879a6d34..bab8a29c9861 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -344,4 +344,17 @@ config NVMEM_APPLE_EFUSES This driver can also be built as a module. If so, the module will be called nvmem-apple-efuses. +config NVMEM_U_BOOT_ENV + tristate "U-Boot environment variables support" + depends on OF && MTD + select CRC32 + help + U-Boot stores its setup as environment variables. This driver adds + support for verifying & exporting such data. It also exposes variables + as NVMEM cells so they can be referenced by other drivers. + + Currently this drivers works only with env variables on top of MTD. + + If compiled as module it will be called nvmem_u-boot-env. + endif diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index c710b64f9fe4..399f9972d45b 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -69,3 +69,5 @@ obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvmem-apple-efuses.o nvmem-apple-efuses-y := apple-efuses.o obj-$(CONFIG_MICROCHIP_OTPC) += nvmem-microchip-otpc.o nvmem-microchip-otpc-y := microchip-otpc.o +obj-$(CONFIG_NVMEM_U_BOOT_ENV) += nvmem_u-boot-env.o +nvmem_u-boot-env-y := u-boot-env.o diff --git a/drivers/nvmem/u-boot-env.c b/drivers/nvmem/u-boot-env.c new file mode 100644 index 000000000000..9b9abfb8f187 --- /dev/null +++ b/drivers/nvmem/u-boot-env.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 RafaÅ‚ MiÅ‚ecki + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum u_boot_env_format { + U_BOOT_FORMAT_SINGLE, + U_BOOT_FORMAT_REDUNDANT, +}; + +struct u_boot_env { + struct device *dev; + enum u_boot_env_format format; + + struct mtd_info *mtd; + + /* Cells */ + struct nvmem_cell_info *cells; + int ncells; +}; + +struct u_boot_env_image_single { + __le32 crc32; + uint8_t data[]; +} __packed; + +struct u_boot_env_image_redundant { + __le32 crc32; + u8 mark; + uint8_t data[]; +} __packed; + +static int u_boot_env_read(void *context, unsigned int offset, void *val, + size_t bytes) +{ + struct u_boot_env *priv = context; + struct device *dev = priv->dev; + size_t bytes_read; + int err; + + err = mtd_read(priv->mtd, offset, bytes, &bytes_read, val); + if (err && !mtd_is_bitflip(err)) { + dev_err(dev, "Failed to read from mtd: %d\n", err); + return err; + } + + if (bytes_read != bytes) { + dev_err(dev, "Failed to read %zu bytes\n", bytes); + return -EIO; + } + + return 0; +} + +static int u_boot_env_add_cells(struct u_boot_env *priv, uint8_t *buf, + size_t data_offset, size_t data_len) +{ + struct device *dev = priv->dev; + char *data = buf + data_offset; + char *var, *value, *eq; + int idx; + + priv->ncells = 0; + for (var = data; var < data + data_len && *var; var += strlen(var) + 1) + priv->ncells++; + + priv->cells = devm_kcalloc(dev, priv->ncells, sizeof(*priv->cells), GFP_KERNEL); + if (!priv->cells) + return -ENOMEM; + + for (var = data, idx = 0; + var < data + data_len && *var; + var = value + strlen(value) + 1, idx++) { + eq = strchr(var, '='); + if (!eq) + break; + *eq = '\0'; + value = eq + 1; + + priv->cells[idx].name = devm_kstrdup(dev, var, GFP_KERNEL); + if (!priv->cells[idx].name) + return -ENOMEM; + priv->cells[idx].offset = data_offset + value - data; + priv->cells[idx].bytes = strlen(value); + } + + if (WARN_ON(idx != priv->ncells)) + priv->ncells = idx; + + return 0; +} + +static int u_boot_env_parse(struct u_boot_env *priv) +{ + struct device *dev = priv->dev; + size_t crc32_data_offset; + size_t crc32_data_len; + size_t crc32_offset; + size_t data_offset; + size_t data_len; + uint32_t crc32; + uint32_t calc; + size_t bytes; + uint8_t *buf; + int err; + + buf = kcalloc(1, priv->mtd->size, GFP_KERNEL); + if (!buf) { + err = -ENOMEM; + goto err_out; + } + + err = mtd_read(priv->mtd, 0, priv->mtd->size, &bytes, buf); + if ((err && !mtd_is_bitflip(err)) || bytes != priv->mtd->size) { + dev_err(dev, "Failed to read from mtd: %d\n", err); + goto err_kfree; + } + + switch (priv->format) { + case U_BOOT_FORMAT_SINGLE: + crc32_offset = offsetof(struct u_boot_env_image_single, crc32); + crc32_data_offset = offsetof(struct u_boot_env_image_single, data); + data_offset = offsetof(struct u_boot_env_image_single, data); + break; + case U_BOOT_FORMAT_REDUNDANT: + crc32_offset = offsetof(struct u_boot_env_image_redundant, crc32); + crc32_data_offset = offsetof(struct u_boot_env_image_redundant, mark); + data_offset = offsetof(struct u_boot_env_image_redundant, data); + break; + } + crc32 = le32_to_cpu(*(uint32_t *)(buf + crc32_offset)); + crc32_data_len = priv->mtd->size - crc32_data_offset; + data_len = priv->mtd->size - data_offset; + + calc = crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L; + if (calc != crc32) { + dev_err(dev, "Invalid calculated CRC32: 0x%08x (expected: 0x%08x)\n", calc, crc32); + err = -EINVAL; + goto err_kfree; + } + + buf[priv->mtd->size - 1] = '\0'; + err = u_boot_env_add_cells(priv, buf, data_offset, data_len); + if (err) + dev_err(dev, "Failed to add cells: %d\n", err); + +err_kfree: + kfree(buf); +err_out: + return err; +} + +static int u_boot_env_probe(struct platform_device *pdev) +{ + struct nvmem_config config = { + .name = "u-boot-env", + .reg_read = u_boot_env_read, + }; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct u_boot_env *priv; + int err; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->dev = dev; + + priv->format = (uintptr_t)of_device_get_match_data(dev); + + priv->mtd = of_get_mtd_device_by_node(np); + if (IS_ERR(priv->mtd)) { + dev_err_probe(dev, PTR_ERR(priv->mtd), "Failed to get %pOF MTD\n", np); + return PTR_ERR(priv->mtd); + } + + err = u_boot_env_parse(priv); + if (err) + return err; + + config.dev = dev; + config.cells = priv->cells; + config.ncells = priv->ncells; + config.priv = priv; + config.size = priv->mtd->size; + + return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config)); +} + +static const struct of_device_id u_boot_env_of_match_table[] = { + { .compatible = "u-boot,env", .data = (void *)U_BOOT_FORMAT_SINGLE, }, + { .compatible = "u-boot,env-redundant-bool", .data = (void *)U_BOOT_FORMAT_REDUNDANT, }, + { .compatible = "u-boot,env-redundant-count", .data = (void *)U_BOOT_FORMAT_REDUNDANT, }, + {}, +}; + +static struct platform_driver u_boot_env_driver = { + .probe = u_boot_env_probe, + .driver = { + .name = "u_boot_env", + .of_match_table = u_boot_env_of_match_table, + }, +}; +module_platform_driver(u_boot_env_driver); + +MODULE_AUTHOR("RafaÅ‚ MiÅ‚ecki"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(of, u_boot_env_of_match_table); -- cgit v1.2.3 From 4a575865c1ea67018d96acda9b43e5d3d25b2366 Mon Sep 17 00:00:00 2001 From: RafaÅ‚ MiÅ‚ecki Date: Fri, 16 Sep 2022 13:20:49 +0100 Subject: mtd: allow getting MTD device associated with a specific DT node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MTD subsystem API allows interacting with MTD devices (e.g. reading, writing, handling bad blocks). So far a random driver could get MTD device only by its name (get_mtd_device_nm()). This change allows getting them also by a DT node. This API is required for drivers handling DT defined MTD partitions in a specific way (e.g. U-Boot (sub)partition with environment variables). Acked-by: Miquel Raynal Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220916122100.170016-3-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/mtdcore.c | 28 ++++++++++++++++++++++++++++ include/linux/mtd/mtd.h | 1 + 2 files changed, 29 insertions(+) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index a9b8be9f40dc..e3bee273595e 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1217,6 +1217,34 @@ int __get_mtd_device(struct mtd_info *mtd) } EXPORT_SYMBOL_GPL(__get_mtd_device); +/** + * of_get_mtd_device_by_node - obtain an MTD device associated with a given node + * + * @np: device tree node + */ +struct mtd_info *of_get_mtd_device_by_node(struct device_node *np) +{ + struct mtd_info *mtd = NULL; + struct mtd_info *tmp; + int err; + + mutex_lock(&mtd_table_mutex); + + err = -EPROBE_DEFER; + mtd_for_each_device(tmp) { + if (mtd_get_of_node(tmp) == np) { + mtd = tmp; + err = __get_mtd_device(mtd); + break; + } + } + + mutex_unlock(&mtd_table_mutex); + + return err ? ERR_PTR(err) : mtd; +} +EXPORT_SYMBOL_GPL(of_get_mtd_device_by_node); + /** * get_mtd_device_nm - obtain a validated handle for an MTD device by * device name diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 955aee14b0f7..6fc841ceef31 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -677,6 +677,7 @@ extern int mtd_device_unregister(struct mtd_info *master); extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num); extern int __get_mtd_device(struct mtd_info *mtd); extern void __put_mtd_device(struct mtd_info *mtd); +extern struct mtd_info *of_get_mtd_device_by_node(struct device_node *np); extern struct mtd_info *get_mtd_device_nm(const char *name); extern void put_mtd_device(struct mtd_info *mtd); -- cgit v1.2.3 From 5544e90c81261e82e02bbf7c6015a4b9c8c825ef Mon Sep 17 00:00:00 2001 From: Gaosheng Cui Date: Fri, 16 Sep 2022 13:20:50 +0100 Subject: nvmem: core: add error handling for dev_set_name The type of return value of dev_set_name is int, which may return wrong result, so we add error handling for it to reclaim memory of nvmem resource, and return early when an error occurs. Signed-off-by: Gaosheng Cui Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220916122100.170016-4-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 2164efd12ba9..321d7d63e068 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -810,18 +810,24 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) switch (config->id) { case NVMEM_DEVID_NONE: - dev_set_name(&nvmem->dev, "%s", config->name); + rval = dev_set_name(&nvmem->dev, "%s", config->name); break; case NVMEM_DEVID_AUTO: - dev_set_name(&nvmem->dev, "%s%d", config->name, nvmem->id); + rval = dev_set_name(&nvmem->dev, "%s%d", config->name, nvmem->id); break; default: - dev_set_name(&nvmem->dev, "%s%d", + rval = dev_set_name(&nvmem->dev, "%s%d", config->name ? : "nvmem", config->name ? config->id : nvmem->id); break; } + if (rval) { + ida_free(&nvmem_ida, nvmem->id); + kfree(nvmem); + return ERR_PTR(rval); + } + nvmem->read_only = device_property_present(config->dev, "read-only") || config->read_only || !nvmem->reg_write; -- cgit v1.2.3 From ff1df1886f43365f2333770f89a7b435424897f4 Mon Sep 17 00:00:00 2001 From: Johnson Wang Date: Fri, 16 Sep 2022 13:20:51 +0100 Subject: dt-bindings: nvmem: mediatek: efuse: Add support for MT8188 Add compatible for MT8188 SoC. Acked-by: Krzysztof Kozlowski Signed-off-by: Johnson Wang Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220916122100.170016-5-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/nvmem/mediatek,efuse.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/nvmem/mediatek,efuse.yaml b/Documentation/devicetree/bindings/nvmem/mediatek,efuse.yaml index b5a1109f2ee1..75e0a516e59a 100644 --- a/Documentation/devicetree/bindings/nvmem/mediatek,efuse.yaml +++ b/Documentation/devicetree/bindings/nvmem/mediatek,efuse.yaml @@ -30,6 +30,7 @@ properties: - mediatek,mt8173-efuse - mediatek,mt8183-efuse - mediatek,mt8186-efuse + - mediatek,mt8188-efuse - mediatek,mt8192-efuse - mediatek,mt8195-efuse - mediatek,mt8516-efuse -- cgit v1.2.3 From d3524bb5b9a0c567b853a0024526afe87dde01ed Mon Sep 17 00:00:00 2001 From: Kenneth Lee Date: Fri, 16 Sep 2022 13:20:52 +0100 Subject: nvmem: brcm_nvram: Use kzalloc for allocating only one element Use kzalloc(...) rather than kcalloc(1, ...) because the number of elements we are specifying in this case is 1, so kzalloc would accomplish the same thing and we can simplify. Signed-off-by: Kenneth Lee Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220916122100.170016-6-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/brcm_nvram.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvmem/brcm_nvram.c b/drivers/nvmem/brcm_nvram.c index 450b927691c3..4441daa20965 100644 --- a/drivers/nvmem/brcm_nvram.c +++ b/drivers/nvmem/brcm_nvram.c @@ -96,7 +96,7 @@ static int brcm_nvram_parse(struct brcm_nvram *priv) len = le32_to_cpu(header.len); - data = kcalloc(1, len, GFP_KERNEL); + data = kzalloc(len, GFP_KERNEL); memcpy_fromio(data, priv->base, len); data[len - 1] = '\0'; -- cgit v1.2.3 From 105ca4190dcf15903cc2b503df7ce473715c432f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 16 Sep 2022 13:20:53 +0100 Subject: dt-bindings: nvmem: qfprom: add IPQ8064 and SDM630 compatibles Document compatibles for QFPROM used on IPQ8064 and SDM630. They are compatible with generic QFPROM fallback. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220916122100.170016-7-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml index dede8892ee01..b4163086a5be 100644 --- a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml +++ b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml @@ -18,6 +18,7 @@ properties: - enum: - qcom,apq8064-qfprom - qcom,apq8084-qfprom + - qcom,ipq8064-qfprom - qcom,msm8974-qfprom - qcom,msm8916-qfprom - qcom,msm8996-qfprom @@ -25,6 +26,7 @@ properties: - qcom,qcs404-qfprom - qcom,sc7180-qfprom - qcom,sc7280-qfprom + - qcom,sdm630-qfprom - qcom,sdm845-qfprom - const: qcom,qfprom -- cgit v1.2.3 From 28fc7c986f01fdcfd28af648be2597624cac0e27 Mon Sep 17 00:00:00 2001 From: RafaÅ‚ MiÅ‚ecki Date: Fri, 16 Sep 2022 13:20:54 +0100 Subject: nvmem: prefix all symbols with NVMEM_ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This unifies all NVMEM symbols. They follow one style now. Reviewed-by: Matthias Brugger Acked-by: Arnd Bergmann Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220916122100.170016-8-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- arch/arm/configs/multi_v7_defconfig | 6 +++--- arch/arm/configs/qcom_defconfig | 2 +- arch/arm64/configs/defconfig | 10 +++++----- arch/mips/configs/ci20_defconfig | 2 +- drivers/cpufreq/Kconfig.arm | 2 +- drivers/nvmem/Kconfig | 24 ++++++++++++------------ drivers/nvmem/Makefile | 24 ++++++++++++------------ drivers/soc/mediatek/Kconfig | 2 +- drivers/thermal/qcom/Kconfig | 2 +- 9 files changed, 37 insertions(+), 37 deletions(-) diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index 12b35008571f..e52edcc8ec41 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -1193,11 +1193,11 @@ CONFIG_TI_PIPE3=y CONFIG_TWL4030_USB=m CONFIG_RAS=y CONFIG_NVMEM_IMX_OCOTP=y -CONFIG_QCOM_QFPROM=y -CONFIG_ROCKCHIP_EFUSE=m +CONFIG_NVMEM_QCOM_QFPROM=y +CONFIG_NVMEM_ROCKCHIP_EFUSE=m CONFIG_NVMEM_SUNXI_SID=y CONFIG_NVMEM_VF610_OCOTP=y -CONFIG_MESON_MX_EFUSE=m +CONFIG_NVMEM_MESON_MX_EFUSE=m CONFIG_NVMEM_RMEM=m CONFIG_FSI=m CONFIG_FSI_MASTER_GPIO=m diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig index 8a59441701a8..fb8c03bd80d7 100644 --- a/arch/arm/configs/qcom_defconfig +++ b/arch/arm/configs/qcom_defconfig @@ -278,7 +278,7 @@ CONFIG_PHY_QCOM_QMP=y CONFIG_PHY_QCOM_USB_HS=y CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2=y CONFIG_PHY_QCOM_USB_HSIC=y -CONFIG_QCOM_QFPROM=y +CONFIG_NVMEM_QCOM_QFPROM=y CONFIG_INTERCONNECT=y CONFIG_INTERCONNECT_QCOM=y CONFIG_INTERCONNECT_QCOM_MSM8974=m diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index d5b2d2dd4904..c6e82787cca3 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -1236,12 +1236,12 @@ CONFIG_QCOM_L3_PMU=y CONFIG_HISI_PMU=y CONFIG_NVMEM_IMX_OCOTP=y CONFIG_NVMEM_IMX_OCOTP_SCU=y -CONFIG_MTK_EFUSE=y -CONFIG_QCOM_QFPROM=y -CONFIG_ROCKCHIP_EFUSE=y +CONFIG_NVMEM_MTK_EFUSE=y +CONFIG_NVMEM_QCOM_QFPROM=y +CONFIG_NVMEM_ROCKCHIP_EFUSE=y CONFIG_NVMEM_SUNXI_SID=y -CONFIG_UNIPHIER_EFUSE=y -CONFIG_MESON_EFUSE=m +CONFIG_NVMEM_UNIPHIER_EFUSE=y +CONFIG_NVMEM_MESON_EFUSE=m CONFIG_NVMEM_RMEM=m CONFIG_NVMEM_LAYERSCAPE_SFP=m CONFIG_FPGA=y diff --git a/arch/mips/configs/ci20_defconfig b/arch/mips/configs/ci20_defconfig index cc69b215854e..e1b49f77414a 100644 --- a/arch/mips/configs/ci20_defconfig +++ b/arch/mips/configs/ci20_defconfig @@ -143,7 +143,7 @@ CONFIG_MEMORY=y CONFIG_JZ4780_NEMC=y CONFIG_PWM=y CONFIG_PWM_JZ4740=m -CONFIG_JZ4780_EFUSE=y +CONFIG_NVMEM_JZ4780_EFUSE=y CONFIG_JZ4770_PHY=y CONFIG_EXT4_FS=y # CONFIG_DNOTIFY is not set diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 954749afb5fe..82e5de1f6f8c 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -153,7 +153,7 @@ config ARM_OMAP2PLUS_CPUFREQ config ARM_QCOM_CPUFREQ_NVMEM tristate "Qualcomm nvmem based CPUFreq" depends on ARCH_QCOM - depends on QCOM_QFPROM + depends on NVMEM_QCOM_QFPROM depends on QCOM_SMEM select PM_OPP help diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index bab8a29c9861..691375c13381 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -52,7 +52,7 @@ config NVMEM_IMX_OCOTP_SCU This is a driver for the SCU On-Chip OTP Controller (OCOTP) available on i.MX8 SoCs. -config JZ4780_EFUSE +config NVMEM_JZ4780_EFUSE tristate "JZ4780 EFUSE Memory Support" depends on MACH_INGENIC || COMPILE_TEST depends on HAS_IOMEM @@ -96,7 +96,7 @@ config NVMEM_MXS_OCOTP This driver can also be built as a module. If so, the module will be called nvmem-mxs-ocotp. -config MTK_EFUSE +config NVMEM_MTK_EFUSE tristate "Mediatek SoCs EFUSE support" depends on ARCH_MEDIATEK || COMPILE_TEST depends on HAS_IOMEM @@ -107,7 +107,7 @@ config MTK_EFUSE This driver can also be built as a module. If so, the module will be called efuse-mtk. -config MICROCHIP_OTPC +config NVMEM_MICROCHIP_OTPC tristate "Microchip OTPC support" depends on ARCH_AT91 || COMPILE_TEST help @@ -126,7 +126,7 @@ config NVMEM_NINTENDO_OTP This driver can also be built as a module. If so, the module will be called nvmem-nintendo-otp. -config QCOM_QFPROM +config NVMEM_QCOM_QFPROM tristate "QCOM QFPROM Support" depends on ARCH_QCOM || COMPILE_TEST depends on HAS_IOMEM @@ -145,7 +145,7 @@ config NVMEM_SPMI_SDAM Qualcomm Technologies, Inc. PMICs. It provides the clients an interface to read/write to the SDAM module's shared memory. -config ROCKCHIP_EFUSE +config NVMEM_ROCKCHIP_EFUSE tristate "Rockchip eFuse Support" depends on ARCH_ROCKCHIP || COMPILE_TEST depends on HAS_IOMEM @@ -156,7 +156,7 @@ config ROCKCHIP_EFUSE This driver can also be built as a module. If so, the module will be called nvmem_rockchip_efuse. -config ROCKCHIP_OTP +config NVMEM_ROCKCHIP_OTP tristate "Rockchip OTP controller support" depends on ARCH_ROCKCHIP || COMPILE_TEST depends on HAS_IOMEM @@ -199,7 +199,7 @@ config NVMEM_SUNXI_SID This driver can also be built as a module. If so, the module will be called nvmem_sunxi_sid. -config UNIPHIER_EFUSE +config NVMEM_UNIPHIER_EFUSE tristate "UniPhier SoCs eFuse support" depends on ARCH_UNIPHIER || COMPILE_TEST depends on HAS_IOMEM @@ -221,7 +221,7 @@ config NVMEM_VF610_OCOTP This driver can also be build as a module. If so, the module will be called nvmem-vf610-ocotp. -config MESON_EFUSE +config NVMEM_MESON_EFUSE tristate "Amlogic Meson GX eFuse Support" depends on (ARCH_MESON || COMPILE_TEST) && MESON_SM help @@ -231,7 +231,7 @@ config MESON_EFUSE This driver can also be built as a module. If so, the module will be called nvmem_meson_efuse. -config MESON_MX_EFUSE +config NVMEM_MESON_MX_EFUSE tristate "Amlogic Meson6/Meson8/Meson8b eFuse Support" depends on ARCH_MESON || COMPILE_TEST help @@ -251,13 +251,13 @@ config NVMEM_SNVS_LPGPR This driver can also be built as a module. If so, the module will be called nvmem-snvs-lpgpr. -config RAVE_SP_EEPROM +config NVMEM_RAVE_SP_EEPROM tristate "Rave SP EEPROM Support" depends on RAVE_SP_CORE help Say y here to enable Rave SP EEPROM support. -config SC27XX_EFUSE +config NVMEM_SC27XX_EFUSE tristate "Spreadtrum SC27XX eFuse Support" depends on MFD_SC27XX_PMIC || COMPILE_TEST depends on HAS_IOMEM @@ -278,7 +278,7 @@ config NVMEM_ZYNQMP If sure, say yes. If unsure, say no. -config SPRD_EFUSE +config NVMEM_SPRD_EFUSE tristate "Spreadtrum SoC eFuse Support" depends on ARCH_SPRD || COMPILE_TEST depends on HAS_IOMEM diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index 399f9972d45b..7ac988c6966e 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -15,7 +15,7 @@ obj-$(CONFIG_NVMEM_IMX_OCOTP) += nvmem-imx-ocotp.o nvmem-imx-ocotp-y := imx-ocotp.o obj-$(CONFIG_NVMEM_IMX_OCOTP_SCU) += nvmem-imx-ocotp-scu.o nvmem-imx-ocotp-scu-y := imx-ocotp-scu.o -obj-$(CONFIG_JZ4780_EFUSE) += nvmem_jz4780_efuse.o +obj-$(CONFIG_NVMEM_JZ4780_EFUSE) += nvmem_jz4780_efuse.o nvmem_jz4780_efuse-y := jz4780-efuse.o obj-$(CONFIG_NVMEM_LPC18XX_EEPROM) += nvmem_lpc18xx_eeprom.o nvmem_lpc18xx_eeprom-y := lpc18xx_eeprom.o @@ -25,37 +25,37 @@ obj-$(CONFIG_NVMEM_MXS_OCOTP) += nvmem-mxs-ocotp.o nvmem-mxs-ocotp-y := mxs-ocotp.o obj-$(CONFIG_NVMEM_NINTENDO_OTP) += nvmem-nintendo-otp.o nvmem-nintendo-otp-y := nintendo-otp.o -obj-$(CONFIG_MTK_EFUSE) += nvmem_mtk-efuse.o +obj-$(CONFIG_NVMEM_MTK_EFUSE) += nvmem_mtk-efuse.o nvmem_mtk-efuse-y := mtk-efuse.o -obj-$(CONFIG_QCOM_QFPROM) += nvmem_qfprom.o +obj-$(CONFIG_NVMEM_QCOM_QFPROM) += nvmem_qfprom.o nvmem_qfprom-y := qfprom.o obj-$(CONFIG_NVMEM_SPMI_SDAM) += nvmem_qcom-spmi-sdam.o nvmem_qcom-spmi-sdam-y += qcom-spmi-sdam.o -obj-$(CONFIG_ROCKCHIP_EFUSE) += nvmem_rockchip_efuse.o +obj-$(CONFIG_NVMEM_ROCKCHIP_EFUSE) += nvmem_rockchip_efuse.o nvmem_rockchip_efuse-y := rockchip-efuse.o -obj-$(CONFIG_ROCKCHIP_OTP) += nvmem-rockchip-otp.o +obj-$(CONFIG_NVMEM_ROCKCHIP_OTP) += nvmem-rockchip-otp.o nvmem-rockchip-otp-y := rockchip-otp.o obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o nvmem_stm32_romem-y := stm32-romem.o obj-$(CONFIG_NVMEM_STM32_ROMEM) += nvmem_stm32_romem.o nvmem_sunxi_sid-y := sunxi_sid.o -obj-$(CONFIG_UNIPHIER_EFUSE) += nvmem-uniphier-efuse.o +obj-$(CONFIG_NVMEM_UNIPHIER_EFUSE) += nvmem-uniphier-efuse.o nvmem-uniphier-efuse-y := uniphier-efuse.o obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvmem-vf610-ocotp.o nvmem-vf610-ocotp-y := vf610-ocotp.o -obj-$(CONFIG_MESON_EFUSE) += nvmem_meson_efuse.o +obj-$(CONFIG_NVMEM_MESON_EFUSE) += nvmem_meson_efuse.o nvmem_meson_efuse-y := meson-efuse.o -obj-$(CONFIG_MESON_MX_EFUSE) += nvmem_meson_mx_efuse.o +obj-$(CONFIG_NVMEM_MESON_MX_EFUSE) += nvmem_meson_mx_efuse.o nvmem_meson_mx_efuse-y := meson-mx-efuse.o obj-$(CONFIG_NVMEM_SNVS_LPGPR) += nvmem_snvs_lpgpr.o nvmem_snvs_lpgpr-y := snvs_lpgpr.o -obj-$(CONFIG_RAVE_SP_EEPROM) += nvmem-rave-sp-eeprom.o +obj-$(CONFIG_NVMEM_RAVE_SP_EEPROM) += nvmem-rave-sp-eeprom.o nvmem-rave-sp-eeprom-y := rave-sp-eeprom.o -obj-$(CONFIG_SC27XX_EFUSE) += nvmem-sc27xx-efuse.o +obj-$(CONFIG_NVMEM_SC27XX_EFUSE) += nvmem-sc27xx-efuse.o nvmem-sc27xx-efuse-y := sc27xx-efuse.o obj-$(CONFIG_NVMEM_ZYNQMP) += nvmem_zynqmp_nvmem.o nvmem_zynqmp_nvmem-y := zynqmp_nvmem.o -obj-$(CONFIG_SPRD_EFUSE) += nvmem_sprd_efuse.o +obj-$(CONFIG_NVMEM_SPRD_EFUSE) += nvmem_sprd_efuse.o nvmem_sprd_efuse-y := sprd-efuse.o obj-$(CONFIG_NVMEM_RMEM) += nvmem-rmem.o nvmem-rmem-y := rmem.o @@ -67,7 +67,7 @@ obj-$(CONFIG_NVMEM_SUNPLUS_OCOTP) += nvmem_sunplus_ocotp.o nvmem_sunplus_ocotp-y := sunplus-ocotp.o obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvmem-apple-efuses.o nvmem-apple-efuses-y := apple-efuses.o -obj-$(CONFIG_MICROCHIP_OTPC) += nvmem-microchip-otpc.o +obj-$(CONFIG_NVMEM_MICROCHIP_OTPC) += nvmem-microchip-otpc.o nvmem-microchip-otpc-y := microchip-otpc.o obj-$(CONFIG_NVMEM_U_BOOT_ENV) += nvmem_u-boot-env.o nvmem_u-boot-env-y := u-boot-env.o diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig index 3c3eedea35f7..9b44dc3d9dff 100644 --- a/drivers/soc/mediatek/Kconfig +++ b/drivers/soc/mediatek/Kconfig @@ -75,7 +75,7 @@ config MTK_MMSYS config MTK_SVS tristate "MediaTek Smart Voltage Scaling(SVS)" - depends on MTK_EFUSE && NVMEM + depends on NVMEM_MTK_EFUSE && NVMEM help The Smart Voltage Scaling(SVS) engine is a piece of hardware which has several controllers(banks) for calculating suitable diff --git a/drivers/thermal/qcom/Kconfig b/drivers/thermal/qcom/Kconfig index bfd889422dd3..2c7f3f9a26eb 100644 --- a/drivers/thermal/qcom/Kconfig +++ b/drivers/thermal/qcom/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config QCOM_TSENS tristate "Qualcomm TSENS Temperature Alarm" - depends on QCOM_QFPROM + depends on NVMEM_QCOM_QFPROM depends on ARCH_QCOM || COMPILE_TEST help This enables the thermal sysfs driver for the TSENS device. It shows -- cgit v1.2.3 From a06d9e5a63b7c2f622c908cd9600ce735e70f7c6 Mon Sep 17 00:00:00 2001 From: RafaÅ‚ MiÅ‚ecki Date: Fri, 16 Sep 2022 13:20:55 +0100 Subject: nvmem: sort config symbols alphabetically MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Match what most subsystems do 2. Simplify maintenance a bit 3. Reduce amount of conflicts for new drivers patches While at it unify indent level in Makefile. Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220916122100.170016-9-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/Kconfig | 300 +++++++++++++++++++++++++------------------------ drivers/nvmem/Makefile | 114 +++++++++---------- 2 files changed, 208 insertions(+), 206 deletions(-) diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 691375c13381..7f2557934834 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -21,6 +21,40 @@ config NVMEM_SYSFS This interface is mostly used by userspace applications to read/write directly into nvmem. +# Devices + +config NVMEM_APPLE_EFUSES + tristate "Apple eFuse support" + depends on ARCH_APPLE || COMPILE_TEST + default ARCH_APPLE + help + Say y here to enable support for reading eFuses on Apple SoCs + such as the M1. These are e.g. used to store factory programmed + calibration data required for the PCIe or the USB-C PHY. + + This driver can also be built as a module. If so, the module will + be called nvmem-apple-efuses. + +config NVMEM_BCM_OCOTP + tristate "Broadcom On-Chip OTP Controller support" + depends on ARCH_BCM_IPROC || COMPILE_TEST + depends on HAS_IOMEM + default ARCH_BCM_IPROC + help + Say y here to enable read/write access to the Broadcom OTP + controller. + + This driver can also be built as a module. If so, the module + will be called nvmem-bcm-ocotp. + +config NVMEM_BRCM_NVRAM + tristate "Broadcom's NVRAM support" + depends on ARCH_BCM_5301X || COMPILE_TEST + depends on HAS_IOMEM + help + This driver provides support for Broadcom's NVRAM that can be accessed + using I/O mapping. + config NVMEM_IMX_IIM tristate "i.MX IC Identification Module support" depends on ARCH_MXC || COMPILE_TEST @@ -64,6 +98,19 @@ config NVMEM_JZ4780_EFUSE To compile this driver as a module, choose M here: the module will be called nvmem_jz4780_efuse. +config NVMEM_LAYERSCAPE_SFP + tristate "Layerscape SFP (Security Fuse Processor) support" + depends on ARCH_LAYERSCAPE || COMPILE_TEST + depends on HAS_IOMEM + select REGMAP_MMIO + help + This driver provides support to read the eFuses on Freescale + Layerscape SoC's. For example, the vendor provides a per part + unique ID there. + + This driver can also be built as a module. If so, the module + will be called layerscape-sfp. + config NVMEM_LPC18XX_EEPROM tristate "NXP LPC18XX EEPROM Memory Support" depends on ARCH_LPC18XX || COMPILE_TEST @@ -84,17 +131,32 @@ config NVMEM_LPC18XX_OTP To compile this driver as a module, choose M here: the module will be called nvmem_lpc18xx_otp. -config NVMEM_MXS_OCOTP - tristate "Freescale MXS On-Chip OTP Memory Support" - depends on ARCH_MXS || COMPILE_TEST - depends on HAS_IOMEM +config NVMEM_MESON_EFUSE + tristate "Amlogic Meson GX eFuse Support" + depends on (ARCH_MESON || COMPILE_TEST) && MESON_SM help - If you say Y here, you will get readonly access to the - One Time Programmable memory pages that are stored - on the Freescale i.MX23/i.MX28 processor. + This is a driver to retrieve specific values from the eFuse found on + the Amlogic Meson GX SoCs. This driver can also be built as a module. If so, the module - will be called nvmem-mxs-ocotp. + will be called nvmem_meson_efuse. + +config NVMEM_MESON_MX_EFUSE + tristate "Amlogic Meson6/Meson8/Meson8b eFuse Support" + depends on ARCH_MESON || COMPILE_TEST + help + This is a driver to retrieve specific values from the eFuse found on + the Amlogic Meson6, Meson8 and Meson8b SoCs. + + This driver can also be built as a module. If so, the module + will be called nvmem_meson_mx_efuse. + +config NVMEM_MICROCHIP_OTPC + tristate "Microchip OTPC support" + depends on ARCH_AT91 || COMPILE_TEST + help + This driver enable the OTP controller available on Microchip SAMA7G5 + SoCs. It controlls the access to the OTP memory connected to it. config NVMEM_MTK_EFUSE tristate "Mediatek SoCs EFUSE support" @@ -107,12 +169,17 @@ config NVMEM_MTK_EFUSE This driver can also be built as a module. If so, the module will be called efuse-mtk. -config NVMEM_MICROCHIP_OTPC - tristate "Microchip OTPC support" - depends on ARCH_AT91 || COMPILE_TEST +config NVMEM_MXS_OCOTP + tristate "Freescale MXS On-Chip OTP Memory Support" + depends on ARCH_MXS || COMPILE_TEST + depends on HAS_IOMEM help - This driver enable the OTP controller available on Microchip SAMA7G5 - SoCs. It controlls the access to the OTP memory connected to it. + If you say Y here, you will get readonly access to the + One Time Programmable memory pages that are stored + on the Freescale i.MX23/i.MX28 processor. + + This driver can also be built as a module. If so, the module + will be called nvmem-mxs-ocotp. config NVMEM_NINTENDO_OTP tristate "Nintendo Wii and Wii U OTP Support" @@ -137,13 +204,21 @@ config NVMEM_QCOM_QFPROM This driver can also be built as a module. If so, the module will be called nvmem_qfprom. -config NVMEM_SPMI_SDAM - tristate "SPMI SDAM Support" - depends on SPMI +config NVMEM_RAVE_SP_EEPROM + tristate "Rave SP EEPROM Support" + depends on RAVE_SP_CORE help - This driver supports the Shared Direct Access Memory Module on - Qualcomm Technologies, Inc. PMICs. It provides the clients - an interface to read/write to the SDAM module's shared memory. + Say y here to enable Rave SP EEPROM support. + +config NVMEM_RMEM + tristate "Reserved Memory Based Driver Support" + depends on HAS_IOMEM + help + This driver maps reserved memory into an nvmem device. It might be + useful to expose information left by firmware in memory. + + This driver can also be built as a module. If so, the module + will be called nvmem-rmem. config NVMEM_ROCKCHIP_EFUSE tristate "Rockchip eFuse Support" @@ -167,79 +242,16 @@ config NVMEM_ROCKCHIP_OTP This driver can also be built as a module. If so, the module will be called nvmem_rockchip_otp. -config NVMEM_BCM_OCOTP - tristate "Broadcom On-Chip OTP Controller support" - depends on ARCH_BCM_IPROC || COMPILE_TEST - depends on HAS_IOMEM - default ARCH_BCM_IPROC - help - Say y here to enable read/write access to the Broadcom OTP - controller. - - This driver can also be built as a module. If so, the module - will be called nvmem-bcm-ocotp. - -config NVMEM_STM32_ROMEM - tristate "STMicroelectronics STM32 factory-programmed memory support" - depends on ARCH_STM32 || COMPILE_TEST - help - Say y here to enable read-only access for STMicroelectronics STM32 - factory-programmed memory area. - - This driver can also be built as a module. If so, the module - will be called nvmem-stm32-romem. - -config NVMEM_SUNXI_SID - tristate "Allwinner SoCs SID support" - depends on ARCH_SUNXI - help - This is a driver for the 'security ID' available on various Allwinner - devices. - - This driver can also be built as a module. If so, the module - will be called nvmem_sunxi_sid. - -config NVMEM_UNIPHIER_EFUSE - tristate "UniPhier SoCs eFuse support" - depends on ARCH_UNIPHIER || COMPILE_TEST - depends on HAS_IOMEM - help - This is a simple driver to dump specified values of UniPhier SoC - from eFuse. - - This driver can also be built as a module. If so, the module - will be called nvmem-uniphier-efuse. - -config NVMEM_VF610_OCOTP - tristate "VF610 SoC OCOTP support" - depends on SOC_VF610 || COMPILE_TEST +config NVMEM_SC27XX_EFUSE + tristate "Spreadtrum SC27XX eFuse Support" + depends on MFD_SC27XX_PMIC || COMPILE_TEST depends on HAS_IOMEM help - This is a driver for the 'OCOTP' peripheral available on Vybrid - devices like VF5xx and VF6xx. - - This driver can also be build as a module. If so, the module will - be called nvmem-vf610-ocotp. - -config NVMEM_MESON_EFUSE - tristate "Amlogic Meson GX eFuse Support" - depends on (ARCH_MESON || COMPILE_TEST) && MESON_SM - help - This is a driver to retrieve specific values from the eFuse found on - the Amlogic Meson GX SoCs. - - This driver can also be built as a module. If so, the module - will be called nvmem_meson_efuse. - -config NVMEM_MESON_MX_EFUSE - tristate "Amlogic Meson6/Meson8/Meson8b eFuse Support" - depends on ARCH_MESON || COMPILE_TEST - help - This is a driver to retrieve specific values from the eFuse found on - the Amlogic Meson6, Meson8 and Meson8b SoCs. + This is a simple driver to dump specified values of Spreadtrum + SC27XX PMICs from eFuse. This driver can also be built as a module. If so, the module - will be called nvmem_meson_mx_efuse. + will be called nvmem-sc27xx-efuse. config NVMEM_SNVS_LPGPR tristate "Support for Low Power General Purpose Register" @@ -251,32 +263,13 @@ config NVMEM_SNVS_LPGPR This driver can also be built as a module. If so, the module will be called nvmem-snvs-lpgpr. -config NVMEM_RAVE_SP_EEPROM - tristate "Rave SP EEPROM Support" - depends on RAVE_SP_CORE - help - Say y here to enable Rave SP EEPROM support. - -config NVMEM_SC27XX_EFUSE - tristate "Spreadtrum SC27XX eFuse Support" - depends on MFD_SC27XX_PMIC || COMPILE_TEST - depends on HAS_IOMEM - help - This is a simple driver to dump specified values of Spreadtrum - SC27XX PMICs from eFuse. - - This driver can also be built as a module. If so, the module - will be called nvmem-sc27xx-efuse. - -config NVMEM_ZYNQMP - bool "Xilinx ZYNQMP SoC nvmem firmware support" - depends on ARCH_ZYNQMP +config NVMEM_SPMI_SDAM + tristate "SPMI SDAM Support" + depends on SPMI help - This is a driver to access hardware related data like - soc revision, IDCODE... etc by using the firmware - interface. - - If sure, say yes. If unsure, say no. + This driver supports the Shared Direct Access Memory Module on + Qualcomm Technologies, Inc. PMICs. It provides the clients + an interface to read/write to the SDAM module's shared memory. config NVMEM_SPRD_EFUSE tristate "Spreadtrum SoC eFuse Support" @@ -289,36 +282,15 @@ config NVMEM_SPRD_EFUSE This driver can also be built as a module. If so, the module will be called nvmem-sprd-efuse. -config NVMEM_RMEM - tristate "Reserved Memory Based Driver Support" - depends on HAS_IOMEM - help - This driver maps reserved memory into an nvmem device. It might be - useful to expose information left by firmware in memory. - - This driver can also be built as a module. If so, the module - will be called nvmem-rmem. - -config NVMEM_BRCM_NVRAM - tristate "Broadcom's NVRAM support" - depends on ARCH_BCM_5301X || COMPILE_TEST - depends on HAS_IOMEM - help - This driver provides support for Broadcom's NVRAM that can be accessed - using I/O mapping. - -config NVMEM_LAYERSCAPE_SFP - tristate "Layerscape SFP (Security Fuse Processor) support" - depends on ARCH_LAYERSCAPE || COMPILE_TEST - depends on HAS_IOMEM - select REGMAP_MMIO +config NVMEM_STM32_ROMEM + tristate "STMicroelectronics STM32 factory-programmed memory support" + depends on ARCH_STM32 || COMPILE_TEST help - This driver provides support to read the eFuses on Freescale - Layerscape SoC's. For example, the vendor provides a per part - unique ID there. + Say y here to enable read-only access for STMicroelectronics STM32 + factory-programmed memory area. This driver can also be built as a module. If so, the module - will be called layerscape-sfp. + will be called nvmem-stm32-romem. config NVMEM_SUNPLUS_OCOTP tristate "Sunplus SoC OTP support" @@ -332,17 +304,15 @@ config NVMEM_SUNPLUS_OCOTP This driver can also be built as a module. If so, the module will be called nvmem-sunplus-ocotp. -config NVMEM_APPLE_EFUSES - tristate "Apple eFuse support" - depends on ARCH_APPLE || COMPILE_TEST - default ARCH_APPLE +config NVMEM_SUNXI_SID + tristate "Allwinner SoCs SID support" + depends on ARCH_SUNXI help - Say y here to enable support for reading eFuses on Apple SoCs - such as the M1. These are e.g. used to store factory programmed - calibration data required for the PCIe or the USB-C PHY. + This is a driver for the 'security ID' available on various Allwinner + devices. - This driver can also be built as a module. If so, the module will - be called nvmem-apple-efuses. + This driver can also be built as a module. If so, the module + will be called nvmem_sunxi_sid. config NVMEM_U_BOOT_ENV tristate "U-Boot environment variables support" @@ -357,4 +327,36 @@ config NVMEM_U_BOOT_ENV If compiled as module it will be called nvmem_u-boot-env. +config NVMEM_UNIPHIER_EFUSE + tristate "UniPhier SoCs eFuse support" + depends on ARCH_UNIPHIER || COMPILE_TEST + depends on HAS_IOMEM + help + This is a simple driver to dump specified values of UniPhier SoC + from eFuse. + + This driver can also be built as a module. If so, the module + will be called nvmem-uniphier-efuse. + +config NVMEM_VF610_OCOTP + tristate "VF610 SoC OCOTP support" + depends on SOC_VF610 || COMPILE_TEST + depends on HAS_IOMEM + help + This is a driver for the 'OCOTP' peripheral available on Vybrid + devices like VF5xx and VF6xx. + + This driver can also be build as a module. If so, the module will + be called nvmem-vf610-ocotp. + +config NVMEM_ZYNQMP + bool "Xilinx ZYNQMP SoC nvmem firmware support" + depends on ARCH_ZYNQMP + help + This is a driver to access hardware related data like + soc revision, IDCODE... etc by using the firmware + interface. + + If sure, say yes. If unsure, say no. + endif diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index 7ac988c6966e..bac799b2fa8d 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -7,67 +7,67 @@ obj-$(CONFIG_NVMEM) += nvmem_core.o nvmem_core-y := core.o # Devices -obj-$(CONFIG_NVMEM_BCM_OCOTP) += nvmem-bcm-ocotp.o -nvmem-bcm-ocotp-y := bcm-ocotp.o -obj-$(CONFIG_NVMEM_IMX_IIM) += nvmem-imx-iim.o -nvmem-imx-iim-y := imx-iim.o -obj-$(CONFIG_NVMEM_IMX_OCOTP) += nvmem-imx-ocotp.o -nvmem-imx-ocotp-y := imx-ocotp.o +obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvmem-apple-efuses.o +nvmem-apple-efuses-y := apple-efuses.o +obj-$(CONFIG_NVMEM_BCM_OCOTP) += nvmem-bcm-ocotp.o +nvmem-bcm-ocotp-y := bcm-ocotp.o +obj-$(CONFIG_NVMEM_BRCM_NVRAM) += nvmem_brcm_nvram.o +nvmem_brcm_nvram-y := brcm_nvram.o +obj-$(CONFIG_NVMEM_IMX_IIM) += nvmem-imx-iim.o +nvmem-imx-iim-y := imx-iim.o +obj-$(CONFIG_NVMEM_IMX_OCOTP) += nvmem-imx-ocotp.o +nvmem-imx-ocotp-y := imx-ocotp.o obj-$(CONFIG_NVMEM_IMX_OCOTP_SCU) += nvmem-imx-ocotp-scu.o -nvmem-imx-ocotp-scu-y := imx-ocotp-scu.o -obj-$(CONFIG_NVMEM_JZ4780_EFUSE) += nvmem_jz4780_efuse.o -nvmem_jz4780_efuse-y := jz4780-efuse.o +nvmem-imx-ocotp-scu-y := imx-ocotp-scu.o +obj-$(CONFIG_NVMEM_JZ4780_EFUSE) += nvmem_jz4780_efuse.o +nvmem_jz4780_efuse-y := jz4780-efuse.o +obj-$(CONFIG_NVMEM_LAYERSCAPE_SFP) += nvmem-layerscape-sfp.o +nvmem-layerscape-sfp-y := layerscape-sfp.o obj-$(CONFIG_NVMEM_LPC18XX_EEPROM) += nvmem_lpc18xx_eeprom.o -nvmem_lpc18xx_eeprom-y := lpc18xx_eeprom.o -obj-$(CONFIG_NVMEM_LPC18XX_OTP) += nvmem_lpc18xx_otp.o -nvmem_lpc18xx_otp-y := lpc18xx_otp.o -obj-$(CONFIG_NVMEM_MXS_OCOTP) += nvmem-mxs-ocotp.o -nvmem-mxs-ocotp-y := mxs-ocotp.o -obj-$(CONFIG_NVMEM_NINTENDO_OTP) += nvmem-nintendo-otp.o -nvmem-nintendo-otp-y := nintendo-otp.o +nvmem_lpc18xx_eeprom-y := lpc18xx_eeprom.o +obj-$(CONFIG_NVMEM_LPC18XX_OTP) += nvmem_lpc18xx_otp.o +nvmem_lpc18xx_otp-y := lpc18xx_otp.o +obj-$(CONFIG_NVMEM_MESON_EFUSE) += nvmem_meson_efuse.o +nvmem_meson_efuse-y := meson-efuse.o +obj-$(CONFIG_NVMEM_MESON_MX_EFUSE) += nvmem_meson_mx_efuse.o +nvmem_meson_mx_efuse-y := meson-mx-efuse.o +obj-$(CONFIG_NVMEM_MICROCHIP_OTPC) += nvmem-microchip-otpc.o +nvmem-microchip-otpc-y := microchip-otpc.o obj-$(CONFIG_NVMEM_MTK_EFUSE) += nvmem_mtk-efuse.o -nvmem_mtk-efuse-y := mtk-efuse.o -obj-$(CONFIG_NVMEM_QCOM_QFPROM) += nvmem_qfprom.o -nvmem_qfprom-y := qfprom.o -obj-$(CONFIG_NVMEM_SPMI_SDAM) += nvmem_qcom-spmi-sdam.o -nvmem_qcom-spmi-sdam-y += qcom-spmi-sdam.o +nvmem_mtk-efuse-y := mtk-efuse.o +obj-$(CONFIG_NVMEM_MXS_OCOTP) += nvmem-mxs-ocotp.o +nvmem-mxs-ocotp-y := mxs-ocotp.o +obj-$(CONFIG_NVMEM_NINTENDO_OTP) += nvmem-nintendo-otp.o +nvmem-nintendo-otp-y := nintendo-otp.o +obj-$(CONFIG_NVMEM_QCOM_QFPROM) += nvmem_qfprom.o +nvmem_qfprom-y := qfprom.o +obj-$(CONFIG_NVMEM_RAVE_SP_EEPROM) += nvmem-rave-sp-eeprom.o +nvmem-rave-sp-eeprom-y := rave-sp-eeprom.o +obj-$(CONFIG_NVMEM_RMEM) += nvmem-rmem.o +nvmem-rmem-y := rmem.o obj-$(CONFIG_NVMEM_ROCKCHIP_EFUSE) += nvmem_rockchip_efuse.o -nvmem_rockchip_efuse-y := rockchip-efuse.o +nvmem_rockchip_efuse-y := rockchip-efuse.o obj-$(CONFIG_NVMEM_ROCKCHIP_OTP) += nvmem-rockchip-otp.o -nvmem-rockchip-otp-y := rockchip-otp.o -obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o -nvmem_stm32_romem-y := stm32-romem.o -obj-$(CONFIG_NVMEM_STM32_ROMEM) += nvmem_stm32_romem.o -nvmem_sunxi_sid-y := sunxi_sid.o -obj-$(CONFIG_NVMEM_UNIPHIER_EFUSE) += nvmem-uniphier-efuse.o -nvmem-uniphier-efuse-y := uniphier-efuse.o -obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvmem-vf610-ocotp.o -nvmem-vf610-ocotp-y := vf610-ocotp.o -obj-$(CONFIG_NVMEM_MESON_EFUSE) += nvmem_meson_efuse.o -nvmem_meson_efuse-y := meson-efuse.o -obj-$(CONFIG_NVMEM_MESON_MX_EFUSE) += nvmem_meson_mx_efuse.o -nvmem_meson_mx_efuse-y := meson-mx-efuse.o -obj-$(CONFIG_NVMEM_SNVS_LPGPR) += nvmem_snvs_lpgpr.o -nvmem_snvs_lpgpr-y := snvs_lpgpr.o -obj-$(CONFIG_NVMEM_RAVE_SP_EEPROM) += nvmem-rave-sp-eeprom.o -nvmem-rave-sp-eeprom-y := rave-sp-eeprom.o +nvmem-rockchip-otp-y := rockchip-otp.o obj-$(CONFIG_NVMEM_SC27XX_EFUSE) += nvmem-sc27xx-efuse.o -nvmem-sc27xx-efuse-y := sc27xx-efuse.o -obj-$(CONFIG_NVMEM_ZYNQMP) += nvmem_zynqmp_nvmem.o -nvmem_zynqmp_nvmem-y := zynqmp_nvmem.o -obj-$(CONFIG_NVMEM_SPRD_EFUSE) += nvmem_sprd_efuse.o -nvmem_sprd_efuse-y := sprd-efuse.o -obj-$(CONFIG_NVMEM_RMEM) += nvmem-rmem.o -nvmem-rmem-y := rmem.o -obj-$(CONFIG_NVMEM_BRCM_NVRAM) += nvmem_brcm_nvram.o -nvmem_brcm_nvram-y := brcm_nvram.o -obj-$(CONFIG_NVMEM_LAYERSCAPE_SFP) += nvmem-layerscape-sfp.o -nvmem-layerscape-sfp-y := layerscape-sfp.o +nvmem-sc27xx-efuse-y := sc27xx-efuse.o +obj-$(CONFIG_NVMEM_SNVS_LPGPR) += nvmem_snvs_lpgpr.o +nvmem_snvs_lpgpr-y := snvs_lpgpr.o +obj-$(CONFIG_NVMEM_SPMI_SDAM) += nvmem_qcom-spmi-sdam.o +nvmem_qcom-spmi-sdam-y += qcom-spmi-sdam.o +obj-$(CONFIG_NVMEM_SPRD_EFUSE) += nvmem_sprd_efuse.o +nvmem_sprd_efuse-y := sprd-efuse.o +obj-$(CONFIG_NVMEM_STM32_ROMEM) += nvmem_stm32_romem.o +nvmem_stm32_romem-y := stm32-romem.o obj-$(CONFIG_NVMEM_SUNPLUS_OCOTP) += nvmem_sunplus_ocotp.o -nvmem_sunplus_ocotp-y := sunplus-ocotp.o -obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvmem-apple-efuses.o -nvmem-apple-efuses-y := apple-efuses.o -obj-$(CONFIG_NVMEM_MICROCHIP_OTPC) += nvmem-microchip-otpc.o -nvmem-microchip-otpc-y := microchip-otpc.o -obj-$(CONFIG_NVMEM_U_BOOT_ENV) += nvmem_u-boot-env.o -nvmem_u-boot-env-y := u-boot-env.o +nvmem_sunplus_ocotp-y := sunplus-ocotp.o +obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o +nvmem_sunxi_sid-y := sunxi_sid.o +obj-$(CONFIG_NVMEM_U_BOOT_ENV) += nvmem_u-boot-env.o +nvmem_u-boot-env-y := u-boot-env.o +obj-$(CONFIG_NVMEM_UNIPHIER_EFUSE) += nvmem-uniphier-efuse.o +nvmem-uniphier-efuse-y := uniphier-efuse.o +obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvmem-vf610-ocotp.o +nvmem-vf610-ocotp-y := vf610-ocotp.o +obj-$(CONFIG_NVMEM_ZYNQMP) += nvmem_zynqmp_nvmem.o +nvmem_zynqmp_nvmem-y := zynqmp_nvmem.o -- cgit v1.2.3 From 8f6a3a19e99cdb52b0ff0d2d3197e810258b6e46 Mon Sep 17 00:00:00 2001 From: Iskren Chernev Date: Fri, 16 Sep 2022 13:20:56 +0100 Subject: dt-bindings: nvmem: Add SoC compatible for sm6115 Document SoC compatible for sm6115. Reviewed-by: Caleb Connolly Acked-by: Krzysztof Kozlowski Signed-off-by: Iskren Chernev Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220916122100.170016-10-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml index b4163086a5be..2eab2f46cb65 100644 --- a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml +++ b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml @@ -28,6 +28,7 @@ properties: - qcom,sc7280-qfprom - qcom,sdm630-qfprom - qcom,sdm845-qfprom + - qcom,sm6115-qfprom - const: qcom,qfprom reg: -- cgit v1.2.3 From d4d432670f7dee0a5432fcffcfc8699b25181ace Mon Sep 17 00:00:00 2001 From: RafaÅ‚ MiÅ‚ecki Date: Fri, 16 Sep 2022 13:20:57 +0100 Subject: nvmem: u-boot-env: find Device Tree nodes for NVMEM cells MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DT binding allows specifying NVMEM cells as NVMEM device (provider) subnodes. Looks for such subnodes when building NVMEM cells. This allows NVMEM consumers to use U-Boot environment variables. Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220916122100.170016-11-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/u-boot-env.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/nvmem/u-boot-env.c b/drivers/nvmem/u-boot-env.c index 9b9abfb8f187..d17a164ae705 100644 --- a/drivers/nvmem/u-boot-env.c +++ b/drivers/nvmem/u-boot-env.c @@ -92,6 +92,7 @@ static int u_boot_env_add_cells(struct u_boot_env *priv, uint8_t *buf, return -ENOMEM; priv->cells[idx].offset = data_offset + value - data; priv->cells[idx].bytes = strlen(value); + priv->cells[idx].np = of_get_child_by_name(dev->of_node, priv->cells[idx].name); } if (WARN_ON(idx != priv->ncells)) -- cgit v1.2.3 From d1b274c4819deed7673e5edec12fea1e57110ad8 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Fri, 16 Sep 2022 13:20:58 +0100 Subject: dt-bindings: lan9662-otpc: document Lan9662 OTPC Document Lan9662 OTP controller. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Horatiu Vultur Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220916122100.170016-12-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../bindings/nvmem/microchip,lan9662-otpc.yaml | 45 ++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 Documentation/devicetree/bindings/nvmem/microchip,lan9662-otpc.yaml diff --git a/Documentation/devicetree/bindings/nvmem/microchip,lan9662-otpc.yaml b/Documentation/devicetree/bindings/nvmem/microchip,lan9662-otpc.yaml new file mode 100644 index 000000000000..f97c6beb4766 --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/microchip,lan9662-otpc.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/nvmem/microchip,lan9662-otpc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip LAN9662 OTP Controller (OTPC) + +maintainers: + - Horatiu Vultur + +description: | + OTP controller drives a NVMEM memory where system specific data + (e.g. hardware configuration settings, chip identifiers) or + user specific data could be stored. + +allOf: + - $ref: nvmem.yaml# + +properties: + compatible: + oneOf: + - items: + - const: microchip,lan9668-otpc + - const: microchip,lan9662-otpc + - enum: + - microchip,lan9662-otpc + + reg: + maxItems: 1 + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + otpc: otp@e0021000 { + compatible = "microchip,lan9662-otpc"; + reg = <0xe0021000 0x300>; + }; + +... -- cgit v1.2.3 From 9e8f208ad5229ddda97cd4a83ecf89c735d99592 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Fri, 16 Sep 2022 13:20:59 +0100 Subject: nvmem: lan9662-otp: add support Add support for OTP controller available on LAN9662. The OTPC controls the access to a non-volatile memory. The size of the memory is 8KB. The OTPC can access the memory based on an offset. Implement both the read and the write functionality. Signed-off-by: Horatiu Vultur Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220916122100.170016-13-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/Kconfig | 8 ++ drivers/nvmem/Makefile | 2 + drivers/nvmem/lan9662-otpc.c | 222 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 232 insertions(+) create mode 100644 drivers/nvmem/lan9662-otpc.c diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 7f2557934834..ec8a49c04003 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -98,6 +98,14 @@ config NVMEM_JZ4780_EFUSE To compile this driver as a module, choose M here: the module will be called nvmem_jz4780_efuse. +config NVMEM_LAN9662_OTPC + tristate "Microchip LAN9662 OTP controller support" + depends on SOC_LAN966 || COMPILE_TEST + depends on HAS_IOMEM + help + This driver enables the OTP controller available on Microchip LAN9662 + SoCs. It controls the access to the OTP memory connected to it. + config NVMEM_LAYERSCAPE_SFP tristate "Layerscape SFP (Security Fuse Processor) support" depends on ARCH_LAYERSCAPE || COMPILE_TEST diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index bac799b2fa8d..fa80fe17e567 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -21,6 +21,8 @@ obj-$(CONFIG_NVMEM_IMX_OCOTP_SCU) += nvmem-imx-ocotp-scu.o nvmem-imx-ocotp-scu-y := imx-ocotp-scu.o obj-$(CONFIG_NVMEM_JZ4780_EFUSE) += nvmem_jz4780_efuse.o nvmem_jz4780_efuse-y := jz4780-efuse.o +obj-$(CONFIG_NVMEM_LAN9662_OTPC) += nvmem-lan9662-otpc.o +nvmem-lan9662-otpc-y := lan9662-otpc.o obj-$(CONFIG_NVMEM_LAYERSCAPE_SFP) += nvmem-layerscape-sfp.o nvmem-layerscape-sfp-y := layerscape-sfp.o obj-$(CONFIG_NVMEM_LPC18XX_EEPROM) += nvmem_lpc18xx_eeprom.o diff --git a/drivers/nvmem/lan9662-otpc.c b/drivers/nvmem/lan9662-otpc.c new file mode 100644 index 000000000000..f6732fd216d8 --- /dev/null +++ b/drivers/nvmem/lan9662-otpc.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include + +#define OTP_OTP_PWR_DN(t) (t + 0x00) +#define OTP_OTP_PWR_DN_OTP_PWRDN_N BIT(0) +#define OTP_OTP_ADDR_HI(t) (t + 0x04) +#define OTP_OTP_ADDR_LO(t) (t + 0x08) +#define OTP_OTP_PRGM_DATA(t) (t + 0x10) +#define OTP_OTP_PRGM_MODE(t) (t + 0x14) +#define OTP_OTP_PRGM_MODE_OTP_PGM_MODE_BYTE BIT(0) +#define OTP_OTP_RD_DATA(t) (t + 0x18) +#define OTP_OTP_FUNC_CMD(t) (t + 0x20) +#define OTP_OTP_FUNC_CMD_OTP_PROGRAM BIT(1) +#define OTP_OTP_FUNC_CMD_OTP_READ BIT(0) +#define OTP_OTP_CMD_GO(t) (t + 0x28) +#define OTP_OTP_CMD_GO_OTP_GO BIT(0) +#define OTP_OTP_PASS_FAIL(t) (t + 0x2c) +#define OTP_OTP_PASS_FAIL_OTP_READ_PROHIBITED BIT(3) +#define OTP_OTP_PASS_FAIL_OTP_WRITE_PROHIBITED BIT(2) +#define OTP_OTP_PASS_FAIL_OTP_FAIL BIT(0) +#define OTP_OTP_STATUS(t) (t + 0x30) +#define OTP_OTP_STATUS_OTP_CPUMPEN BIT(1) +#define OTP_OTP_STATUS_OTP_BUSY BIT(0) + +#define OTP_MEM_SIZE 8192 +#define OTP_SLEEP_US 10 +#define OTP_TIMEOUT_US 500000 + +struct lan9662_otp { + struct device *dev; + void __iomem *base; +}; + +static bool lan9662_otp_wait_flag_clear(void __iomem *reg, u32 flag) +{ + u32 val; + + return readl_poll_timeout(reg, val, !(val & flag), + OTP_SLEEP_US, OTP_TIMEOUT_US); +} + +static int lan9662_otp_power(struct lan9662_otp *otp, bool up) +{ + void __iomem *pwrdn = OTP_OTP_PWR_DN(otp->base); + + if (up) { + writel(readl(pwrdn) & ~OTP_OTP_PWR_DN_OTP_PWRDN_N, pwrdn); + if (lan9662_otp_wait_flag_clear(OTP_OTP_STATUS(otp->base), + OTP_OTP_STATUS_OTP_CPUMPEN)) + return -ETIMEDOUT; + } else { + writel(readl(pwrdn) | OTP_OTP_PWR_DN_OTP_PWRDN_N, pwrdn); + } + + return 0; +} + +static int lan9662_otp_execute(struct lan9662_otp *otp) +{ + if (lan9662_otp_wait_flag_clear(OTP_OTP_CMD_GO(otp->base), + OTP_OTP_CMD_GO_OTP_GO)) + return -ETIMEDOUT; + + if (lan9662_otp_wait_flag_clear(OTP_OTP_STATUS(otp->base), + OTP_OTP_STATUS_OTP_BUSY)) + return -ETIMEDOUT; + + return 0; +} + +static void lan9662_otp_set_address(struct lan9662_otp *otp, u32 offset) +{ + writel(0xff & (offset >> 8), OTP_OTP_ADDR_HI(otp->base)); + writel(0xff & offset, OTP_OTP_ADDR_LO(otp->base)); +} + +static int lan9662_otp_read_byte(struct lan9662_otp *otp, u32 offset, u8 *dst) +{ + u32 pass; + int rc; + + lan9662_otp_set_address(otp, offset); + writel(OTP_OTP_FUNC_CMD_OTP_READ, OTP_OTP_FUNC_CMD(otp->base)); + writel(OTP_OTP_CMD_GO_OTP_GO, OTP_OTP_CMD_GO(otp->base)); + rc = lan9662_otp_execute(otp); + if (!rc) { + pass = readl(OTP_OTP_PASS_FAIL(otp->base)); + if (pass & OTP_OTP_PASS_FAIL_OTP_READ_PROHIBITED) + return -EACCES; + *dst = (u8) readl(OTP_OTP_RD_DATA(otp->base)); + } + return rc; +} + +static int lan9662_otp_write_byte(struct lan9662_otp *otp, u32 offset, u8 data) +{ + u32 pass; + int rc; + + lan9662_otp_set_address(otp, offset); + writel(OTP_OTP_PRGM_MODE_OTP_PGM_MODE_BYTE, OTP_OTP_PRGM_MODE(otp->base)); + writel(data, OTP_OTP_PRGM_DATA(otp->base)); + writel(OTP_OTP_FUNC_CMD_OTP_PROGRAM, OTP_OTP_FUNC_CMD(otp->base)); + writel(OTP_OTP_CMD_GO_OTP_GO, OTP_OTP_CMD_GO(otp->base)); + + rc = lan9662_otp_execute(otp); + if (!rc) { + pass = readl(OTP_OTP_PASS_FAIL(otp->base)); + if (pass & OTP_OTP_PASS_FAIL_OTP_WRITE_PROHIBITED) + return -EACCES; + if (pass & OTP_OTP_PASS_FAIL_OTP_FAIL) + return -EIO; + } + return rc; +} + +static int lan9662_otp_read(void *context, unsigned int offset, + void *_val, size_t bytes) +{ + struct lan9662_otp *otp = context; + u8 *val = _val; + uint8_t data; + int i, rc = 0; + + lan9662_otp_power(otp, true); + for (i = 0; i < bytes; i++) { + rc = lan9662_otp_read_byte(otp, offset + i, &data); + if (rc < 0) + break; + *val++ = data; + } + lan9662_otp_power(otp, false); + + return rc; +} + +static int lan9662_otp_write(void *context, unsigned int offset, + void *_val, size_t bytes) +{ + struct lan9662_otp *otp = context; + u8 *val = _val; + u8 data, newdata; + int i, rc = 0; + + lan9662_otp_power(otp, true); + for (i = 0; i < bytes; i++) { + /* Skip zero bytes */ + if (val[i]) { + rc = lan9662_otp_read_byte(otp, offset + i, &data); + if (rc < 0) + break; + + newdata = data | val[i]; + if (newdata == data) + continue; + + rc = lan9662_otp_write_byte(otp, offset + i, + newdata); + if (rc < 0) + break; + } + } + lan9662_otp_power(otp, false); + + return rc; +} + +static struct nvmem_config otp_config = { + .name = "lan9662-otp", + .stride = 1, + .word_size = 1, + .reg_read = lan9662_otp_read, + .reg_write = lan9662_otp_write, + .size = OTP_MEM_SIZE, +}; + +static int lan9662_otp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct nvmem_device *nvmem; + struct lan9662_otp *otp; + + otp = devm_kzalloc(&pdev->dev, sizeof(*otp), GFP_KERNEL); + if (!otp) + return -ENOMEM; + + otp->dev = dev; + otp->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(otp->base)) + return PTR_ERR(otp->base); + + otp_config.priv = otp; + otp_config.dev = dev; + + nvmem = devm_nvmem_register(dev, &otp_config); + + return PTR_ERR_OR_ZERO(nvmem); +} + +static const struct of_device_id lan9662_otp_match[] = { + { .compatible = "microchip,lan9662-otp", }, + { }, +}; +MODULE_DEVICE_TABLE(of, lan9662_otp_match); + +static struct platform_driver lan9662_otp_driver = { + .probe = lan9662_otp_probe, + .driver = { + .name = "lan9662-otp", + .of_match_table = lan9662_otp_match, + }, +}; +module_platform_driver(lan9662_otp_driver); + +MODULE_AUTHOR("Horatiu Vultur "); +MODULE_DESCRIPTION("lan9662 OTP driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 3717ca3e0cc8683f93b41d3f06ca79631eb58715 Mon Sep 17 00:00:00 2001 From: RafaÅ‚ MiÅ‚ecki Date: Fri, 16 Sep 2022 13:21:00 +0100 Subject: nvmem: u-boot-env: fix crc32 casting type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes: drivers/nvmem/u-boot-env.c:141:17: sparse: sparse: cast to restricted __le32 Fixes: d5542923f200 ("nvmem: add driver handling U-Boot environment variables") Reported-by: kernel test robot Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20220916122100.170016-14-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/u-boot-env.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvmem/u-boot-env.c b/drivers/nvmem/u-boot-env.c index d17a164ae705..8e72d1bbd649 100644 --- a/drivers/nvmem/u-boot-env.c +++ b/drivers/nvmem/u-boot-env.c @@ -139,7 +139,7 @@ static int u_boot_env_parse(struct u_boot_env *priv) data_offset = offsetof(struct u_boot_env_image_redundant, data); break; } - crc32 = le32_to_cpu(*(uint32_t *)(buf + crc32_offset)); + crc32 = le32_to_cpu(*(__le32 *)(buf + crc32_offset)); crc32_data_len = priv->mtd->size - crc32_data_offset; data_len = priv->mtd->size - data_offset; -- cgit v1.2.3 From 1b46c82146d732aa6dab5ef51a89ec0b53bd05b4 Mon Sep 17 00:00:00 2001 From: Gaosheng Cui Date: Tue, 13 Sep 2022 19:03:56 +0800 Subject: drivers/misc/sgi-xp: Remove orphan declarations from drivers/misc/sgi-xp/xp.h Remove the following orphan declarations from drivers/misc/sgi-xp/xp.h: 1. xp_nofault_PIOR_target 2. xp_error_PIOR 3. xp_nofault_PIOR They have been removed since commit 9726bfcdb977 ("misc/sgi-xp: remove SGI SN2 support"), so remove them. Reviewed-by: Steve Wahl Signed-off-by: Gaosheng Cui Link: https://lore.kernel.org/r/20220913110356.764711-1-cuigaosheng1@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sgi-xp/xp.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/misc/sgi-xp/xp.h b/drivers/misc/sgi-xp/xp.h index 9f9af77f8d2e..f1336f43d3bd 100644 --- a/drivers/misc/sgi-xp/xp.h +++ b/drivers/misc/sgi-xp/xp.h @@ -334,10 +334,6 @@ extern int (*xp_cpu_to_nasid) (int); extern enum xp_retval (*xp_expand_memprotect) (unsigned long, unsigned long); extern enum xp_retval (*xp_restrict_memprotect) (unsigned long, unsigned long); -extern u64 xp_nofault_PIOR_target; -extern int xp_nofault_PIOR(void *); -extern int xp_error_PIOR(void); - extern struct device *xp; extern enum xp_retval xp_init_uv(void); extern void xp_exit_uv(void); -- cgit v1.2.3 From 4b25cf09c69c0e48e7fb75786f29b50bddf50ebf Mon Sep 17 00:00:00 2001 From: Jilin Yuan Date: Sun, 18 Sep 2022 18:04:31 +0800 Subject: mei: fix repeated words in comments Delete the redundant word 'from'. Acked-by: Tomas Winkler Signed-off-by: Jilin Yuan Link: https://lore.kernel.org/r/20220918100431.28381-1-yuanjilin@cdjrlc.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-txe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c index 00652c137cc7..1f72bbd5ee51 100644 --- a/drivers/misc/mei/hw-txe.c +++ b/drivers/misc/mei/hw-txe.c @@ -176,7 +176,7 @@ static bool mei_txe_aliveness_set(struct mei_device *dev, u32 req) * @dev: the device structure * * Extract HICR_HOST_ALIVENESS_RESP_ACK bit from - * from HICR_HOST_ALIVENESS_REQ register value + * HICR_HOST_ALIVENESS_REQ register value * * Return: SICR_HOST_ALIVENESS_REQ_REQUESTED bit value */ -- cgit v1.2.3 From 9ea224b119223ddacbed3d7c96a30b2823e09579 Mon Sep 17 00:00:00 2001 From: Shang XiaoJing Date: Fri, 23 Sep 2022 18:08:41 +0800 Subject: mei: gsc: Remove redundant dev_err call devm_ioremap_resource() prints error message in itself. Remove the dev_err call to avoid redundant error message. Signed-off-by: Shang XiaoJing Link: https://lore.kernel.org/r/20220923100841.17719-1-shangxiaojing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/gsc-me.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/misc/mei/gsc-me.c b/drivers/misc/mei/gsc-me.c index c8145e9b62b6..dd54f6110ae5 100644 --- a/drivers/misc/mei/gsc-me.c +++ b/drivers/misc/mei/gsc-me.c @@ -56,7 +56,6 @@ static int mei_gsc_probe(struct auxiliary_device *aux_dev, hw = to_me_hw(dev); hw->mem_addr = devm_ioremap_resource(device, &adev->bar); if (IS_ERR(hw->mem_addr)) { - dev_err(device, "mmio not mapped\n"); ret = PTR_ERR(hw->mem_addr); goto err; } -- cgit v1.2.3 From 711898b1d5ac37bc85a9495f3f2815f5fbd0a937 Mon Sep 17 00:00:00 2001 From: Vishnu Dasa Date: Wed, 14 Sep 2022 20:13:21 -0700 Subject: MAINTAINERS: Add header files under VMWARE VMCI DRIVER Add include/linux/vmw_vmci* files under VMWARE VMCI DRIVER. Suggested-by: Stefano Garzarella Acked-by: Bryan Tan Acked-by: Stefano Garzarella Signed-off-by: Vishnu Dasa Link: https://lore.kernel.org/r/20220915031321.1121-1-vdasa@vmware.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 750e62bab132..ad28606cec5b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21746,6 +21746,7 @@ R: VMware PV-Drivers Reviewers L: linux-kernel@vger.kernel.org S: Supported F: drivers/misc/vmw_vmci/ +F: include/linux/vmw_vmci* VMWARE VMMOUSE SUBDRIVER M: Zack Rusin -- cgit v1.2.3 From a5ccec12acfaf3a7ebbbeb90555b35b275fce0df Mon Sep 17 00:00:00 2001 From: Liu Shixin Date: Fri, 16 Sep 2022 22:12:44 +0800 Subject: bus: mvebu-mbus: use DEFINE_SHOW_ATTRIBUTE to simplify mvebu_{sdram/devs}_debug Use DEFINE_SHOW_ATTRIBUTE helper macro to simplify the code. Signed-off-by: Liu Shixin Link: https://lore.kernel.org/r/20220916141244.2174005-1-liushixin2@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/bus/mvebu-mbus.c | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c index 5dc2669432ba..d51573ac525e 100644 --- a/drivers/bus/mvebu-mbus.c +++ b/drivers/bus/mvebu-mbus.c @@ -466,18 +466,7 @@ static int mvebu_sdram_debug_show(struct seq_file *seq, void *v) struct mvebu_mbus_state *mbus = &mbus_state; return mbus->soc->show_cpu_target(mbus, seq, v); } - -static int mvebu_sdram_debug_open(struct inode *inode, struct file *file) -{ - return single_open(file, mvebu_sdram_debug_show, inode->i_private); -} - -static const struct file_operations mvebu_sdram_debug_fops = { - .open = mvebu_sdram_debug_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(mvebu_sdram_debug); static int mvebu_devs_debug_show(struct seq_file *seq, void *v) { @@ -516,18 +505,7 @@ static int mvebu_devs_debug_show(struct seq_file *seq, void *v) return 0; } - -static int mvebu_devs_debug_open(struct inode *inode, struct file *file) -{ - return single_open(file, mvebu_devs_debug_show, inode->i_private); -} - -static const struct file_operations mvebu_devs_debug_fops = { - .open = mvebu_devs_debug_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(mvebu_devs_debug); /* * SoC-specific functions and definitions -- cgit v1.2.3 From 3e081438b8e639cc76ef1a5ce0c1bd8a154082c7 Mon Sep 17 00:00:00 2001 From: "Guilherme G. Piccoli" Date: Fri, 9 Sep 2022 17:07:55 -0300 Subject: firmware: google: Test spinlock on panic path to avoid lockups Currently the gsmi driver registers a panic notifier as well as reboot and die notifiers. The callbacks registered are called in atomic and very limited context - for instance, panic disables preemption and local IRQs, also all secondary CPUs (not executing the panic path) are shutdown. With that said, taking a spinlock in this scenario is a dangerous invitation for lockup scenarios. So, fix that by checking if the spinlock is free to acquire in the panic notifier callback - if not, bail-out and avoid a potential hang. Fixes: 74c5b31c6618 ("driver: Google EFI SMI") Cc: Andrew Morton Cc: Ard Biesheuvel Cc: David Gow Cc: Greg Kroah-Hartman Cc: Julius Werner Cc: Petr Mladek Reviewed-by: Evan Green Signed-off-by: Guilherme G. Piccoli Link: https://lore.kernel.org/r/20220909200755.189679-1-gpiccoli@igalia.com Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/google/gsmi.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/firmware/google/gsmi.c b/drivers/firmware/google/gsmi.c index adaa492c3d2d..4e2575dfeb90 100644 --- a/drivers/firmware/google/gsmi.c +++ b/drivers/firmware/google/gsmi.c @@ -681,6 +681,15 @@ static struct notifier_block gsmi_die_notifier = { static int gsmi_panic_callback(struct notifier_block *nb, unsigned long reason, void *arg) { + + /* + * Panic callbacks are executed with all other CPUs stopped, + * so we must not attempt to spin waiting for gsmi_dev.lock + * to be released. + */ + if (spin_is_locked(&gsmi_dev.lock)) + return NOTIFY_DONE; + gsmi_shutdown_reason(GSMI_SHUTDOWN_PANIC); return NOTIFY_DONE; } -- cgit v1.2.3 From 9483811a126a319ecac749f1b767ea5faecc7aed Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 30 Jul 2022 20:04:59 +0200 Subject: extcon: usbc-tusb320: Factor out extcon into dedicated functions Move extcon code into separate functions in preparation for addition of USB TYPE-C support. No functional change. Signed-off-by: Marek Vasut Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-usbc-tusb320.c | 75 ++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 29 deletions(-) diff --git a/drivers/extcon/extcon-usbc-tusb320.c b/drivers/extcon/extcon-usbc-tusb320.c index 6ba3d89b106d..aced4bbb455d 100644 --- a/drivers/extcon/extcon-usbc-tusb320.c +++ b/drivers/extcon/extcon-usbc-tusb320.c @@ -184,19 +184,9 @@ static struct tusb320_ops tusb320l_ops = { .get_revision = tusb320l_get_revision, }; -static irqreturn_t tusb320_irq_handler(int irq, void *dev_id) +static void tusb320_extcon_irq_handler(struct tusb320_priv *priv, u8 reg) { - struct tusb320_priv *priv = dev_id; int state, polarity; - unsigned reg; - - if (regmap_read(priv->regmap, TUSB320_REG9, ®)) { - dev_err(priv->dev, "error during i2c read!\n"); - return IRQ_NONE; - } - - if (!(reg & TUSB320_REG9_INTERRUPT_STATUS)) - return IRQ_NONE; state = (reg >> TUSB320_REG9_ATTACHED_STATE_SHIFT) & TUSB320_REG9_ATTACHED_STATE_MASK; @@ -219,6 +209,22 @@ static irqreturn_t tusb320_irq_handler(int irq, void *dev_id) extcon_sync(priv->edev, EXTCON_USB_HOST); priv->state = state; +} + +static irqreturn_t tusb320_irq_handler(int irq, void *dev_id) +{ + struct tusb320_priv *priv = dev_id; + unsigned int reg; + + if (regmap_read(priv->regmap, TUSB320_REG9, ®)) { + dev_err(priv->dev, "error during i2c read!\n"); + return IRQ_NONE; + } + + if (!(reg & TUSB320_REG9_INTERRUPT_STATUS)) + return IRQ_NONE; + + tusb320_extcon_irq_handler(priv, reg); regmap_write(priv->regmap, TUSB320_REG9, reg); @@ -230,8 +236,32 @@ static const struct regmap_config tusb320_regmap_config = { .val_bits = 8, }; -static int tusb320_extcon_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tusb320_extcon_probe(struct tusb320_priv *priv) +{ + int ret; + + 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); + } + + ret = devm_extcon_dev_register(priv->dev, priv->edev); + if (ret < 0) { + dev_err(priv->dev, "failed to register extcon device\n"); + return ret; + } + + extcon_set_property_capability(priv->edev, EXTCON_USB, + EXTCON_PROP_USB_TYPEC_POLARITY); + extcon_set_property_capability(priv->edev, EXTCON_USB_HOST, + EXTCON_PROP_USB_TYPEC_POLARITY); + + return 0; +} + +static int tusb320_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct tusb320_priv *priv; const void *match_data; @@ -257,12 +287,6 @@ static int tusb320_extcon_probe(struct i2c_client *client, 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) @@ -272,16 +296,9 @@ static int tusb320_extcon_probe(struct i2c_client *client, 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"); + ret = tusb320_extcon_probe(priv); + if (ret) return ret; - } - - extcon_set_property_capability(priv->edev, EXTCON_USB, - EXTCON_PROP_USB_TYPEC_POLARITY); - extcon_set_property_capability(priv->edev, EXTCON_USB_HOST, - EXTCON_PROP_USB_TYPEC_POLARITY); /* update initial state */ tusb320_irq_handler(client->irq, priv); @@ -313,7 +330,7 @@ static const struct of_device_id tusb320_extcon_dt_match[] = { MODULE_DEVICE_TABLE(of, tusb320_extcon_dt_match); static struct i2c_driver tusb320_extcon_driver = { - .probe = tusb320_extcon_probe, + .probe = tusb320_probe, .driver = { .name = "extcon-tusb320", .of_match_table = tusb320_extcon_dt_match, -- cgit v1.2.3 From bf7571c00dca0a9c5af3f5125ef5a89a40b13cd5 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 30 Jul 2022 20:05:00 +0200 Subject: extcon: usbc-tusb320: Add USB TYPE-C support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TI TUSB320 seems like a better fit for USB TYPE-C subsystem, which can expose details collected by the TUSB320 in a far more precise way than extcon. Since there are existing users in the kernel and in DT which depend on the extcon interface, keep it for now. Add TYPE-C interface and expose the supported supply current, direction and connector polarity via the TYPE-C interface. Signed-off-by: Marek Vasut Acked-by: Heikki Krogerus Reviewed-by: Alvin Å ipraga Signed-off-by: Chanwoo Choi --- drivers/extcon/Kconfig | 2 +- drivers/extcon/extcon-usbc-tusb320.c | 159 +++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+), 1 deletion(-) diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index dca7cecb37e3..290186e44e6b 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -183,7 +183,7 @@ config EXTCON_USBC_CROS_EC config EXTCON_USBC_TUSB320 tristate "TI TUSB320 USB-C extcon support" - depends on I2C + depends on I2C && TYPEC select REGMAP_I2C help Say Y here to enable support for USB Type C cable detection extcon diff --git a/drivers/extcon/extcon-usbc-tusb320.c b/drivers/extcon/extcon-usbc-tusb320.c index aced4bbb455d..edb8c3f997c9 100644 --- a/drivers/extcon/extcon-usbc-tusb320.c +++ b/drivers/extcon/extcon-usbc-tusb320.c @@ -6,6 +6,7 @@ * Author: Michael Auchter */ +#include #include #include #include @@ -13,6 +14,24 @@ #include #include #include +#include + +#define TUSB320_REG8 0x8 +#define TUSB320_REG8_CURRENT_MODE_ADVERTISE GENMASK(7, 6) +#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_USB 0x0 +#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_15A 0x1 +#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_30A 0x2 +#define TUSB320_REG8_CURRENT_MODE_DETECT GENMASK(5, 4) +#define TUSB320_REG8_CURRENT_MODE_DETECT_DEF 0x0 +#define TUSB320_REG8_CURRENT_MODE_DETECT_MED 0x1 +#define TUSB320_REG8_CURRENT_MODE_DETECT_ACC 0x2 +#define TUSB320_REG8_CURRENT_MODE_DETECT_HI 0x3 +#define TUSB320_REG8_ACCESSORY_CONNECTED GENMASK(3, 2) +#define TUSB320_REG8_ACCESSORY_CONNECTED_NONE 0x0 +#define TUSB320_REG8_ACCESSORY_CONNECTED_AUDIO 0x4 +#define TUSB320_REG8_ACCESSORY_CONNECTED_ACC 0x5 +#define TUSB320_REG8_ACCESSORY_CONNECTED_DEBUG 0x6 +#define TUSB320_REG8_ACTIVE_CABLE_DETECTION BIT(0) #define TUSB320_REG9 0x9 #define TUSB320_REG9_ATTACHED_STATE_SHIFT 6 @@ -55,6 +74,10 @@ struct tusb320_priv { struct extcon_dev *edev; struct tusb320_ops *ops; enum tusb320_attached_state state; + struct typec_port *port; + struct typec_capability cap; + enum typec_port_type port_type; + enum typec_pwr_opmode pwr_opmode; }; static const char * const tusb_attached_states[] = { @@ -184,6 +207,44 @@ static struct tusb320_ops tusb320l_ops = { .get_revision = tusb320l_get_revision, }; +static int tusb320_set_adv_pwr_mode(struct tusb320_priv *priv) +{ + u8 mode; + + if (priv->pwr_opmode == TYPEC_PWR_MODE_USB) + mode = TUSB320_REG8_CURRENT_MODE_ADVERTISE_USB; + else if (priv->pwr_opmode == TYPEC_PWR_MODE_1_5A) + mode = TUSB320_REG8_CURRENT_MODE_ADVERTISE_15A; + else if (priv->pwr_opmode == TYPEC_PWR_MODE_3_0A) + mode = TUSB320_REG8_CURRENT_MODE_ADVERTISE_30A; + else /* No other mode is supported. */ + return -EINVAL; + + return regmap_write_bits(priv->regmap, TUSB320_REG8, + TUSB320_REG8_CURRENT_MODE_ADVERTISE, + FIELD_PREP(TUSB320_REG8_CURRENT_MODE_ADVERTISE, + mode)); +} + +static int tusb320_port_type_set(struct typec_port *port, + enum typec_port_type type) +{ + struct tusb320_priv *priv = typec_get_drvdata(port); + + if (type == TYPEC_PORT_SRC) + return priv->ops->set_mode(priv, TUSB320_MODE_DFP); + else if (type == TYPEC_PORT_SNK) + return priv->ops->set_mode(priv, TUSB320_MODE_UFP); + else if (type == TYPEC_PORT_DRP) + return priv->ops->set_mode(priv, TUSB320_MODE_DRP); + else + return priv->ops->set_mode(priv, TUSB320_MODE_PORT); +} + +static const struct typec_operations tusb320_typec_ops = { + .port_type_set = tusb320_port_type_set, +}; + static void tusb320_extcon_irq_handler(struct tusb320_priv *priv, u8 reg) { int state, polarity; @@ -211,6 +272,47 @@ static void tusb320_extcon_irq_handler(struct tusb320_priv *priv, u8 reg) priv->state = state; } +static void tusb320_typec_irq_handler(struct tusb320_priv *priv, u8 reg9) +{ + struct typec_port *port = priv->port; + struct device *dev = priv->dev; + u8 mode, role, state; + int ret, reg8; + bool ori; + + ori = reg9 & TUSB320_REG9_CABLE_DIRECTION; + typec_set_orientation(port, ori ? TYPEC_ORIENTATION_REVERSE : + TYPEC_ORIENTATION_NORMAL); + + state = (reg9 >> TUSB320_REG9_ATTACHED_STATE_SHIFT) & + TUSB320_REG9_ATTACHED_STATE_MASK; + if (state == TUSB320_ATTACHED_STATE_DFP) + role = TYPEC_SOURCE; + else + role = TYPEC_SINK; + + typec_set_vconn_role(port, role); + typec_set_pwr_role(port, role); + typec_set_data_role(port, role == TYPEC_SOURCE ? + TYPEC_HOST : TYPEC_DEVICE); + + ret = regmap_read(priv->regmap, TUSB320_REG8, ®8); + if (ret) { + dev_err(dev, "error during reg8 i2c read, ret=%d!\n", ret); + return; + } + + mode = FIELD_GET(TUSB320_REG8_CURRENT_MODE_DETECT, reg8); + if (mode == TUSB320_REG8_CURRENT_MODE_DETECT_DEF) + typec_set_pwr_opmode(port, TYPEC_PWR_MODE_USB); + else if (mode == TUSB320_REG8_CURRENT_MODE_DETECT_MED) + typec_set_pwr_opmode(port, TYPEC_PWR_MODE_1_5A); + else if (mode == TUSB320_REG8_CURRENT_MODE_DETECT_HI) + typec_set_pwr_opmode(port, TYPEC_PWR_MODE_3_0A); + else /* Charge through accessory */ + typec_set_pwr_opmode(port, TYPEC_PWR_MODE_USB); +} + static irqreturn_t tusb320_irq_handler(int irq, void *dev_id) { struct tusb320_priv *priv = dev_id; @@ -225,6 +327,7 @@ static irqreturn_t tusb320_irq_handler(int irq, void *dev_id) return IRQ_NONE; tusb320_extcon_irq_handler(priv, reg); + tusb320_typec_irq_handler(priv, reg); regmap_write(priv->regmap, TUSB320_REG9, reg); @@ -260,6 +363,58 @@ static int tusb320_extcon_probe(struct tusb320_priv *priv) return 0; } +static int tusb320_typec_probe(struct i2c_client *client, + struct tusb320_priv *priv) +{ + struct fwnode_handle *connector; + const char *cap_str; + int ret; + + /* The Type-C connector is optional, for backward compatibility. */ + connector = device_get_named_child_node(&client->dev, "connector"); + if (!connector) + return 0; + + /* Type-C connector found. */ + ret = typec_get_fw_cap(&priv->cap, connector); + if (ret) + return ret; + + priv->port_type = priv->cap.type; + + /* This goes into register 0x8 field CURRENT_MODE_ADVERTISE */ + ret = fwnode_property_read_string(connector, "typec-power-opmode", &cap_str); + if (ret) + return ret; + + ret = typec_find_pwr_opmode(cap_str); + if (ret < 0) + return ret; + if (ret == TYPEC_PWR_MODE_PD) + return -EINVAL; + + priv->pwr_opmode = ret; + + /* Initialize the hardware with the devicetree settings. */ + ret = tusb320_set_adv_pwr_mode(priv); + if (ret) + return ret; + + priv->cap.revision = USB_TYPEC_REV_1_1; + priv->cap.accessory[0] = TYPEC_ACCESSORY_AUDIO; + priv->cap.accessory[1] = TYPEC_ACCESSORY_DEBUG; + priv->cap.orientation_aware = true; + priv->cap.driver_data = priv; + priv->cap.ops = &tusb320_typec_ops; + priv->cap.fwnode = connector; + + priv->port = typec_register_port(&client->dev, &priv->cap); + if (IS_ERR(priv->port)) + return PTR_ERR(priv->port); + + return 0; +} + static int tusb320_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -300,6 +455,10 @@ static int tusb320_probe(struct i2c_client *client, if (ret) return ret; + ret = tusb320_typec_probe(client, priv); + if (ret) + return ret; + /* update initial state */ tusb320_irq_handler(client->irq, priv); -- cgit v1.2.3 From 08099ecd9216219f51cc82637f06797cf81890b6 Mon Sep 17 00:00:00 2001 From: Rong Chen Date: Tue, 6 Sep 2022 12:04:29 +0800 Subject: extcon: usbc-tusb320: fix kernel-doc warning Fix the warning: drivers/extcon/extcon-usbc-tusb320.c:19: warning: expecting prototype for drivers/extcon/extcon-tusb320.c(). Prototype was for TUSB320_REG8() instead Reported-by: kernel test robot Signed-off-by: Rong Chen Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-usbc-tusb320.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/extcon/extcon-usbc-tusb320.c b/drivers/extcon/extcon-usbc-tusb320.c index edb8c3f997c9..41041ff0fadb 100644 --- a/drivers/extcon/extcon-usbc-tusb320.c +++ b/drivers/extcon/extcon-usbc-tusb320.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * drivers/extcon/extcon-tusb320.c - TUSB320 extcon driver * * Copyright (C) 2020 National Instruments Corporation -- cgit v1.2.3 From f157555ea3b0f1f7ff19ca7d52576ab231fabfad Mon Sep 17 00:00:00 2001 From: Luo Xueqin Date: Tue, 5 Jul 2022 23:27:57 +0800 Subject: fsi: Fix typo in comment Spelling mistake in comment. Reported-by: k2ci Signed-off-by: Luo Xueqin Link: https://lore.kernel.org/r/20220705152757.27843-1-luoxueqin66@gmail.com Signed-off-by: Joel Stanley --- drivers/fsi/fsi-master.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h index cd6bee5e12a7..4762315a46ba 100644 --- a/drivers/fsi/fsi-master.h +++ b/drivers/fsi/fsi-master.h @@ -51,7 +51,7 @@ #define FSI_MMODE_CRS1SHFT 8 /* Clk rate selection 1 shift */ #define FSI_MMODE_CRS1MASK 0x3ff /* Clk rate selection 1 mask */ -/* MRESB: Reset brindge */ +/* MRESB: Reset bridge */ #define FSI_MRESB_RST_GEN 0x80000000 /* General reset */ #define FSI_MRESB_RST_ERR 0x40000000 /* Error Reset */ -- cgit v1.2.3 From 7326939faa4b8d078dd2b8cddc36b5758d9b984c Mon Sep 17 00:00:00 2001 From: Eddie James Date: Tue, 26 Apr 2022 10:49:55 -0500 Subject: fsi: occ: Fix checksum failure mode Change the checksum errno to something different than the errno used for a bad SBE message. In addition, don't set the user's response length to the data length in this case, since it's not SBE FFDC. Signed-off-by: Eddie James Link: https://lore.kernel.org/r/20220426154956.27205-2-eajames@linux.ibm.com Signed-off-by: Joel Stanley --- drivers/fsi/fsi-occ.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/fsi/fsi-occ.c b/drivers/fsi/fsi-occ.c index c9cc75fbdfb9..3d04e8baecbb 100644 --- a/drivers/fsi/fsi-occ.c +++ b/drivers/fsi/fsi-occ.c @@ -246,7 +246,7 @@ static int occ_verify_checksum(struct occ *occ, struct occ_response *resp, if (checksum != checksum_resp) { dev_err(occ->dev, "Bad checksum: %04x!=%04x\n", checksum, checksum_resp); - return -EBADMSG; + return -EBADE; } return 0; @@ -575,8 +575,11 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len, dev_dbg(dev, "resp_status=%02x resp_data_len=%d\n", resp->return_status, resp_data_length); - occ->client_response_size = resp_data_length + 7; rc = occ_verify_checksum(occ, resp, resp_data_length); + if (rc) + goto done; + + occ->client_response_size = resp_data_length + 7; done: *resp_len = occ->client_response_size; -- cgit v1.2.3 From dbed963ed62c4c2b8870a02c8b7dcb0c2af3ee0b Mon Sep 17 00:00:00 2001 From: Eddie James Date: Tue, 26 Apr 2022 10:49:56 -0500 Subject: hwmon (occ): Retry for checksum failure Due to the OCC communication design with a shared SRAM area, checkum errors are expected due to corrupted buffer from OCC communications with other system components. Therefore, retry the command twice in the event of a checksum failure. Signed-off-by: Eddie James Acked-by: Guenter Roeck Link: https://lore.kernel.org/r/20220426154956.27205-3-eajames@linux.ibm.com Signed-off-by: Joel Stanley --- drivers/hwmon/occ/p9_sbe.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/occ/p9_sbe.c b/drivers/hwmon/occ/p9_sbe.c index c1e0a1d96cd4..f3791a589b01 100644 --- a/drivers/hwmon/occ/p9_sbe.c +++ b/drivers/hwmon/occ/p9_sbe.c @@ -14,6 +14,8 @@ #include "common.h" +#define OCC_CHECKSUM_RETRIES 3 + struct p9_sbe_occ { struct occ occ; bool sbe_error; @@ -80,18 +82,23 @@ done: static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd, size_t len, void *resp, size_t resp_len) { + size_t original_resp_len = resp_len; struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ); - int rc; + int rc, i; - rc = fsi_occ_submit(ctx->sbe, cmd, len, resp, &resp_len); - if (rc < 0) { + for (i = 0; i < OCC_CHECKSUM_RETRIES; ++i) { + rc = fsi_occ_submit(ctx->sbe, cmd, len, resp, &resp_len); + if (rc >= 0) + break; 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; } - - return rc; + if (rc != -EBADE) + return rc; + resp_len = original_resp_len; } switch (((struct occ_response *)resp)->return_status) { -- cgit v1.2.3 From d3e1e24604031b0d83b6c2d38f54eeea265cfcc0 Mon Sep 17 00:00:00 2001 From: Eddie James Date: Fri, 13 May 2022 14:44:24 -0500 Subject: fsi: occ: Prevent use after free Use get_device and put_device in the open and close functions to make sure the device doesn't get freed while a file descriptor is open. Also, lock around the freeing of the device buffer and check the buffer before using it in the submit function. Signed-off-by: Eddie James Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20220513194424.53468-1-eajames@linux.ibm.com Signed-off-by: Joel Stanley --- drivers/fsi/fsi-occ.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/fsi/fsi-occ.c b/drivers/fsi/fsi-occ.c index 3d04e8baecbb..8f7f602b909d 100644 --- a/drivers/fsi/fsi-occ.c +++ b/drivers/fsi/fsi-occ.c @@ -94,6 +94,7 @@ static int occ_open(struct inode *inode, struct file *file) client->occ = occ; mutex_init(&client->lock); file->private_data = client; + get_device(occ->dev); /* We allocate a 1-page buffer, make sure it all fits */ BUILD_BUG_ON((OCC_CMD_DATA_BYTES + 3) > PAGE_SIZE); @@ -197,6 +198,7 @@ static int occ_release(struct inode *inode, struct file *file) { struct occ_client *client = file->private_data; + put_device(client->occ->dev); free_page((unsigned long)client->buffer); kfree(client); @@ -493,12 +495,19 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len, for (i = 1; i < req_len - 2; ++i) checksum += byte_request[i]; - mutex_lock(&occ->occ_lock); + rc = mutex_lock_interruptible(&occ->occ_lock); + if (rc) + return rc; occ->client_buffer = response; occ->client_buffer_size = user_resp_len; occ->client_response_size = 0; + if (!occ->buffer) { + rc = -ENOENT; + goto done; + } + /* * Get a sequence number and update the counter. Avoid a sequence * number of 0 which would pass the response check below even if the @@ -674,10 +683,13 @@ static int occ_remove(struct platform_device *pdev) { struct occ *occ = platform_get_drvdata(pdev); - kvfree(occ->buffer); - misc_deregister(&occ->mdev); + mutex_lock(&occ->occ_lock); + kvfree(occ->buffer); + occ->buffer = NULL; + mutex_unlock(&occ->occ_lock); + device_for_each_child(&pdev->dev, NULL, occ_unregister_child); ida_simple_remove(&occ_ida, occ->idx); -- cgit v1.2.3 From 362fbc830a7ecd440d797c2ddce53020fd1020f5 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Sun, 3 Apr 2022 10:09:37 -0400 Subject: fsi: cleanup extern usage in function definition Smatch reports these issues fsi-core.c:395:12: warning: function 'fsi_slave_claim_range' with external linkage has definition fsi-core.c:409:13: warning: function 'fsi_slave_release_range' with external linkage has definition The storage-class-specifier extern is not needed in a definition, so remove it. Signed-off-by: Tom Rix Link: https://lore.kernel.org/r/20220403140937.3833578-1-trix@redhat.com Signed-off-by: Joel Stanley --- drivers/fsi/fsi-core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c index 3a7b78e36701..0d11a17c53a0 100644 --- a/drivers/fsi/fsi-core.c +++ b/drivers/fsi/fsi-core.c @@ -392,8 +392,8 @@ int fsi_slave_write(struct fsi_slave *slave, uint32_t addr, } EXPORT_SYMBOL_GPL(fsi_slave_write); -extern int fsi_slave_claim_range(struct fsi_slave *slave, - uint32_t addr, uint32_t size) +int fsi_slave_claim_range(struct fsi_slave *slave, + uint32_t addr, uint32_t size) { if (addr + size < addr) return -EINVAL; @@ -406,8 +406,8 @@ extern int fsi_slave_claim_range(struct fsi_slave *slave, } EXPORT_SYMBOL_GPL(fsi_slave_claim_range); -extern void fsi_slave_release_range(struct fsi_slave *slave, - uint32_t addr, uint32_t size) +void fsi_slave_release_range(struct fsi_slave *slave, + uint32_t addr, uint32_t size) { } EXPORT_SYMBOL_GPL(fsi_slave_release_range); -- cgit v1.2.3 From b1534a05e1f283a157231f1b334f34d07fdda511 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Fri, 15 Apr 2022 14:37:57 +0930 Subject: fsi: sbefifo: Add detailed debugging information Provide more output on the timeout status, and make some vdbg calls into dbg calls so they can be enabled at runtime. Signed-off-by: Joel Stanley Link: https://lore.kernel.org/r/20220415050757.281158-1-joel@jms.id.au Signed-off-by: Joel Stanley --- drivers/fsi/fsi-sbefifo.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c index f52a912cdf16..5f93a53846aa 100644 --- a/drivers/fsi/fsi-sbefifo.c +++ b/drivers/fsi/fsi-sbefifo.c @@ -477,7 +477,8 @@ static int sbefifo_wait(struct sbefifo *sbefifo, bool up, 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); + dev_err(dev, "%s FIFO Timeout (%u ms)! status=%08x\n", + up ? "UP" : "DOWN", jiffies_to_msecs(timeout), sts); return -ETIMEDOUT; } dev_vdbg(dev, "End of wait status: %08x\n", sts); @@ -497,8 +498,8 @@ static int sbefifo_send_command(struct sbefifo *sbefifo, u32 status; int rc; - dev_vdbg(dev, "sending command (%zd words, cmd=%04x)\n", - cmd_len, be32_to_cpu(command[1])); + dev_dbg(dev, "sending command (%zd words, cmd=%04x)\n", + cmd_len, be32_to_cpu(command[1])); /* As long as there's something to send */ timeout = msecs_to_jiffies(SBEFIFO_TIMEOUT_START_CMD); @@ -551,21 +552,23 @@ static int sbefifo_read_response(struct sbefifo *sbefifo, struct iov_iter *respo size_t len; int rc; - dev_vdbg(dev, "reading response, buflen = %zd\n", iov_iter_count(response)); + dev_dbg(dev, "reading response, buflen = %zd\n", iov_iter_count(response)); timeout = msecs_to_jiffies(sbefifo->timeout_start_rsp_ms); for (;;) { /* Grab FIFO status (this will handle parity errors) */ rc = sbefifo_wait(sbefifo, false, &status, timeout); - if (rc < 0) + if (rc < 0) { + dev_dbg(dev, "timeout waiting (%u ms)\n", jiffies_to_msecs(timeout)); return rc; + } timeout = msecs_to_jiffies(SBEFIFO_TIMEOUT_IN_RSP); /* Decode status */ len = sbefifo_populated(status); eot_set = sbefifo_eot_set(status); - dev_vdbg(dev, " chunk size %zd eot_set=0x%x\n", len, eot_set); + dev_dbg(dev, " chunk size %zd eot_set=0x%x\n", len, eot_set); /* Go through the chunk */ while(len--) { -- cgit v1.2.3 From 182d98e00e4745fe253cb0c24c63bbac253464a2 Mon Sep 17 00:00:00 2001 From: Lv Ruyi Date: Thu, 7 Apr 2022 08:59:11 +0000 Subject: fsi: master-ast-cf: Fix missing of_node_put in fsi_master_acf_probe of_parse_phandle returns node pointer with refcount incremented, use of_node_put() on it when done. Reported-by: Zeal Robot Signed-off-by: Lv Ruyi Link: https://lore.kernel.org/r/20220407085911.2491719-1-lv.ruyi@zte.com.cn Signed-off-by: Joel Stanley --- drivers/fsi/fsi-master-ast-cf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/fsi/fsi-master-ast-cf.c b/drivers/fsi/fsi-master-ast-cf.c index 24292acdbaf8..5f608ef8b53c 100644 --- a/drivers/fsi/fsi-master-ast-cf.c +++ b/drivers/fsi/fsi-master-ast-cf.c @@ -1324,12 +1324,14 @@ static int fsi_master_acf_probe(struct platform_device *pdev) } master->cvic = devm_of_iomap(&pdev->dev, np, 0, NULL); if (IS_ERR(master->cvic)) { + of_node_put(np); rc = PTR_ERR(master->cvic); dev_err(&pdev->dev, "Error %d mapping CVIC\n", rc); goto err_free; } rc = of_property_read_u32(np, "copro-sw-interrupts", &master->cvic_sw_irq); + of_node_put(np); if (rc) { dev_err(&pdev->dev, "Can't find coprocessor SW interrupt\n"); goto err_free; -- cgit v1.2.3 From 8d4c99002d2f38edd50a9896a29a1650be3de1af Mon Sep 17 00:00:00 2001 From: Eddie James Date: Tue, 9 Aug 2022 15:06:59 -0500 Subject: dt-bindings: hwmon: Add IBM OCC bindings These bindings describe the POWER processor On Chip Controller accessed from a service processor or baseboard management controller (BMC). Signed-off-by: Eddie James Link: https://lore.kernel.org/r/20220809200701.218059-2-eajames@linux.ibm.com Signed-off-by: Joel Stanley --- .../devicetree/bindings/hwmon/ibm,occ-hwmon.yaml | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/ibm,occ-hwmon.yaml diff --git a/Documentation/devicetree/bindings/hwmon/ibm,occ-hwmon.yaml b/Documentation/devicetree/bindings/hwmon/ibm,occ-hwmon.yaml new file mode 100644 index 000000000000..3dbdc5af2804 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/ibm,occ-hwmon.yaml @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/ibm,occ-hwmon.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: IBM On-Chip Controller (OCC) accessed from a service processor + +maintainers: + - Eddie James + +description: | + The POWER processor On-Chip Controller (OCC) helps manage power and + thermals for the system. A service processor or baseboard management + controller can query the OCC for it's power and thermal data to report + through hwmon. + +properties: + compatible: + enum: + - ibm,p9-occ-hwmon + - ibm,p10-occ-hwmon + + ibm,no-poll-on-init: + description: This property describes whether or not the OCC should + be polled during driver initialization. + type: boolean + +required: + - compatible + +additionalProperties: false + +examples: + - | + hwmon { + compatible = "ibm,p10-occ-hwmon"; + ibm,no-poll-on-init; + }; -- cgit v1.2.3 From 0fead4fc926f000d2daee938f0d4886ac8da11d0 Mon Sep 17 00:00:00 2001 From: Eddie James Date: Tue, 9 Aug 2022 15:07:00 -0500 Subject: fsi: occ: Support probing the hwmon child device from dts node There is now a need for reading devicetree properties in the OCC hwmon driver, which isn't current supported as the FSI driver just instantiates a basic platform device. Add support for this use case by checking for an "occ-hwmon" node and if present, creating an OF device from it. Signed-off-by: Eddie James Link: https://lore.kernel.org/r/20220809200701.218059-3-eajames@linux.ibm.com Signed-off-by: Joel Stanley --- drivers/fsi/fsi-occ.c | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/drivers/fsi/fsi-occ.c b/drivers/fsi/fsi-occ.c index 8f7f602b909d..abdd37d5507f 100644 --- a/drivers/fsi/fsi-occ.c +++ b/drivers/fsi/fsi-occ.c @@ -44,6 +44,7 @@ struct occ { struct device *sbefifo; char name[32]; int idx; + bool platform_hwmon; u8 sequence_number; void *buffer; void *client_buffer; @@ -598,7 +599,7 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len, } EXPORT_SYMBOL_GPL(fsi_occ_submit); -static int occ_unregister_child(struct device *dev, void *data) +static int occ_unregister_platform_child(struct device *dev, void *data) { struct platform_device *hwmon_dev = to_platform_device(dev); @@ -607,12 +608,25 @@ static int occ_unregister_child(struct device *dev, void *data) return 0; } +static int occ_unregister_of_child(struct device *dev, void *data) +{ + struct platform_device *hwmon_dev = to_platform_device(dev); + + of_device_unregister(hwmon_dev); + if (dev->of_node) + of_node_clear_flag(dev->of_node, OF_POPULATED); + + return 0; +} + static int occ_probe(struct platform_device *pdev) { int rc; u32 reg; + char child_name[32]; struct occ *occ; - struct platform_device *hwmon_dev; + struct platform_device *hwmon_dev = NULL; + struct device_node *hwmon_node; struct device *dev = &pdev->dev; struct platform_device_info hwmon_dev_info = { .parent = dev, @@ -671,10 +685,20 @@ static int occ_probe(struct platform_device *pdev) return rc; } - hwmon_dev_info.id = occ->idx; - hwmon_dev = platform_device_register_full(&hwmon_dev_info); - if (IS_ERR(hwmon_dev)) - dev_warn(dev, "failed to create hwmon device\n"); + hwmon_node = of_get_child_by_name(dev->of_node, hwmon_dev_info.name); + if (hwmon_node) { + snprintf(child_name, sizeof(child_name), "%s.%d", hwmon_dev_info.name, occ->idx); + hwmon_dev = of_platform_device_create(hwmon_node, child_name, dev); + of_node_put(hwmon_node); + } + + if (!hwmon_dev) { + occ->platform_hwmon = true; + hwmon_dev_info.id = occ->idx; + hwmon_dev = platform_device_register_full(&hwmon_dev_info); + if (IS_ERR(hwmon_dev)) + dev_warn(dev, "failed to create hwmon device\n"); + } return 0; } @@ -690,7 +714,10 @@ static int occ_remove(struct platform_device *pdev) occ->buffer = NULL; mutex_unlock(&occ->occ_lock); - device_for_each_child(&pdev->dev, NULL, occ_unregister_child); + if (occ->platform_hwmon) + device_for_each_child(&pdev->dev, NULL, occ_unregister_platform_child); + else + device_for_each_child(&pdev->dev, NULL, occ_unregister_of_child); ida_simple_remove(&occ_ida, occ->idx); -- cgit v1.2.3 From 89a286fb153643b9b0f6e78160857f86eef16ba5 Mon Sep 17 00:00:00 2001 From: Eddie James Date: Tue, 9 Aug 2022 15:07:01 -0500 Subject: hwmon: (occ) Check for device property for setting OCC active during probe A previous commit changed the existing behavior of the driver to skip attempting to communicate with the OCC during probe. Return to the previous default behavior of automatically communicating with the OCC and make it optional with a new device-tree property. Signed-off-by: Eddie James Acked-by: Guenter Roeck Link: https://lore.kernel.org/r/20220809200701.218059-4-eajames@linux.ibm.com Signed-off-by: Joel Stanley --- drivers/hwmon/occ/common.c | 11 ++++++++++- drivers/hwmon/occ/p9_sbe.c | 9 +++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c index 45407b12db4b..dd690f700d49 100644 --- a/drivers/hwmon/occ/common.c +++ b/drivers/hwmon/occ/common.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -1216,8 +1217,16 @@ int occ_setup(struct occ *occ) occ->groups[0] = &occ->group; rc = occ_setup_sysfs(occ); - if (rc) + if (rc) { dev_err(occ->bus_dev, "failed to setup sysfs: %d\n", rc); + return rc; + } + + if (!device_property_read_bool(occ->bus_dev, "ibm,no-poll-on-init")) { + rc = occ_active(occ, true); + if (rc) + occ_shutdown_sysfs(occ); + } return rc; } diff --git a/drivers/hwmon/occ/p9_sbe.c b/drivers/hwmon/occ/p9_sbe.c index f3791a589b01..96521363b696 100644 --- a/drivers/hwmon/occ/p9_sbe.c +++ b/drivers/hwmon/occ/p9_sbe.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -181,9 +182,17 @@ static int p9_sbe_occ_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id p9_sbe_occ_of_match[] = { + { .compatible = "ibm,p9-occ-hwmon" }, + { .compatible = "ibm,p10-occ-hwmon" }, + {} +}; +MODULE_DEVICE_TABLE(of, p9_sbe_occ_of_match); + static struct platform_driver p9_sbe_occ_driver = { .driver = { .name = "occ-hwmon", + .of_match_table = p9_sbe_occ_of_match, }, .probe = p9_sbe_occ_probe, .remove = p9_sbe_occ_remove, -- cgit v1.2.3 From 35af9fb49bc5c6d61ef70b501c3a56fe161cce3e Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Tue, 11 Jan 2022 15:34:11 +0800 Subject: fsi: core: Check error number after calling ida_simple_get If allocation fails, the ida_simple_get() will return error number. So master->idx could be error number and be used in dev_set_name(). Therefore, it should be better to check it and return error if fails, like the ida_simple_get() in __fsi_get_new_minor(). Fixes: 09aecfab93b8 ("drivers/fsi: Add fsi master definition") Signed-off-by: Jiasheng Jiang Reviewed-by: Eddie James Link: https://lore.kernel.org/r/20220111073411.614138-1-jiasheng@iscas.ac.cn Signed-off-by: Joel Stanley --- drivers/fsi/fsi-core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c index 0d11a17c53a0..694e80c06665 100644 --- a/drivers/fsi/fsi-core.c +++ b/drivers/fsi/fsi-core.c @@ -1314,6 +1314,9 @@ int fsi_master_register(struct fsi_master *master) mutex_init(&master->scan_lock); master->idx = ida_simple_get(&master_ida, 0, INT_MAX, GFP_KERNEL); + if (master->idx < 0) + return master->idx; + dev_set_name(&master->dev, "fsi%d", master->idx); master->dev.class = &fsi_master_class; -- cgit v1.2.3 From 55e5832bb1f24237ce294acca5b2c67ed1197cbc Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Tue, 27 Sep 2022 18:53:35 -0400 Subject: MAINTAINERS: Update Counter subsystem git tree repo link The Counter subsystem git tree is now located on the kernel.org git server. Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/075c91bb0af32d27a139112701b12b118a50edd6.1664318353.git.william.gray@linaro.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index db50c8234da6..bccb697b346f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5285,7 +5285,7 @@ COUNTER SUBSYSTEM M: William Breathitt Gray L: linux-iio@vger.kernel.org S: Maintained -T: git https://git.linaro.org/people/william.gray/counter.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/wbg/counter.git F: Documentation/ABI/testing/sysfs-bus-counter F: Documentation/driver-api/generic-counter.rst F: drivers/counter/ -- cgit v1.2.3 From 3216e5512abdd1fc671ed8443ffc8fa9e4adc78c Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Tue, 27 Sep 2022 18:53:36 -0400 Subject: counter: Move symbols into COUNTER namespace Counter subsystem symbols are only relevant to counter drivers. A COUNTER namespace is created to control the availability of these symbols to modules that import this namespace explicitly. Cc: Patrick Havelange Cc: Jarkko Nikula Cc: Oleksij Rempel Cc: Pengutronix Kernel Team Cc: Kamel Bouhara Cc: Fabrice Gasnier Cc: Maxime Coquelin Cc: Alexandre Torgue Acked-by: David Lechner Reviewed-by: Jonathan Cameron Link: https://lore.kernel.org/r/20220815220321.74161-1-william.gray@linaro.org/ Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/8a756df96c24946547a7ece5caa5f654809c5e7f.1664318353.git.william.gray@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/counter/104-quad-8.c | 1 + drivers/counter/counter-chrdev.c | 2 +- drivers/counter/counter-core.c | 14 +++++++------- drivers/counter/ftm-quaddec.c | 1 + drivers/counter/intel-qep.c | 1 + drivers/counter/interrupt-cnt.c | 1 + drivers/counter/microchip-tcb-capture.c | 1 + drivers/counter/stm32-lptimer-cnt.c | 1 + drivers/counter/stm32-timer-cnt.c | 1 + drivers/counter/ti-eqep.c | 1 + 10 files changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c index 62c2b7ac4339..1323edfbe40c 100644 --- a/drivers/counter/104-quad-8.c +++ b/drivers/counter/104-quad-8.c @@ -1241,3 +1241,4 @@ module_isa_driver(quad8_driver, num_quad8); MODULE_AUTHOR("William Breathitt Gray "); MODULE_DESCRIPTION("ACCES 104-QUAD-8 driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(COUNTER); diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c index 69d340be9c93..4e71a19d7e6a 100644 --- a/drivers/counter/counter-chrdev.c +++ b/drivers/counter/counter-chrdev.c @@ -574,4 +574,4 @@ exit_early: if (copied) wake_up_poll(&counter->events_wait, EPOLLIN); } -EXPORT_SYMBOL_GPL(counter_push_event); +EXPORT_SYMBOL_NS_GPL(counter_push_event, COUNTER); diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c index 938651f9e9e0..09c77afb33ca 100644 --- a/drivers/counter/counter-core.c +++ b/drivers/counter/counter-core.c @@ -73,7 +73,7 @@ void *counter_priv(const struct counter_device *const counter) return &ch->privdata; } -EXPORT_SYMBOL_GPL(counter_priv); +EXPORT_SYMBOL_NS_GPL(counter_priv, COUNTER); /** * counter_alloc - allocate a counter_device @@ -133,13 +133,13 @@ err_ida_alloc: return NULL; } -EXPORT_SYMBOL_GPL(counter_alloc); +EXPORT_SYMBOL_NS_GPL(counter_alloc, COUNTER); void counter_put(struct counter_device *counter) { put_device(&counter->dev); } -EXPORT_SYMBOL_GPL(counter_put); +EXPORT_SYMBOL_NS_GPL(counter_put, COUNTER); /** * counter_add - complete registration of a counter @@ -166,7 +166,7 @@ int counter_add(struct counter_device *counter) /* implies device_add(dev) */ return cdev_device_add(&counter->chrdev, dev); } -EXPORT_SYMBOL_GPL(counter_add); +EXPORT_SYMBOL_NS_GPL(counter_add, COUNTER); /** * counter_unregister - unregister Counter from the system @@ -188,7 +188,7 @@ void counter_unregister(struct counter_device *const counter) mutex_unlock(&counter->ops_exist_lock); } -EXPORT_SYMBOL_GPL(counter_unregister); +EXPORT_SYMBOL_NS_GPL(counter_unregister, COUNTER); static void devm_counter_release(void *counter) { @@ -223,7 +223,7 @@ struct counter_device *devm_counter_alloc(struct device *dev, size_t sizeof_priv return counter; } -EXPORT_SYMBOL_GPL(devm_counter_alloc); +EXPORT_SYMBOL_NS_GPL(devm_counter_alloc, COUNTER); /** * devm_counter_add - complete registration of a counter @@ -244,7 +244,7 @@ int devm_counter_add(struct device *dev, return devm_add_action_or_reset(dev, devm_counter_release, counter); } -EXPORT_SYMBOL_GPL(devm_counter_add); +EXPORT_SYMBOL_NS_GPL(devm_counter_add, COUNTER); #define COUNTER_DEV_MAX 256 diff --git a/drivers/counter/ftm-quaddec.c b/drivers/counter/ftm-quaddec.c index 2a58582a9df4..aea6622a9b13 100644 --- a/drivers/counter/ftm-quaddec.c +++ b/drivers/counter/ftm-quaddec.c @@ -325,3 +325,4 @@ module_platform_driver(ftm_quaddec_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kjeld Flarup "); MODULE_AUTHOR("Patrick Havelange "); +MODULE_IMPORT_NS(COUNTER); diff --git a/drivers/counter/intel-qep.c b/drivers/counter/intel-qep.c index 47a6a9dfc9e8..af5942e66f7d 100644 --- a/drivers/counter/intel-qep.c +++ b/drivers/counter/intel-qep.c @@ -523,3 +523,4 @@ MODULE_AUTHOR("Jarkko Nikula "); MODULE_AUTHOR("Raymond Tan "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Intel Quadrature Encoder Peripheral driver"); +MODULE_IMPORT_NS(COUNTER); diff --git a/drivers/counter/interrupt-cnt.c b/drivers/counter/interrupt-cnt.c index 3b13f56bbb11..5a11b65fc0e5 100644 --- a/drivers/counter/interrupt-cnt.c +++ b/drivers/counter/interrupt-cnt.c @@ -242,3 +242,4 @@ MODULE_ALIAS("platform:interrupt-counter"); MODULE_AUTHOR("Oleksij Rempel "); MODULE_DESCRIPTION("Interrupt counter driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(COUNTER); diff --git a/drivers/counter/microchip-tcb-capture.c b/drivers/counter/microchip-tcb-capture.c index 00844445143b..f9dee15d9777 100644 --- a/drivers/counter/microchip-tcb-capture.c +++ b/drivers/counter/microchip-tcb-capture.c @@ -394,3 +394,4 @@ module_platform_driver(mchp_tc_driver); MODULE_AUTHOR("Kamel Bouhara "); MODULE_DESCRIPTION("Microchip TCB Capture driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(COUNTER); diff --git a/drivers/counter/stm32-lptimer-cnt.c b/drivers/counter/stm32-lptimer-cnt.c index 68031d93ce89..d6b80b6dfc28 100644 --- a/drivers/counter/stm32-lptimer-cnt.c +++ b/drivers/counter/stm32-lptimer-cnt.c @@ -520,3 +520,4 @@ MODULE_AUTHOR("Fabrice Gasnier "); MODULE_ALIAS("platform:stm32-lptimer-counter"); MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(COUNTER); diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c index 5779ae7c73cf..9bf20a5d6bda 100644 --- a/drivers/counter/stm32-timer-cnt.c +++ b/drivers/counter/stm32-timer-cnt.c @@ -417,3 +417,4 @@ MODULE_AUTHOR("Benjamin Gaignard "); MODULE_ALIAS("platform:stm32-timer-counter"); MODULE_DESCRIPTION("STMicroelectronics STM32 TIMER counter driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(COUNTER); diff --git a/drivers/counter/ti-eqep.c b/drivers/counter/ti-eqep.c index 0489d26eb47c..b0f24cf3e891 100644 --- a/drivers/counter/ti-eqep.c +++ b/drivers/counter/ti-eqep.c @@ -456,3 +456,4 @@ module_platform_driver(ti_eqep_driver); MODULE_AUTHOR("David Lechner "); MODULE_DESCRIPTION("TI eQEP counter driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(COUNTER); -- cgit v1.2.3 From 7bbf842cdcff30ddd1da4ab9059cf92f9d1a4326 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Tue, 27 Sep 2022 18:53:37 -0400 Subject: counter: interrupt-cnt: Implement watch_validate callback The interrupt-cnt counter driver only pushes one type of event on only one channel: COUNTER_EVENT_CHANGE_OF_STATE on channel 0. The interrupt_cnt_watch_validate() watch_valid callback is implemented to ensure watch configurations are valid for this driver. Cc: Oleksij Rempel Cc: Pengutronix Kernel Team Link: https://lore.kernel.org/r/20220815225058.144203-1-william.gray@linaro.org/ Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/c50b5eede7d3f523de8dc3937dc44680f2773e1d.1664318353.git.william.gray@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/counter/interrupt-cnt.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/counter/interrupt-cnt.c b/drivers/counter/interrupt-cnt.c index 5a11b65fc0e5..229473855c5b 100644 --- a/drivers/counter/interrupt-cnt.c +++ b/drivers/counter/interrupt-cnt.c @@ -139,12 +139,23 @@ static int interrupt_cnt_signal_read(struct counter_device *counter, return 0; } +static int interrupt_cnt_watch_validate(struct counter_device *counter, + const struct counter_watch *watch) +{ + if (watch->channel != 0 || + watch->event != COUNTER_EVENT_CHANGE_OF_STATE) + return -EINVAL; + + return 0; +} + static const struct counter_ops interrupt_cnt_ops = { .action_read = interrupt_cnt_action_read, .count_read = interrupt_cnt_read, .count_write = interrupt_cnt_write, .function_read = interrupt_cnt_function_read, .signal_read = interrupt_cnt_signal_read, + .watch_validate = interrupt_cnt_watch_validate, }; static int interrupt_cnt_probe(struct platform_device *pdev) -- cgit v1.2.3 From 650ae67bbf7ba5ac193f053969612fbb93247b64 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Tue, 27 Sep 2022 18:53:38 -0400 Subject: counter: Introduce the Signal polarity component The Signal polarity component represents the active level of a respective Signal. There are two possible states: positive (rising edge) and negative (falling edge); enum counter_signal_polarity represents these states. A convenience macro COUNTER_COMP_POLARITY() is provided for driver authors to declare a Signal polarity component. Cc: Julien Panis Link: https://lore.kernel.org/r/8f47d6e1db71a11bb1e2666f8e2a6e9d256d4131.1664204990.git.william.gray@linaro.org/ Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/b6e53438badcb6318997d13dd2fc052f97d808ac.1664318353.git.william.gray@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-bus-counter | 14 ++++++++++++++ drivers/counter/counter-chrdev.c | 1 + drivers/counter/counter-sysfs.c | 12 ++++++++++++ include/linux/counter.h | 10 ++++++++++ include/uapi/linux/counter.h | 6 ++++++ 5 files changed, 43 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-counter b/Documentation/ABI/testing/sysfs-bus-counter index 06c2b3e27e0b..2a996deabe9e 100644 --- a/Documentation/ABI/testing/sysfs-bus-counter +++ b/Documentation/ABI/testing/sysfs-bus-counter @@ -217,6 +217,7 @@ 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/polarity_component_id What: /sys/bus/counter/devices/counterX/signalY/synchronous_mode_component_id KernelVersion: 5.16 Contact: linux-iio@vger.kernel.org @@ -303,6 +304,19 @@ Description: Discrete set of available values for the respective Signal Y configuration are listed in this file. +What: /sys/bus/counter/devices/counterX/signalY/polarity +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Active level of Signal Y. The following polarity values are + available: + + positive: + Signal high state considered active level (rising edge). + + negative: + Signal low state considered active level (falling edge). + What: /sys/bus/counter/devices/counterX/signalY/name KernelVersion: 5.2 Contact: linux-iio@vger.kernel.org diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c index 4e71a19d7e6a..120879ee2e87 100644 --- a/drivers/counter/counter-chrdev.c +++ b/drivers/counter/counter-chrdev.c @@ -487,6 +487,7 @@ static int counter_get_data(struct counter_device *const counter, case COUNTER_COMP_ENUM: case COUNTER_COMP_COUNT_DIRECTION: case COUNTER_COMP_COUNT_MODE: + case COUNTER_COMP_SIGNAL_POLARITY: switch (comp_node->component.scope) { case COUNTER_SCOPE_DEVICE: ret = comp->device_u32_read(counter, &value_u32); diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c index 04eac41dad33..e5dd36e1a45f 100644 --- a/drivers/counter/counter-sysfs.c +++ b/drivers/counter/counter-sysfs.c @@ -91,6 +91,11 @@ static const char *const counter_count_mode_str[] = { [COUNTER_COUNT_MODE_MODULO_N] = "modulo-n" }; +static const char *const counter_signal_polarity_str[] = { + [COUNTER_SIGNAL_POLARITY_POSITIVE] = "positive", + [COUNTER_SIGNAL_POLARITY_NEGATIVE] = "negative" +}; + static ssize_t counter_comp_u8_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -201,6 +206,8 @@ static ssize_t counter_comp_u32_show(struct device *dev, 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]); + case COUNTER_COMP_SIGNAL_POLARITY: + return sysfs_emit(buf, "%s\n", counter_signal_polarity_str[data]); default: return sysfs_emit(buf, "%u\n", (unsigned int)data); } @@ -252,6 +259,10 @@ static ssize_t counter_comp_u32_store(struct device *dev, err = counter_find_enum(&data, avail->enums, avail->num_items, buf, counter_count_mode_str); break; + case COUNTER_COMP_SIGNAL_POLARITY: + err = counter_find_enum(&data, avail->enums, avail->num_items, + buf, counter_signal_polarity_str); + break; default: err = kstrtou32(buf, 0, &data); break; @@ -469,6 +480,7 @@ static int counter_attr_create(struct device *const dev, case COUNTER_COMP_ENUM: case COUNTER_COMP_COUNT_DIRECTION: case COUNTER_COMP_COUNT_MODE: + case COUNTER_COMP_SIGNAL_POLARITY: if (comp->device_u32_read) { dev_attr->attr.mode |= 0444; dev_attr->show = counter_comp_u32_show; diff --git a/include/linux/counter.h b/include/linux/counter.h index a81234bc8ea8..b3fb6b68881a 100644 --- a/include/linux/counter.h +++ b/include/linux/counter.h @@ -31,6 +31,7 @@ enum counter_comp_type { COUNTER_COMP_ENUM, COUNTER_COMP_COUNT_DIRECTION, COUNTER_COMP_COUNT_MODE, + COUNTER_COMP_SIGNAL_POLARITY, }; /** @@ -477,6 +478,15 @@ struct counter_available { #define COUNTER_COMP_FLOOR(_read, _write) \ COUNTER_COMP_COUNT_U64("floor", _read, _write) +#define COUNTER_COMP_POLARITY(_read, _write, _available) \ +{ \ + .type = COUNTER_COMP_SIGNAL_POLARITY, \ + .name = "polarity", \ + .signal_u32_read = (_read), \ + .signal_u32_write = (_write), \ + .priv = &(_available), \ +} + #define COUNTER_COMP_PRESET(_read, _write) \ COUNTER_COMP_COUNT_U64("preset", _read, _write) diff --git a/include/uapi/linux/counter.h b/include/uapi/linux/counter.h index 96c5ffd368ad..e9610e1944dc 100644 --- a/include/uapi/linux/counter.h +++ b/include/uapi/linux/counter.h @@ -153,4 +153,10 @@ enum counter_synapse_action { COUNTER_SYNAPSE_ACTION_BOTH_EDGES, }; +/* Signal polarity values */ +enum counter_signal_polarity { + COUNTER_SIGNAL_POLARITY_POSITIVE, + COUNTER_SIGNAL_POLARITY_NEGATIVE, +}; + #endif /* _UAPI_COUNTER_H_ */ -- cgit v1.2.3 From 9830288aeada5ec8ded1665aafe2d2b8f4121bad Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Tue, 27 Sep 2022 18:53:39 -0400 Subject: counter: 104-quad-8: Add Signal polarity component The 104-quad-8 driver provides support for Index signal polarity modes via the "index_polarity" Signal component. This patch exposes the same functionality through the more standard "polarity" Signal component. Link: https://lore.kernel.org/r/01d00c21873159833035cb6775d0d0e8ad55f2ef.1664204990.git.william.gray@linaro.org/ Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/0bf840beee1665e9f04ea82368ecdde87c791a22.1664318353.git.william.gray@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/counter/104-quad-8.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c index 1323edfbe40c..2a9d8259ed4b 100644 --- a/drivers/counter/104-quad-8.c +++ b/drivers/counter/104-quad-8.c @@ -549,6 +549,32 @@ static int quad8_index_polarity_set(struct counter_device *counter, return 0; } +static int quad8_polarity_read(struct counter_device *counter, + struct counter_signal *signal, + enum counter_signal_polarity *polarity) +{ + int err; + u32 index_polarity; + + err = quad8_index_polarity_get(counter, signal, &index_polarity); + if (err) + return err; + + *polarity = (index_polarity) ? COUNTER_SIGNAL_POLARITY_POSITIVE : + COUNTER_SIGNAL_POLARITY_NEGATIVE; + + return 0; +} + +static int quad8_polarity_write(struct counter_device *counter, + struct counter_signal *signal, + enum counter_signal_polarity polarity) +{ + const u32 pol = (polarity == COUNTER_SIGNAL_POLARITY_POSITIVE) ? 1 : 0; + + return quad8_index_polarity_set(counter, signal, pol); +} + static const char *const quad8_synchronous_modes[] = { "non-synchronous", "synchronous" @@ -977,6 +1003,13 @@ static struct counter_comp quad8_signal_ext[] = { quad8_signal_fck_prescaler_write) }; +static const enum counter_signal_polarity quad8_polarities[] = { + COUNTER_SIGNAL_POLARITY_POSITIVE, + COUNTER_SIGNAL_POLARITY_NEGATIVE, +}; + +static DEFINE_COUNTER_AVAILABLE(quad8_polarity_available, quad8_polarities); + static DEFINE_COUNTER_ENUM(quad8_index_pol_enum, quad8_index_polarity_modes); static DEFINE_COUNTER_ENUM(quad8_synch_mode_enum, quad8_synchronous_modes); @@ -984,6 +1017,8 @@ 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_POLARITY(quad8_polarity_read, quad8_polarity_write, + quad8_polarity_available), COUNTER_COMP_SIGNAL_ENUM("synchronous_mode", quad8_synchronous_mode_get, quad8_synchronous_mode_set, quad8_synch_mode_enum), -- cgit v1.2.3 From 45d2918520b2d8e640e4fb3fbf664dfb823dc520 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Tue, 27 Sep 2022 18:53:40 -0400 Subject: counter: Introduce the Count capture component Some devices provide a latch function to save historic Count values. This patch standardizes exposure of such functionality as Count capture components. A COUNTER_COMP_CAPTURE macro is provided for driver authors to define a capture component. A new event COUNTER_EVENT_CAPTURE is introduced to represent Count value capture events. Cc: Julien Panis Link: https://lore.kernel.org/r/c239572ab4208d0d6728136e82a88ad464369a7a.1664204990.git.william.gray@linaro.org/ Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/3cebaa0b807a225eb277d771504fe6dba7269ffd.1664318353.git.william.gray@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-bus-counter | 7 +++++++ include/linux/counter.h | 3 +++ include/uapi/linux/counter.h | 2 ++ 3 files changed, 12 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-counter b/Documentation/ABI/testing/sysfs-bus-counter index 2a996deabe9e..3eb6b063970a 100644 --- a/Documentation/ABI/testing/sysfs-bus-counter +++ b/Documentation/ABI/testing/sysfs-bus-counter @@ -4,6 +4,12 @@ Contact: linux-iio@vger.kernel.org Description: Count data of Count Y represented as a string. +What: /sys/bus/counter/devices/counterX/countY/capture +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Historical capture of the Count Y count data. + What: /sys/bus/counter/devices/counterX/countY/ceiling KernelVersion: 5.2 Contact: linux-iio@vger.kernel.org @@ -203,6 +209,7 @@ Description: both edges: Any state transition. +What: /sys/bus/counter/devices/counterX/countY/capture_component_id 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 diff --git a/include/linux/counter.h b/include/linux/counter.h index b3fb6b68881a..e160197971dd 100644 --- a/include/linux/counter.h +++ b/include/linux/counter.h @@ -453,6 +453,9 @@ struct counter_available { .priv = &(_available), \ } +#define COUNTER_COMP_CAPTURE(_read, _write) \ + COUNTER_COMP_COUNT_U64("capture", _read, _write) + #define COUNTER_COMP_CEILING(_read, _write) \ COUNTER_COMP_COUNT_U64("ceiling", _read, _write) diff --git a/include/uapi/linux/counter.h b/include/uapi/linux/counter.h index e9610e1944dc..8ab12d731e3b 100644 --- a/include/uapi/linux/counter.h +++ b/include/uapi/linux/counter.h @@ -63,6 +63,8 @@ enum counter_event_type { COUNTER_EVENT_INDEX, /* State of counter is changed */ COUNTER_EVENT_CHANGE_OF_STATE, + /* Count value captured */ + COUNTER_EVENT_CAPTURE, }; /** -- cgit v1.2.3 From bb4bbbec664ffdb4652bf3d5daf7c930e68e5c40 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Tue, 27 Sep 2022 18:53:41 -0400 Subject: counter: Consolidate Counter extension sysfs attribute creation Counter extensions are handled for the Device, Counts, and Signals. The code loops through each Counter extension and creates the expected sysfs attributes. This patch consolidates that code into functions to reduce redundancy and make the intention of the code clearer. Link: https://lore.kernel.org/r/6f2121cf52073028c119dbf981a8b72f3eb625d2.1664204990.git.william.gray@linaro.org/ Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/0469c3ae3fbccbca908993c78d94f221761a6a3a.1664318353.git.william.gray@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/counter/counter-sysfs.c | 98 ++++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c index e5dd36e1a45f..b393da402e0b 100644 --- a/drivers/counter/counter-sysfs.c +++ b/drivers/counter/counter-sysfs.c @@ -592,6 +592,46 @@ static int counter_comp_id_attr_create(struct device *const dev, return 0; } +static int counter_ext_attrs_create(struct device *const dev, + struct counter_attribute_group *const group, + const struct counter_comp *const ext, + const enum counter_scope scope, + void *const parent, const size_t id) +{ + int err; + + /* Create main extension attribute */ + err = counter_attr_create(dev, group, ext, scope, parent); + if (err < 0) + return err; + + /* Create extension id attribute */ + return counter_comp_id_attr_create(dev, group, ext->name, id); +} + +static int counter_sysfs_exts_add(struct device *const dev, + struct counter_attribute_group *const group, + const struct counter_comp *const exts, + const size_t num_ext, + const enum counter_scope scope, + void *const parent) +{ + size_t i; + const struct counter_comp *ext; + int err; + + /* Create attributes for each extension */ + for (i = 0; i < num_ext; i++) { + ext = &exts[i]; + err = counter_ext_attrs_create(dev, group, ext, scope, parent, + i); + if (err < 0) + return err; + } + + return 0; +} + static struct counter_comp counter_signal_comp = { .type = COUNTER_COMP_SIGNAL_LEVEL, .name = "signal", @@ -605,8 +645,6 @@ static int counter_signal_attrs_create(struct counter_device *const counter, struct device *const dev = &counter->dev; int err; struct counter_comp comp; - size_t i; - struct counter_comp *ext; /* Create main Signal attribute */ comp = counter_signal_comp; @@ -620,21 +658,9 @@ static int counter_signal_attrs_create(struct counter_device *const counter, if (err < 0) return err; - /* Create an attribute for each extension */ - for (i = 0; i < signal->num_ext; i++) { - 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; - } - - return 0; + /* Add Signal extensions */ + return counter_sysfs_exts_add(dev, cattr_group, signal->ext, + signal->num_ext, scope, signal); } static int counter_sysfs_signals_add(struct counter_device *const counter, @@ -719,8 +745,6 @@ static int counter_count_attrs_create(struct counter_device *const counter, struct device *const dev = &counter->dev; int err; struct counter_comp comp; - size_t i; - struct counter_comp *ext; /* Create main Count attribute */ comp = counter_count_comp; @@ -743,21 +767,9 @@ static int counter_count_attrs_create(struct counter_device *const counter, if (err < 0) return err; - /* Create an attribute for each extension */ - for (i = 0; i < count->num_ext; i++) { - 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; - } - - return 0; + /* Add Count extensions */ + return counter_sysfs_exts_add(dev, cattr_group, count->ext, + count->num_ext, scope, count); } static int counter_sysfs_counts_add(struct counter_device *const counter, @@ -850,8 +862,6 @@ static int counter_sysfs_attr_add(struct counter_device *const counter, const enum counter_scope scope = COUNTER_SCOPE_DEVICE; 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); @@ -888,19 +898,9 @@ static int counter_sysfs_attr_add(struct counter_device *const counter, if (err < 0) return err; - /* Create an attribute for each extension */ - for (i = 0; i < counter->num_ext; i++) { - 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; - } + /* Add device extensions */ + return counter_sysfs_exts_add(dev, cattr_group, counter->ext, + counter->num_ext, scope, NULL); return 0; } -- cgit v1.2.3 From d2011be1e22f7769c7c71d6d7f777ffcc544808d Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Tue, 27 Sep 2022 18:53:42 -0400 Subject: counter: Introduce the COUNTER_COMP_ARRAY component type The COUNTER_COMP_ARRAY Counter component type is introduced to enable support for Counter array components. With Counter array components, exposure for buffers on counter devices can be defined via new Counter array component macros. This should simplify code for driver authors who would otherwise need to define individual Counter components for each array element. Eight Counter array component macros are introduced:: DEFINE_COUNTER_ARRAY_U64(_name, _length) DEFINE_COUNTER_ARRAY_CAPTURE(_name, _length) DEFINE_COUNTER_ARRAY_POLARITY(_name, _enums, _length) COUNTER_COMP_DEVICE_ARRAY_U64(_name, _read, _write, _array) COUNTER_COMP_COUNT_ARRAY_U64(_name, _read, _write, _array) COUNTER_COMP_SIGNAL_ARRAY_U64(_name, _read, _write, _array) COUNTER_COMP_ARRAY_CAPTURE(_read, _write, _array) COUNTER_COMP_ARRAY_POLARITY(_read, _write, _array) Eight Counter array callbacks are introduced as well:: int (*signal_array_u32_read)(struct counter_device *counter, struct counter_signal *signal, size_t idx, u32 *val); int (*signal_array_u32_write)(struct counter_device *counter, struct counter_signal *signal, size_t idx, u32 val); int (*device_array_u64_read)(struct counter_device *counter, size_t idx, u64 *val); int (*count_array_u64_read)(struct counter_device *counter, struct counter_count *count, size_t idx, u64 *val); int (*signal_array_u64_read)(struct counter_device *counter, struct counter_signal *signal, size_t idx, u64 *val); int (*device_array_u64_write)(struct counter_device *counter, size_t idx, u64 val); int (*count_array_u64_write)(struct counter_device *counter, struct counter_count *count, size_t idx, u64 val); int (*signal_array_u64_write)(struct counter_device *counter, struct counter_signal *signal, size_t idx, u64 val); Driver authors can handle reads/writes for an array component by receiving an element index via the `idx` parameter and processing the respective value via the `val` parameter. For example, suppose a driver wants to expose a Count's read-only capture buffer of four elements using a callback `foobar_capture_read()`:: DEFINE_COUNTER_ARRAY_CAPTURE(foobar_capture_array, 4); COUNTER_COMP_ARRAY_CAPTURE(foobar_capture_read, NULL, foobar_capture_array) Respective sysfs attributes for each array element would appear for the respective Count: * /sys/bus/counter/devices/counterX/countY/capture0 * /sys/bus/counter/devices/counterX/countY/capture1 * /sys/bus/counter/devices/counterX/countY/capture2 * /sys/bus/counter/devices/counterX/countY/capture3 If a user tries to read _capture2_ for example, `idx` will be `2` when passed to the `foobar_capture_read()` callback, and thus the driver knows which array element to handle. Counter arrays for polarity elements can be defined in a similar manner as u64 elements:: const enum counter_signal_polarity foobar_polarity_states[] = { COUNTER_SIGNAL_POLARITY_POSITIVE, COUNTER_SIGNAL_POLARITY_NEGATIVE, }; DEFINE_COUNTER_ARRAY_POLARITY(foobar_polarity_array, foobar_polarity_states, 4); COUNTER_COMP_ARRAY_POLARITY(foobar_polarity_read, foobar_polarity_write, foobar_polarity_array) Tested-by: Julien Panis Link: https://lore.kernel.org/r/5310c22520aeae65b1b74952419f49ac4c8e1ec1.1664204990.git.william.gray@linaro.org/ Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/a51fd608704bdfc5a0efa503fc5481df34241e0a.1664318353.git.william.gray@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/counter/counter-chrdev.c | 134 ++++++++++++++++++++++---- drivers/counter/counter-sysfs.c | 198 ++++++++++++++++++++++++++++++++++++++- include/linux/counter.h | 134 ++++++++++++++++++++++++++ 3 files changed, 446 insertions(+), 20 deletions(-) diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c index 120879ee2e87..80acdf62794a 100644 --- a/drivers/counter/counter-chrdev.c +++ b/drivers/counter/counter-chrdev.c @@ -40,7 +40,11 @@ struct counter_comp_node { 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) + a.signal_u64_read == b.signal_u64_read || \ + a.signal_array_u32_read == b.signal_array_u32_read || \ + a.device_array_u64_read == b.device_array_u64_read || \ + a.count_array_u64_read == b.count_array_u64_read || \ + a.signal_array_u64_read == b.signal_array_u64_read) #define counter_comp_read_is_set(comp) \ (comp.action_read || \ @@ -52,7 +56,11 @@ struct counter_comp_node { comp.signal_u32_read || \ comp.device_u64_read || \ comp.count_u64_read || \ - comp.signal_u64_read) + comp.signal_u64_read || \ + comp.signal_array_u32_read || \ + comp.device_array_u64_read || \ + comp.count_array_u64_read || \ + comp.signal_array_u64_read) static ssize_t counter_chrdev_read(struct file *filp, char __user *buf, size_t len, loff_t *f_ps) @@ -228,6 +236,31 @@ static int counter_disable_events(struct counter_device *const counter) return err; } +static int counter_get_ext(const struct counter_comp *const ext, + const size_t num_ext, const size_t component_id, + size_t *const ext_idx, size_t *const id) +{ + struct counter_array *element; + + *id = 0; + for (*ext_idx = 0; *ext_idx < num_ext; (*ext_idx)++) { + if (*id == component_id) + return 0; + + if (ext->type == COUNTER_COMP_ARRAY) { + element = ext->priv; + + if (component_id - *id < element->length) + return 0; + + *id += element->length; + } else + (*id)++; + } + + return -EINVAL; +} + static int counter_add_watch(struct counter_device *const counter, const unsigned long arg) { @@ -237,6 +270,7 @@ static int counter_add_watch(struct counter_device *const counter, size_t parent, id; struct counter_comp *ext; size_t num_ext; + size_t ext_idx, ext_id; int err = 0; if (copy_from_user(&watch, uwatch, sizeof(watch))) @@ -314,11 +348,11 @@ static int counter_add_watch(struct counter_device *const counter, 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); + err = counter_get_ext(ext, num_ext, id, &ext_idx, &ext_id); + if (err < 0) + return err; - comp_node.comp = ext[id]; + comp_node.comp = ext[ext_idx]; break; default: return -EINVAL; @@ -451,14 +485,56 @@ void counter_chrdev_remove(struct counter_device *const counter) kfifo_free(&counter->events); } +static int counter_get_array_data(struct counter_device *const counter, + const enum counter_scope scope, + void *const parent, + const struct counter_comp *const comp, + const size_t idx, u64 *const value) +{ + const struct counter_array *const element = comp->priv; + u32 value_u32 = 0; + int ret; + + switch (element->type) { + case COUNTER_COMP_SIGNAL_POLARITY: + if (scope != COUNTER_SCOPE_SIGNAL) + return -EINVAL; + ret = comp->signal_array_u32_read(counter, parent, idx, + &value_u32); + *value = value_u32; + return ret; + case COUNTER_COMP_U64: + switch (scope) { + case COUNTER_SCOPE_DEVICE: + return comp->device_array_u64_read(counter, idx, value); + case COUNTER_SCOPE_SIGNAL: + return comp->signal_array_u64_read(counter, parent, idx, + value); + case COUNTER_SCOPE_COUNT: + return comp->count_array_u64_read(counter, parent, idx, + value); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + 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; + const enum counter_scope scope = comp_node->component.scope; + const size_t id = comp_node->component.id; + struct counter_signal *const signal = comp_node->parent; + struct counter_count *const count = comp_node->parent; u8 value_u8 = 0; u32 value_u32 = 0; + const struct counter_comp *ext; + size_t num_ext; + size_t ext_idx, ext_id; int ret; if (comp_node->component.type == COUNTER_COMPONENT_NONE) @@ -467,15 +543,15 @@ static int counter_get_data(struct counter_device *const counter, switch (comp->type) { case COUNTER_COMP_U8: case COUNTER_COMP_BOOL: - switch (comp_node->component.scope) { + switch (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); + ret = comp->signal_u8_read(counter, signal, &value_u8); break; case COUNTER_SCOPE_COUNT: - ret = comp->count_u8_read(counter, parent, &value_u8); + ret = comp->count_u8_read(counter, count, &value_u8); break; default: return -EINVAL; @@ -488,16 +564,16 @@ static int counter_get_data(struct counter_device *const counter, case COUNTER_COMP_COUNT_DIRECTION: case COUNTER_COMP_COUNT_MODE: case COUNTER_COMP_SIGNAL_POLARITY: - switch (comp_node->component.scope) { + switch (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, + ret = comp->signal_u32_read(counter, signal, &value_u32); break; case COUNTER_SCOPE_COUNT: - ret = comp->count_u32_read(counter, parent, &value_u32); + ret = comp->count_u32_read(counter, count, &value_u32); break; default: return -EINVAL; @@ -505,21 +581,43 @@ static int counter_get_data(struct counter_device *const counter, *value = value_u32; return ret; case COUNTER_COMP_U64: - switch (comp_node->component.scope) { + switch (scope) { case COUNTER_SCOPE_DEVICE: return comp->device_u64_read(counter, value); case COUNTER_SCOPE_SIGNAL: - return comp->signal_u64_read(counter, parent, value); + return comp->signal_u64_read(counter, signal, value); case COUNTER_SCOPE_COUNT: - return comp->count_u64_read(counter, parent, value); + return comp->count_u64_read(counter, count, value); default: return -EINVAL; } case COUNTER_COMP_SYNAPSE_ACTION: - ret = comp->action_read(counter, parent, comp->priv, - &value_u32); + ret = comp->action_read(counter, count, comp->priv, &value_u32); *value = value_u32; return ret; + case COUNTER_COMP_ARRAY: + switch (scope) { + case COUNTER_SCOPE_DEVICE: + ext = counter->ext; + num_ext = counter->num_ext; + break; + case COUNTER_SCOPE_SIGNAL: + ext = signal->ext; + num_ext = signal->num_ext; + break; + case COUNTER_SCOPE_COUNT: + ext = count->ext; + num_ext = count->num_ext; + break; + default: + return -EINVAL; + } + ret = counter_get_ext(ext, num_ext, id, &ext_idx, &ext_id); + if (ret < 0) + return ret; + + return counter_get_array_data(counter, scope, comp_node->parent, + comp, id - ext_id, value); default: return -EINVAL; } diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c index b393da402e0b..b9efe66f9f8d 100644 --- a/drivers/counter/counter-sysfs.c +++ b/drivers/counter/counter-sysfs.c @@ -352,6 +352,124 @@ static ssize_t counter_comp_u64_store(struct device *dev, return len; } +static ssize_t counter_comp_array_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 = counter_from_dev(dev); + const struct counter_array *const element = a->comp.priv; + int err; + u32 data = 0; + + if (a->scope != COUNTER_SCOPE_SIGNAL || + element->type != COUNTER_COMP_SIGNAL_POLARITY) + return -EINVAL; + + err = a->comp.signal_array_u32_read(counter, a->parent, element->idx, + &data); + if (err < 0) + return err; + + return sysfs_emit(buf, "%s\n", counter_signal_polarity_str[data]); +} + +static ssize_t counter_comp_array_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 = counter_from_dev(dev); + const struct counter_array *const element = a->comp.priv; + int err; + u32 data = 0; + + if (element->type != COUNTER_COMP_SIGNAL_POLARITY || + a->scope != COUNTER_SCOPE_SIGNAL) + return -EINVAL; + + err = counter_find_enum(&data, element->avail->enums, + element->avail->num_items, buf, + counter_signal_polarity_str); + if (err < 0) + return err; + + err = a->comp.signal_array_u32_write(counter, a->parent, element->idx, + data); + if (err < 0) + return err; + + return len; +} + +static ssize_t counter_comp_array_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 = counter_from_dev(dev); + const struct counter_array *const element = a->comp.priv; + int err; + u64 data = 0; + + switch (a->scope) { + case COUNTER_SCOPE_DEVICE: + err = a->comp.device_array_u64_read(counter, element->idx, + &data); + break; + case COUNTER_SCOPE_SIGNAL: + err = a->comp.signal_array_u64_read(counter, a->parent, + element->idx, &data); + break; + case COUNTER_SCOPE_COUNT: + err = a->comp.count_array_u64_read(counter, a->parent, + element->idx, &data); + break; + default: + return -EINVAL; + } + if (err < 0) + return err; + + return sysfs_emit(buf, "%llu\n", (unsigned long long)data); +} + +static ssize_t counter_comp_array_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 = counter_from_dev(dev); + const struct counter_array *const element = a->comp.priv; + 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_array_u64_write(counter, element->idx, + data); + break; + case COUNTER_SCOPE_SIGNAL: + err = a->comp.signal_array_u64_write(counter, a->parent, + element->idx, data); + break; + case COUNTER_SCOPE_COUNT: + err = a->comp.count_array_u64_write(counter, a->parent, + element->idx, 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) @@ -446,6 +564,7 @@ static int counter_attr_create(struct device *const dev, const enum counter_scope scope, void *const parent) { + const struct counter_array *const array = comp->priv; struct counter_attribute *counter_attr; struct device_attribute *dev_attr; @@ -500,6 +619,32 @@ static int counter_attr_create(struct device *const dev, dev_attr->store = counter_comp_u64_store; } break; + case COUNTER_COMP_ARRAY: + switch (array->type) { + case COUNTER_COMP_SIGNAL_POLARITY: + if (comp->signal_array_u32_read) { + dev_attr->attr.mode |= 0444; + dev_attr->show = counter_comp_array_u32_show; + } + if (comp->signal_array_u32_write) { + dev_attr->attr.mode |= 0200; + dev_attr->store = counter_comp_array_u32_store; + } + break; + case COUNTER_COMP_U64: + if (comp->device_array_u64_read) { + dev_attr->attr.mode |= 0444; + dev_attr->show = counter_comp_array_u64_show; + } + if (comp->device_array_u64_write) { + dev_attr->attr.mode |= 0200; + dev_attr->store = counter_comp_array_u64_store; + } + break; + default: + return -EINVAL; + } + break; default: return -EINVAL; } @@ -609,6 +754,45 @@ static int counter_ext_attrs_create(struct device *const dev, return counter_comp_id_attr_create(dev, group, ext->name, id); } +static int counter_array_attrs_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, const size_t id) +{ + const struct counter_array *const array = comp->priv; + struct counter_comp ext = *comp; + struct counter_array *element; + size_t idx; + int err; + + /* Create an attribute for each array element */ + for (idx = 0; idx < array->length; idx++) { + /* Generate array element attribute name */ + ext.name = devm_kasprintf(dev, GFP_KERNEL, "%s%zu", comp->name, + idx); + if (!ext.name) + return -ENOMEM; + + /* Allocate and configure array element */ + element = devm_kzalloc(dev, sizeof(*element), GFP_KERNEL); + if (!element) + return -ENOMEM; + element->type = array->type; + element->avail = array->avail; + element->idx = idx; + ext.priv = element; + + /* Create all attributes associated with the array element */ + err = counter_ext_attrs_create(dev, group, &ext, scope, parent, + id + idx); + if (err < 0) + return err; + } + + return 0; +} + static int counter_sysfs_exts_add(struct device *const dev, struct counter_attribute_group *const group, const struct counter_comp *const exts, @@ -619,12 +803,22 @@ static int counter_sysfs_exts_add(struct device *const dev, size_t i; const struct counter_comp *ext; int err; + size_t id = 0; + const struct counter_array *array; /* Create attributes for each extension */ for (i = 0; i < num_ext; i++) { ext = &exts[i]; - err = counter_ext_attrs_create(dev, group, ext, scope, parent, - i); + if (ext->type == COUNTER_COMP_ARRAY) { + err = counter_array_attrs_create(dev, group, ext, scope, + parent, id); + array = ext->priv; + id += array->length; + } else { + err = counter_ext_attrs_create(dev, group, ext, scope, + parent, id); + id++; + } if (err < 0) return err; } diff --git a/include/linux/counter.h b/include/linux/counter.h index e160197971dd..c41fa602ed28 100644 --- a/include/linux/counter.h +++ b/include/linux/counter.h @@ -32,6 +32,7 @@ enum counter_comp_type { COUNTER_COMP_COUNT_DIRECTION, COUNTER_COMP_COUNT_MODE, COUNTER_COMP_SIGNAL_POLARITY, + COUNTER_COMP_ARRAY, }; /** @@ -69,6 +70,30 @@ enum counter_comp_type { * @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. + * @signal_array_u32_read: Signal u32 array component read callback. The + * index of the respective Count u32 array + * component element is passed via the idx + * parameter. The read value of the respective + * Count u32 array component element should be + * passed back via the val parameter. + * @device_array_u64_read: Device u64 array component read callback. The + * index of the respective Device u64 array + * component element is passed via the idx + * parameter. The read value of the respective + * Device u64 array component element should be + * passed back via the val parameter. + * @count_array_u64_read: Count u64 array component read callback. The + * index of the respective Count u64 array + * component element is passed via the idx + * parameter. The read value of the respective + * Count u64 array component element should be + * passed back via the val parameter. + * @signal_array_u64_read: Signal u64 array component read callback. The + * index of the respective Count u64 array + * component element is passed via the idx + * parameter. The read value of the respective + * Count u64 array component element 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. @@ -99,6 +124,30 @@ enum counter_comp_type { * @signal_u64_write: Signal u64 component write callback. The write value of * the respective Signal u64 component is passed via the * val parameter. + * @signal_array_u32_write: Signal u32 array component write callback. The + * index of the respective Signal u32 array + * component element is passed via the idx + * parameter. The write value of the respective + * Signal u32 array component element is passed via + * the val parameter. + * @device_array_u64_write: Device u64 array component write callback. The + * index of the respective Device u64 array + * component element is passed via the idx + * parameter. The write value of the respective + * Device u64 array component element is passed via + * the val parameter. + * @count_array_u64_write: Count u64 array component write callback. The + * index of the respective Count u64 array + * component element is passed via the idx + * parameter. The write value of the respective + * Count u64 array component element is passed via + * the val parameter. + * @signal_array_u64_write: Signal u64 array component write callback. The + * index of the respective Signal u64 array + * component element is passed via the idx + * parameter. The write value of the respective + * Signal u64 array component element is passed via + * the val parameter. */ struct counter_comp { enum counter_comp_type type; @@ -126,6 +175,17 @@ struct counter_comp { struct counter_count *count, u64 *val); int (*signal_u64_read)(struct counter_device *counter, struct counter_signal *signal, u64 *val); + int (*signal_array_u32_read)(struct counter_device *counter, + struct counter_signal *signal, + size_t idx, u32 *val); + int (*device_array_u64_read)(struct counter_device *counter, + size_t idx, u64 *val); + int (*count_array_u64_read)(struct counter_device *counter, + struct counter_count *count, + size_t idx, u64 *val); + int (*signal_array_u64_read)(struct counter_device *counter, + struct counter_signal *signal, + size_t idx, u64 *val); }; union { int (*action_write)(struct counter_device *counter, @@ -149,6 +209,17 @@ struct counter_comp { struct counter_count *count, u64 val); int (*signal_u64_write)(struct counter_device *counter, struct counter_signal *signal, u64 val); + int (*signal_array_u32_write)(struct counter_device *counter, + struct counter_signal *signal, + size_t idx, u32 val); + int (*device_array_u64_write)(struct counter_device *counter, + size_t idx, u64 val); + int (*count_array_u64_write)(struct counter_device *counter, + struct counter_count *count, + size_t idx, u64 val); + int (*signal_array_u64_write)(struct counter_device *counter, + struct counter_signal *signal, + size_t idx, u64 val); }; }; @@ -453,6 +524,57 @@ struct counter_available { .priv = &(_available), \ } +struct counter_array { + enum counter_comp_type type; + const struct counter_available *avail; + union { + size_t length; + size_t idx; + }; +}; + +#define DEFINE_COUNTER_ARRAY_U64(_name, _length) \ + struct counter_array _name = { \ + .type = COUNTER_COMP_U64, \ + .length = (_length), \ + } + +#define DEFINE_COUNTER_ARRAY_CAPTURE(_name, _length) \ + DEFINE_COUNTER_ARRAY_U64(_name, _length) + +#define DEFINE_COUNTER_ARRAY_POLARITY(_name, _enums, _length) \ + DEFINE_COUNTER_AVAILABLE(_name##_available, _enums); \ + struct counter_array _name = { \ + .type = COUNTER_COMP_SIGNAL_POLARITY, \ + .avail = &(_name##_available), \ + .length = (_length), \ + } + +#define COUNTER_COMP_DEVICE_ARRAY_U64(_name, _read, _write, _array) \ +{ \ + .type = COUNTER_COMP_ARRAY, \ + .name = (_name), \ + .device_array_u64_read = (_read), \ + .device_array_u64_write = (_write), \ + .priv = &(_array), \ +} +#define COUNTER_COMP_COUNT_ARRAY_U64(_name, _read, _write, _array) \ +{ \ + .type = COUNTER_COMP_ARRAY, \ + .name = (_name), \ + .count_array_u64_read = (_read), \ + .count_array_u64_write = (_write), \ + .priv = &(_array), \ +} +#define COUNTER_COMP_SIGNAL_ARRAY_U64(_name, _read, _write, _array) \ +{ \ + .type = COUNTER_COMP_ARRAY, \ + .name = (_name), \ + .signal_array_u64_read = (_read), \ + .signal_array_u64_write = (_write), \ + .priv = &(_array), \ +} + #define COUNTER_COMP_CAPTURE(_read, _write) \ COUNTER_COMP_COUNT_U64("capture", _read, _write) @@ -496,4 +618,16 @@ struct counter_available { #define COUNTER_COMP_PRESET_ENABLE(_read, _write) \ COUNTER_COMP_COUNT_BOOL("preset_enable", _read, _write) +#define COUNTER_COMP_ARRAY_CAPTURE(_read, _write, _array) \ + COUNTER_COMP_COUNT_ARRAY_U64("capture", _read, _write, _array) + +#define COUNTER_COMP_ARRAY_POLARITY(_read, _write, _array) \ +{ \ + .type = COUNTER_COMP_ARRAY, \ + .name = "polarity", \ + .signal_array_u32_read = (_read), \ + .signal_array_u32_write = (_write), \ + .priv = &(_array), \ +} + #endif /* _COUNTER_H_ */ -- cgit v1.2.3 From b5bee6ced21ca98389000b7017dd41b0cc37fa50 Mon Sep 17 00:00:00 2001 From: Julien Panis Date: Tue, 27 Sep 2022 18:53:43 -0400 Subject: dt-bindings: counter: add ti,am62-ecap-capture.yaml This commit adds a YAML binding for TI ECAP used in capture operating mode. Signed-off-by: Julien Panis Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220923142437.271328-2-jpanis@baylibre.com/ Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/33c27451f61b3a01d886da5e6bf6456088956439.1664318353.git.william.gray@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../bindings/counter/ti,am62-ecap-capture.yaml | 61 ++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 Documentation/devicetree/bindings/counter/ti,am62-ecap-capture.yaml diff --git a/Documentation/devicetree/bindings/counter/ti,am62-ecap-capture.yaml b/Documentation/devicetree/bindings/counter/ti,am62-ecap-capture.yaml new file mode 100644 index 000000000000..4e0b2d2b303e --- /dev/null +++ b/Documentation/devicetree/bindings/counter/ti,am62-ecap-capture.yaml @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/counter/ti,am62-ecap-capture.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments Enhanced Capture (eCAP) Module + +maintainers: + - Julien Panis + +description: | + The eCAP module resources can be used to capture timestamps + on input signal events (falling/rising edges). + +properties: + compatible: + const: ti,am62-ecap-capture + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + const: fck + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + #include + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + capture@23100000 { /* eCAP in capture mode on am62x */ + compatible = "ti,am62-ecap-capture"; + reg = <0x00 0x23100000 0x00 0x100>; + interrupts = ; + power-domains = <&k3_pds 51 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 51 0>; + clock-names = "fck"; + }; + }; -- cgit v1.2.3 From 5a47aed0d651490ad0d6f9dbc98bc6dfc71de787 Mon Sep 17 00:00:00 2001 From: Julien Panis Date: Tue, 27 Sep 2022 18:53:44 -0400 Subject: Documentation: ABI: sysfs-bus-counter: add frequency & num_overflows items This commit adds frequency and num_overflows items to counter ABI file (e.g. for TI ECAP hardware used in capture operating mode). Signed-off-by: Julien Panis Link: https://lore.kernel.org/r/20220923142437.271328-3-jpanis@baylibre.com/ Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/467ae80e97c586c6bc9c453c6156ffcb5d4853d6.1664318353.git.william.gray@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-bus-counter | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-counter b/Documentation/ABI/testing/sysfs-bus-counter index 3eb6b063970a..ff83320b4255 100644 --- a/Documentation/ABI/testing/sysfs-bus-counter +++ b/Documentation/ABI/testing/sysfs-bus-counter @@ -209,6 +209,12 @@ Description: both edges: Any state transition. +What: /sys/bus/counter/devices/counterX/countY/num_overflows +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + This attribute indicates the number of overflows of count Y. + What: /sys/bus/counter/devices/counterX/countY/capture_component_id What: /sys/bus/counter/devices/counterX/countY/ceiling_component_id What: /sys/bus/counter/devices/counterX/countY/floor_component_id @@ -220,12 +226,14 @@ 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/countY/num_overflows_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/polarity_component_id What: /sys/bus/counter/devices/counterX/signalY/synchronous_mode_component_id +What: /sys/bus/counter/devices/counterX/signalY/frequency_component_id KernelVersion: 5.16 Contact: linux-iio@vger.kernel.org Description: @@ -366,3 +374,9 @@ Description: via index_polarity. The index function (as enabled via preset_enable) is performed synchronously with the quadrature clock on the active level of the index input. + +What: /sys/bus/counter/devices/counterX/signalY/frequency +KernelVersion: 6.1 +Contact: linux-iio@vger.kernel.org +Description: + Read-only attribute that indicates the signal Y frequency, in Hz. -- cgit v1.2.3 From 4e2f42aa00b67605938173a61d07a44fe13bad68 Mon Sep 17 00:00:00 2001 From: Julien Panis Date: Tue, 27 Sep 2022 18:53:45 -0400 Subject: counter: ti-ecap-capture: capture driver support for ECAP ECAP hardware on TI AM62x SoC supports capture feature. It can be used to timestamp events (falling/rising edges) detected on input signal. This commit adds capture driver support for ECAP hardware on AM62x SoC. In the ECAP hardware, capture pin can also be configured to be in PWM mode. Current implementation only supports capture operating mode. Hardware also supports timebase sync between multiple instances, but this driver supports simple independent capture functionality. Signed-off-by: Julien Panis Link: https://lore.kernel.org/r/20220923142437.271328-4-jpanis@baylibre.com/ Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/25644ce1f2fd15d116977770ede20e024f658513.1664318353.git.william.gray@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/counter/Kconfig | 15 + drivers/counter/Makefile | 1 + drivers/counter/ti-ecap-capture.c | 614 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 630 insertions(+) create mode 100644 drivers/counter/ti-ecap-capture.c diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index 5edd155f1911..d388bf26f4dc 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -101,4 +101,19 @@ config INTEL_QEP To compile this driver as a module, choose M here: the module will be called intel-qep. +config TI_ECAP_CAPTURE + tristate "TI eCAP capture driver" + depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST + depends on HAS_IOMEM + select REGMAP_MMIO + help + Select this option to enable the Texas Instruments Enhanced Capture + (eCAP) driver in input mode. + + It can be used to timestamp events (falling/rising edges) detected + on ECAP input signal. + + To compile this driver as a module, choose M here: the module + will be called ti-ecap-capture. + endif # COUNTER diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile index 8fde6c100ebc..b9a369e0d4fc 100644 --- a/drivers/counter/Makefile +++ b/drivers/counter/Makefile @@ -14,3 +14,4 @@ obj-$(CONFIG_TI_EQEP) += ti-eqep.o obj-$(CONFIG_FTM_QUADDEC) += ftm-quaddec.o obj-$(CONFIG_MICROCHIP_TCB_CAPTURE) += microchip-tcb-capture.o obj-$(CONFIG_INTEL_QEP) += intel-qep.o +obj-$(CONFIG_TI_ECAP_CAPTURE) += ti-ecap-capture.o diff --git a/drivers/counter/ti-ecap-capture.c b/drivers/counter/ti-ecap-capture.c new file mode 100644 index 000000000000..af10de30aba5 --- /dev/null +++ b/drivers/counter/ti-ecap-capture.c @@ -0,0 +1,614 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ECAP Capture driver + * + * Copyright (C) 2022 Julien Panis + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ECAP_DRV_NAME "ecap" + +/* ECAP event IDs */ +#define ECAP_CEVT1 0 +#define ECAP_CEVT2 1 +#define ECAP_CEVT3 2 +#define ECAP_CEVT4 3 +#define ECAP_CNTOVF 4 + +#define ECAP_CEVT_LAST ECAP_CEVT4 +#define ECAP_NB_CEVT (ECAP_CEVT_LAST + 1) + +#define ECAP_EVT_LAST ECAP_CNTOVF +#define ECAP_NB_EVT (ECAP_EVT_LAST + 1) + +/* Registers */ +#define ECAP_TSCNT_REG 0x00 + +#define ECAP_CAP_REG(i) (((i) << 2) + 0x08) + +#define ECAP_ECCTL_REG 0x28 +#define ECAP_CAPPOL_BIT(i) BIT((i) << 1) +#define ECAP_EV_MODE_MASK GENMASK(7, 0) +#define ECAP_CAPLDEN_BIT BIT(8) +#define ECAP_CONT_ONESHT_BIT BIT(16) +#define ECAP_STOPVALUE_MASK GENMASK(18, 17) +#define ECAP_TSCNTSTP_BIT BIT(20) +#define ECAP_SYNCO_DIS_MASK GENMASK(23, 22) +#define ECAP_CAP_APWM_BIT BIT(25) +#define ECAP_ECCTL_EN_MASK (ECAP_CAPLDEN_BIT | ECAP_TSCNTSTP_BIT) +#define ECAP_ECCTL_CFG_MASK (ECAP_SYNCO_DIS_MASK | ECAP_STOPVALUE_MASK \ + | ECAP_ECCTL_EN_MASK | ECAP_CAP_APWM_BIT \ + | ECAP_CONT_ONESHT_BIT) + +#define ECAP_ECINT_EN_FLG_REG 0x2c +#define ECAP_EVT_EN_MASK GENMASK(ECAP_NB_EVT, ECAP_NB_CEVT) +#define ECAP_EVT_FLG_BIT(i) BIT((i) + 17) + +#define ECAP_ECINT_CLR_FRC_REG 0x30 +#define ECAP_INT_CLR_BIT BIT(0) +#define ECAP_EVT_CLR_BIT(i) BIT((i) + 1) +#define ECAP_EVT_CLR_MASK GENMASK(ECAP_NB_EVT, 0) + +#define ECAP_PID_REG 0x5c + +/* ECAP signals */ +#define ECAP_CLOCK_SIG 0 +#define ECAP_INPUT_SIG 1 + +static const struct regmap_config ecap_cnt_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = ECAP_PID_REG, +}; + +/** + * struct ecap_cnt_dev - device private data structure + * @enabled: device state + * @lock: synchronization lock to prevent I/O race conditions + * @clk: device clock + * @regmap: device register map + * @nb_ovf: number of overflows since capture start + * @pm_ctx: device context for PM operations + * @pm_ctx.ev_mode: event mode bits + * @pm_ctx.time_cntr: timestamp counter value + */ +struct ecap_cnt_dev { + bool enabled; + struct mutex lock; + struct clk *clk; + struct regmap *regmap; + atomic_t nb_ovf; + struct { + u8 ev_mode; + u32 time_cntr; + } pm_ctx; +}; + +static u8 ecap_cnt_capture_get_evmode(struct counter_device *counter) +{ + struct ecap_cnt_dev *ecap_dev = counter_priv(counter); + unsigned int regval; + + pm_runtime_get_sync(counter->parent); + regmap_read(ecap_dev->regmap, ECAP_ECCTL_REG, ®val); + pm_runtime_put_sync(counter->parent); + + return regval; +} + +static void ecap_cnt_capture_set_evmode(struct counter_device *counter, u8 ev_mode) +{ + struct ecap_cnt_dev *ecap_dev = counter_priv(counter); + + pm_runtime_get_sync(counter->parent); + regmap_update_bits(ecap_dev->regmap, ECAP_ECCTL_REG, ECAP_EV_MODE_MASK, ev_mode); + pm_runtime_put_sync(counter->parent); +} + +static void ecap_cnt_capture_enable(struct counter_device *counter) +{ + struct ecap_cnt_dev *ecap_dev = counter_priv(counter); + + pm_runtime_get_sync(counter->parent); + + /* Enable interrupts on events */ + regmap_update_bits(ecap_dev->regmap, ECAP_ECINT_EN_FLG_REG, + ECAP_EVT_EN_MASK, ECAP_EVT_EN_MASK); + + /* Run counter */ + regmap_update_bits(ecap_dev->regmap, ECAP_ECCTL_REG, ECAP_ECCTL_CFG_MASK, + ECAP_SYNCO_DIS_MASK | ECAP_STOPVALUE_MASK | ECAP_ECCTL_EN_MASK); +} + +static void ecap_cnt_capture_disable(struct counter_device *counter) +{ + struct ecap_cnt_dev *ecap_dev = counter_priv(counter); + + /* Stop counter */ + regmap_update_bits(ecap_dev->regmap, ECAP_ECCTL_REG, ECAP_ECCTL_EN_MASK, 0); + + /* Disable interrupts on events */ + regmap_update_bits(ecap_dev->regmap, ECAP_ECINT_EN_FLG_REG, ECAP_EVT_EN_MASK, 0); + + pm_runtime_put_sync(counter->parent); +} + +static u32 ecap_cnt_count_get_val(struct counter_device *counter, unsigned int reg) +{ + struct ecap_cnt_dev *ecap_dev = counter_priv(counter); + unsigned int regval; + + pm_runtime_get_sync(counter->parent); + regmap_read(ecap_dev->regmap, reg, ®val); + pm_runtime_put_sync(counter->parent); + + return regval; +} + +static void ecap_cnt_count_set_val(struct counter_device *counter, unsigned int reg, u32 val) +{ + struct ecap_cnt_dev *ecap_dev = counter_priv(counter); + + pm_runtime_get_sync(counter->parent); + regmap_write(ecap_dev->regmap, reg, val); + pm_runtime_put_sync(counter->parent); +} + +static int ecap_cnt_count_read(struct counter_device *counter, + struct counter_count *count, u64 *val) +{ + *val = ecap_cnt_count_get_val(counter, ECAP_TSCNT_REG); + + return 0; +} + +static int ecap_cnt_count_write(struct counter_device *counter, + struct counter_count *count, u64 val) +{ + if (val > U32_MAX) + return -ERANGE; + + ecap_cnt_count_set_val(counter, ECAP_TSCNT_REG, val); + + return 0; +} + +static int ecap_cnt_function_read(struct counter_device *counter, + struct counter_count *count, + enum counter_function *function) +{ + *function = COUNTER_FUNCTION_INCREASE; + + return 0; +} + +static int ecap_cnt_action_read(struct counter_device *counter, + struct counter_count *count, + struct counter_synapse *synapse, + enum counter_synapse_action *action) +{ + *action = (synapse->signal->id == ECAP_CLOCK_SIG) ? + COUNTER_SYNAPSE_ACTION_RISING_EDGE : + COUNTER_SYNAPSE_ACTION_NONE; + + return 0; +} + +static int ecap_cnt_watch_validate(struct counter_device *counter, + const struct counter_watch *watch) +{ + if (watch->channel > ECAP_CEVT_LAST) + return -EINVAL; + + switch (watch->event) { + case COUNTER_EVENT_CAPTURE: + case COUNTER_EVENT_OVERFLOW: + return 0; + default: + return -EINVAL; + } +} + +static int ecap_cnt_clk_get_freq(struct counter_device *counter, + struct counter_signal *signal, u64 *freq) +{ + struct ecap_cnt_dev *ecap_dev = counter_priv(counter); + + *freq = clk_get_rate(ecap_dev->clk); + + return 0; +} + +static int ecap_cnt_pol_read(struct counter_device *counter, + struct counter_signal *signal, + size_t idx, enum counter_signal_polarity *pol) +{ + struct ecap_cnt_dev *ecap_dev = counter_priv(counter); + int bitval; + + pm_runtime_get_sync(counter->parent); + bitval = regmap_test_bits(ecap_dev->regmap, ECAP_ECCTL_REG, ECAP_CAPPOL_BIT(idx)); + pm_runtime_put_sync(counter->parent); + + *pol = bitval ? COUNTER_SIGNAL_POLARITY_NEGATIVE : COUNTER_SIGNAL_POLARITY_POSITIVE; + + return 0; +} + +static int ecap_cnt_pol_write(struct counter_device *counter, + struct counter_signal *signal, + size_t idx, enum counter_signal_polarity pol) +{ + struct ecap_cnt_dev *ecap_dev = counter_priv(counter); + + pm_runtime_get_sync(counter->parent); + if (pol == COUNTER_SIGNAL_POLARITY_NEGATIVE) + regmap_set_bits(ecap_dev->regmap, ECAP_ECCTL_REG, ECAP_CAPPOL_BIT(idx)); + else + regmap_clear_bits(ecap_dev->regmap, ECAP_ECCTL_REG, ECAP_CAPPOL_BIT(idx)); + pm_runtime_put_sync(counter->parent); + + return 0; +} + +static int ecap_cnt_cap_read(struct counter_device *counter, + struct counter_count *count, + size_t idx, u64 *cap) +{ + *cap = ecap_cnt_count_get_val(counter, ECAP_CAP_REG(idx)); + + return 0; +} + +static int ecap_cnt_cap_write(struct counter_device *counter, + struct counter_count *count, + size_t idx, u64 cap) +{ + if (cap > U32_MAX) + return -ERANGE; + + ecap_cnt_count_set_val(counter, ECAP_CAP_REG(idx), cap); + + return 0; +} + +static int ecap_cnt_nb_ovf_read(struct counter_device *counter, + struct counter_count *count, u64 *val) +{ + struct ecap_cnt_dev *ecap_dev = counter_priv(counter); + + *val = atomic_read(&ecap_dev->nb_ovf); + + return 0; +} + +static int ecap_cnt_nb_ovf_write(struct counter_device *counter, + struct counter_count *count, u64 val) +{ + struct ecap_cnt_dev *ecap_dev = counter_priv(counter); + + if (val > U32_MAX) + return -ERANGE; + + atomic_set(&ecap_dev->nb_ovf, val); + + return 0; +} + +static int ecap_cnt_ceiling_read(struct counter_device *counter, + struct counter_count *count, u64 *val) +{ + *val = U32_MAX; + + return 0; +} + +static int ecap_cnt_enable_read(struct counter_device *counter, + struct counter_count *count, u8 *enable) +{ + struct ecap_cnt_dev *ecap_dev = counter_priv(counter); + + *enable = ecap_dev->enabled; + + return 0; +} + +static int ecap_cnt_enable_write(struct counter_device *counter, + struct counter_count *count, u8 enable) +{ + struct ecap_cnt_dev *ecap_dev = counter_priv(counter); + + mutex_lock(&ecap_dev->lock); + + if (enable == ecap_dev->enabled) + goto out; + + if (enable) + ecap_cnt_capture_enable(counter); + else + ecap_cnt_capture_disable(counter); + ecap_dev->enabled = enable; + +out: + mutex_unlock(&ecap_dev->lock); + + return 0; +} + +static const struct counter_ops ecap_cnt_ops = { + .count_read = ecap_cnt_count_read, + .count_write = ecap_cnt_count_write, + .function_read = ecap_cnt_function_read, + .action_read = ecap_cnt_action_read, + .watch_validate = ecap_cnt_watch_validate, +}; + +static const enum counter_function ecap_cnt_functions[] = { + COUNTER_FUNCTION_INCREASE, +}; + +static const enum counter_synapse_action ecap_cnt_clock_actions[] = { + COUNTER_SYNAPSE_ACTION_RISING_EDGE, +}; + +static const enum counter_synapse_action ecap_cnt_input_actions[] = { + COUNTER_SYNAPSE_ACTION_NONE, +}; + +static struct counter_comp ecap_cnt_clock_ext[] = { + COUNTER_COMP_SIGNAL_U64("frequency", ecap_cnt_clk_get_freq, NULL), +}; + +static const enum counter_signal_polarity ecap_cnt_pol_avail[] = { + COUNTER_SIGNAL_POLARITY_POSITIVE, + COUNTER_SIGNAL_POLARITY_NEGATIVE, +}; + +static DEFINE_COUNTER_ARRAY_POLARITY(ecap_cnt_pol_array, ecap_cnt_pol_avail, ECAP_NB_CEVT); + +static struct counter_comp ecap_cnt_signal_ext[] = { + COUNTER_COMP_ARRAY_POLARITY(ecap_cnt_pol_read, ecap_cnt_pol_write, ecap_cnt_pol_array), +}; + +static struct counter_signal ecap_cnt_signals[] = { + { + .id = ECAP_CLOCK_SIG, + .name = "Clock Signal", + .ext = ecap_cnt_clock_ext, + .num_ext = ARRAY_SIZE(ecap_cnt_clock_ext), + }, + { + .id = ECAP_INPUT_SIG, + .name = "Input Signal", + .ext = ecap_cnt_signal_ext, + .num_ext = ARRAY_SIZE(ecap_cnt_signal_ext), + }, +}; + +static struct counter_synapse ecap_cnt_synapses[] = { + { + .actions_list = ecap_cnt_clock_actions, + .num_actions = ARRAY_SIZE(ecap_cnt_clock_actions), + .signal = &ecap_cnt_signals[ECAP_CLOCK_SIG], + }, + { + .actions_list = ecap_cnt_input_actions, + .num_actions = ARRAY_SIZE(ecap_cnt_input_actions), + .signal = &ecap_cnt_signals[ECAP_INPUT_SIG], + }, +}; + +static DEFINE_COUNTER_ARRAY_CAPTURE(ecap_cnt_cap_array, ECAP_NB_CEVT); + +static struct counter_comp ecap_cnt_count_ext[] = { + COUNTER_COMP_ARRAY_CAPTURE(ecap_cnt_cap_read, ecap_cnt_cap_write, ecap_cnt_cap_array), + COUNTER_COMP_COUNT_U64("num_overflows", ecap_cnt_nb_ovf_read, ecap_cnt_nb_ovf_write), + COUNTER_COMP_CEILING(ecap_cnt_ceiling_read, NULL), + COUNTER_COMP_ENABLE(ecap_cnt_enable_read, ecap_cnt_enable_write), +}; + +static struct counter_count ecap_cnt_counts[] = { + { + .name = "Timestamp Counter", + .functions_list = ecap_cnt_functions, + .num_functions = ARRAY_SIZE(ecap_cnt_functions), + .synapses = ecap_cnt_synapses, + .num_synapses = ARRAY_SIZE(ecap_cnt_synapses), + .ext = ecap_cnt_count_ext, + .num_ext = ARRAY_SIZE(ecap_cnt_count_ext), + }, +}; + +static irqreturn_t ecap_cnt_isr(int irq, void *dev_id) +{ + struct counter_device *counter_dev = dev_id; + struct ecap_cnt_dev *ecap_dev = counter_priv(counter_dev); + unsigned int clr = 0; + unsigned int flg; + int i; + + regmap_read(ecap_dev->regmap, ECAP_ECINT_EN_FLG_REG, &flg); + + /* Check capture events */ + for (i = 0 ; i < ECAP_NB_CEVT ; i++) { + if (flg & ECAP_EVT_FLG_BIT(i)) { + counter_push_event(counter_dev, COUNTER_EVENT_CAPTURE, i); + clr |= ECAP_EVT_CLR_BIT(i); + } + } + + /* Check counter overflow */ + if (flg & ECAP_EVT_FLG_BIT(ECAP_CNTOVF)) { + atomic_inc(&ecap_dev->nb_ovf); + for (i = 0 ; i < ECAP_NB_CEVT ; i++) + counter_push_event(counter_dev, COUNTER_EVENT_OVERFLOW, i); + clr |= ECAP_EVT_CLR_BIT(ECAP_CNTOVF); + } + + clr |= ECAP_INT_CLR_BIT; + regmap_update_bits(ecap_dev->regmap, ECAP_ECINT_CLR_FRC_REG, ECAP_EVT_CLR_MASK, clr); + + return IRQ_HANDLED; +} + +static void ecap_cnt_pm_disable(void *dev) +{ + pm_runtime_disable(dev); +} + +static int ecap_cnt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ecap_cnt_dev *ecap_dev; + struct counter_device *counter_dev; + void __iomem *mmio_base; + unsigned long clk_rate; + int ret; + + counter_dev = devm_counter_alloc(dev, sizeof(*ecap_dev)); + if (IS_ERR(counter_dev)) + return PTR_ERR(counter_dev); + + counter_dev->name = ECAP_DRV_NAME; + counter_dev->parent = dev; + counter_dev->ops = &ecap_cnt_ops; + counter_dev->signals = ecap_cnt_signals; + counter_dev->num_signals = ARRAY_SIZE(ecap_cnt_signals); + counter_dev->counts = ecap_cnt_counts; + counter_dev->num_counts = ARRAY_SIZE(ecap_cnt_counts); + + ecap_dev = counter_priv(counter_dev); + + mutex_init(&ecap_dev->lock); + + ecap_dev->clk = devm_clk_get_enabled(dev, "fck"); + if (IS_ERR(ecap_dev->clk)) + return dev_err_probe(dev, PTR_ERR(ecap_dev->clk), "failed to get clock\n"); + + clk_rate = clk_get_rate(ecap_dev->clk); + if (!clk_rate) { + dev_err(dev, "failed to get clock rate\n"); + return -EINVAL; + } + + mmio_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mmio_base)) + return PTR_ERR(mmio_base); + + ecap_dev->regmap = devm_regmap_init_mmio(dev, mmio_base, &ecap_cnt_regmap_config); + if (IS_ERR(ecap_dev->regmap)) + return dev_err_probe(dev, PTR_ERR(ecap_dev->regmap), "failed to init regmap\n"); + + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get irq\n"); + + ret = devm_request_irq(dev, ret, ecap_cnt_isr, 0, pdev->name, counter_dev); + if (ret) + return dev_err_probe(dev, ret, "failed to request irq\n"); + + platform_set_drvdata(pdev, counter_dev); + + pm_runtime_enable(dev); + + /* Register a cleanup callback to care for disabling PM */ + ret = devm_add_action_or_reset(dev, ecap_cnt_pm_disable, dev); + if (ret) + return dev_err_probe(dev, ret, "failed to add pm disable action\n"); + + ret = devm_counter_add(dev, counter_dev); + if (ret) + return dev_err_probe(dev, ret, "failed to add counter\n"); + + return 0; +} + +static int ecap_cnt_remove(struct platform_device *pdev) +{ + struct counter_device *counter_dev = platform_get_drvdata(pdev); + struct ecap_cnt_dev *ecap_dev = counter_priv(counter_dev); + + if (ecap_dev->enabled) + ecap_cnt_capture_disable(counter_dev); + + return 0; +} + +static int ecap_cnt_suspend(struct device *dev) +{ + struct counter_device *counter_dev = dev_get_drvdata(dev); + struct ecap_cnt_dev *ecap_dev = counter_priv(counter_dev); + + /* If eCAP is running, stop capture then save timestamp counter */ + if (ecap_dev->enabled) { + /* + * Disabling capture has the following effects: + * - interrupts are disabled + * - loading of capture registers is disabled + * - timebase counter is stopped + */ + ecap_cnt_capture_disable(counter_dev); + ecap_dev->pm_ctx.time_cntr = ecap_cnt_count_get_val(counter_dev, ECAP_TSCNT_REG); + } + + ecap_dev->pm_ctx.ev_mode = ecap_cnt_capture_get_evmode(counter_dev); + + clk_disable(ecap_dev->clk); + + return 0; +} + +static int ecap_cnt_resume(struct device *dev) +{ + struct counter_device *counter_dev = dev_get_drvdata(dev); + struct ecap_cnt_dev *ecap_dev = counter_priv(counter_dev); + + clk_enable(ecap_dev->clk); + + ecap_cnt_capture_set_evmode(counter_dev, ecap_dev->pm_ctx.ev_mode); + + /* If eCAP was running, restore timestamp counter then run capture */ + if (ecap_dev->enabled) { + ecap_cnt_count_set_val(counter_dev, ECAP_TSCNT_REG, ecap_dev->pm_ctx.time_cntr); + ecap_cnt_capture_enable(counter_dev); + } + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(ecap_cnt_pm_ops, ecap_cnt_suspend, ecap_cnt_resume); + +static const struct of_device_id ecap_cnt_of_match[] = { + { .compatible = "ti,am62-ecap-capture" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ecap_cnt_of_match); + +static struct platform_driver ecap_cnt_driver = { + .probe = ecap_cnt_probe, + .remove = ecap_cnt_remove, + .driver = { + .name = "ecap-capture", + .of_match_table = ecap_cnt_of_match, + .pm = pm_sleep_ptr(&ecap_cnt_pm_ops), + }, +}; +module_platform_driver(ecap_cnt_driver); + +MODULE_DESCRIPTION("ECAP Capture driver"); +MODULE_AUTHOR("Julien Panis "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(COUNTER); -- cgit v1.2.3 From 54d0999f96abadcc0586fe577e6cf4dc4c2749c1 Mon Sep 17 00:00:00 2001 From: Julien Panis Date: Tue, 27 Sep 2022 18:53:46 -0400 Subject: MAINTAINERS: add TI ECAP driver info This commit adds driver info for TI ECAP used in capture operating mode. Signed-off-by: Julien Panis Link: https://lore.kernel.org/r/20220923142437.271328-5-jpanis@baylibre.com/ Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/bb980cb69381c570b72701398991100ac91079ec.1664318353.git.william.gray@linaro.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index bccb697b346f..c547559eddf9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20346,6 +20346,15 @@ T: git git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git F: drivers/media/platform/ti/davinci/ F: include/media/davinci/ +TI ENHANCED CAPTURE (eCAP) DRIVER +M: Vignesh Raghavendra +R: Julien Panis +L: linux-iio@vger.kernel.org +L: linux-omap@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/counter/ti,am62-ecap-capture.yaml +F: drivers/counter/ti-ecap-capture.c + TI ENHANCED QUADRATURE ENCODER PULSE (eQEP) DRIVER R: David Lechner L: linux-iio@vger.kernel.org -- cgit v1.2.3 From 9357fc3b3b85a7d9f0dedf5446222a26957852b4 Mon Sep 17 00:00:00 2001 From: keliu Date: Thu, 29 Sep 2022 17:50:10 -0700 Subject: drivers: spmi: Directly use ida_alloc()/free() Use ida_alloc()/ida_free() instead of deprecated ida_simple_get()/ida_simple_remove() . Signed-off-by: keliu Link: https://lore.kernel.org/r/20220527071338.2359733-1-liuke94@huawei.com Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20220930005019.2663064-2-sboyd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/spmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c index a456ce5141e1..55381592bb5a 100644 --- a/drivers/spmi/spmi.c +++ b/drivers/spmi/spmi.c @@ -35,7 +35,7 @@ static void spmi_ctrl_release(struct device *dev) { struct spmi_controller *ctrl = to_spmi_controller(dev); - ida_simple_remove(&ctrl_ida, ctrl->nr); + ida_free(&ctrl_ida, ctrl->nr); kfree(ctrl); } @@ -457,7 +457,7 @@ struct spmi_controller *spmi_controller_alloc(struct device *parent, ctrl->dev.of_node = parent->of_node; spmi_controller_set_drvdata(ctrl, &ctrl[1]); - id = ida_simple_get(&ctrl_ida, 0, 0, GFP_KERNEL); + id = ida_alloc(&ctrl_ida, GFP_KERNEL); if (id < 0) { dev_err(parent, "unable to allocate SPMI controller identifier.\n"); -- cgit v1.2.3 From 33c912d3c5efea62298921627fad7f5ad396b8fb Mon Sep 17 00:00:00 2001 From: Abhijeet Dharmapurikar Date: Thu, 29 Sep 2022 17:50:11 -0700 Subject: spmi: pmic-arb: add a print in cleanup_irq The cleanup_irq() was meant to clear and mask interrupts that were left enabled in the hardware but there was no interrupt handler registered for it. Add an error print when it gets invoked. Signed-off-by: Abhijeet Dharmapurikar Signed-off-by: David Collins Signed-off-by: Fenglin Wu Link: https://lore.kernel.org/r/1655004286-11493-2-git-send-email-quic_fenglinw@quicinc.com Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20220930005019.2663064-3-sboyd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/spmi-pmic-arb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 2113be40b5a9..5a99723b3f32 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -590,6 +590,8 @@ static void cleanup_irq(struct spmi_pmic_arb *pmic_arb, u16 apid, int id) u8 per = ppid & 0xFF; u8 irq_mask = BIT(id); + dev_err_ratelimited(&pmic_arb->spmic->dev, "%s apid=%d sid=0x%x per=0x%x irq=%d\n", + __func__, apid, sid, per, id); writel_relaxed(irq_mask, pmic_arb->ver_ops->irq_clear(pmic_arb, apid)); if (pmic_arb_write_cmd(pmic_arb->spmic, SPMI_CMD_EXT_WRITEL, sid, -- cgit v1.2.3 From abb9088b3a39cfec1321e93170ee3c0c1255fd9d Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Thu, 29 Sep 2022 17:50:12 -0700 Subject: spmi: pmic-arb: handle spurious interrupt Call handle_bad_irq() when the summary interrupt is fired spuriously. Signed-off-by: Fenglin Wu Link: https://lore.kernel.org/r/1655004286-11493-3-git-send-email-quic_fenglinw@quicinc.com Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20220930005019.2663064-4-sboyd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/spmi-pmic-arb.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 5a99723b3f32..719bd73e5153 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -605,10 +605,11 @@ static void cleanup_irq(struct spmi_pmic_arb *pmic_arb, u16 apid, int id) irq_mask, ppid); } -static void periph_interrupt(struct spmi_pmic_arb *pmic_arb, u16 apid) +static int periph_interrupt(struct spmi_pmic_arb *pmic_arb, u16 apid) { unsigned int irq; u32 status, id; + int handled = 0; u8 sid = (pmic_arb->apid_data[apid].ppid >> 8) & 0xF; u8 per = pmic_arb->apid_data[apid].ppid & 0xFF; @@ -623,7 +624,10 @@ static void periph_interrupt(struct spmi_pmic_arb *pmic_arb, u16 apid) continue; } generic_handle_irq(irq); + handled++; } + + return handled; } static void pmic_arb_chained_irq(struct irq_desc *desc) @@ -634,7 +638,7 @@ static void pmic_arb_chained_irq(struct irq_desc *desc) int first = pmic_arb->min_apid >> 5; int last = pmic_arb->max_apid >> 5; u8 ee = pmic_arb->ee; - u32 status, enable; + u32 status, enable, handled = 0; int i, id, apid; chained_irq_enter(chip, desc); @@ -649,10 +653,14 @@ static void pmic_arb_chained_irq(struct irq_desc *desc) enable = readl_relaxed( ver_ops->acc_enable(pmic_arb, apid)); if (enable & SPMI_PIC_ACC_ENABLE_BIT) - periph_interrupt(pmic_arb, apid); + if (periph_interrupt(pmic_arb, apid) != 0) + handled++; } } + if (handled == 0) + handle_bad_irq(desc); + chained_irq_exit(chip, desc); } -- cgit v1.2.3 From b6c1761721193c52234e3ed048e4d16ab527bb74 Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Thu, 29 Sep 2022 17:50:13 -0700 Subject: spmi: pmic-arb: do not ack and clear peripheral interrupts in cleanup_irq Currently, cleanup_irq() is invoked when a peripheral's interrupt fires and there is no mapping present in the interrupt domain of spmi interrupt controller. The cleanup_irq clears the arbiter bit, clears the pmic interrupt and disables it at the pmic in that order. The last disable in cleanup_irq races with request_irq() in that it stomps over the enable issued by request_irq. Fix this by not writing to the pmic in cleanup_irq. The latched bit will be left set in the pmic, which will not send us more interrupts even if the enable bit stays enabled. When a client wants to request an interrupt, use the activate callback on the irq_domain to clear latched bit. This ensures that the latched, if set due to the above changes in cleanup_irq or when the bootloader leaves it set, gets cleaned up, paving way for upcoming interrupts to trigger. With this, there is a possibility of unwanted triggering of interrupt right after the latched bit is cleared - the interrupt may be left enabled too. To avoid that, clear the enable first followed by clearing the latched bit in the activate callback. Fixes: 6bc546e71e50 ("spmi: pmic-arb: cleanup unrequested irqs") Fixes: 02abec3616c1 ("spmi: pmic-arb: rename pa_xx to pmic_arb_xx and other cleanup") Signed-off-by: Subbaraman Narayanamurthy [collinsd@codeaurora.org: fix merge conflict] Signed-off-by: David Collins Signed-off-by: Fenglin Wu Link: https://lore.kernel.org/r/1655004286-11493-4-git-send-email-quic_fenglinw@quicinc.com Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20220930005019.2663064-5-sboyd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/spmi-pmic-arb.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 719bd73e5153..2bc3b88f35c9 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -593,16 +593,6 @@ static void cleanup_irq(struct spmi_pmic_arb *pmic_arb, u16 apid, int id) dev_err_ratelimited(&pmic_arb->spmic->dev, "%s apid=%d sid=0x%x per=0x%x irq=%d\n", __func__, apid, sid, per, id); writel_relaxed(irq_mask, pmic_arb->ver_ops->irq_clear(pmic_arb, apid)); - - if (pmic_arb_write_cmd(pmic_arb->spmic, SPMI_CMD_EXT_WRITEL, sid, - (per << 8) + QPNPINT_REG_LATCHED_CLR, &irq_mask, 1)) - dev_err_ratelimited(&pmic_arb->spmic->dev, "failed to ack irq_mask = 0x%x for ppid = %x\n", - irq_mask, ppid); - - if (pmic_arb_write_cmd(pmic_arb->spmic, SPMI_CMD_EXT_WRITEL, sid, - (per << 8) + QPNPINT_REG_EN_CLR, &irq_mask, 1)) - dev_err_ratelimited(&pmic_arb->spmic->dev, "failed to ack irq_mask = 0x%x for ppid = %x\n", - irq_mask, ppid); } static int periph_interrupt(struct spmi_pmic_arb *pmic_arb, u16 apid) @@ -780,6 +770,7 @@ static int qpnpint_irq_domain_activate(struct irq_domain *domain, u16 apid = hwirq_to_apid(d->hwirq); u16 sid = hwirq_to_sid(d->hwirq); u16 irq = hwirq_to_irq(d->hwirq); + u8 buf; if (pmic_arb->apid_data[apid].irq_ee != pmic_arb->ee) { dev_err(&pmic_arb->spmic->dev, "failed to xlate sid = %#x, periph = %#x, irq = %u: ee=%u but owner=%u\n", @@ -788,6 +779,10 @@ static int qpnpint_irq_domain_activate(struct irq_domain *domain, return -ENODEV; } + buf = BIT(irq); + qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &buf, 1); + qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &buf, 1); + return 0; } -- cgit v1.2.3 From 191adbdd26f1a26c9302369b2701bfddf8c0780e Mon Sep 17 00:00:00 2001 From: David Collins Date: Thu, 29 Sep 2022 17:50:14 -0700 Subject: spmi: pmic-arb: check apid against limits before calling irq handler Check that the apid for an SPMI interrupt falls between the min_apid and max_apid that can be handled by the APPS processor before invoking the per-apid interrupt handler: periph_interrupt(). This avoids an access violation in rare cases where the status bit is set for an interrupt that is not owned by the APPS processor. Signed-off-by: David Collins Signed-off-by: Fenglin Wu Link: https://lore.kernel.org/r/1655004286-11493-5-git-send-email-quic_fenglinw@quicinc.com Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20220930005019.2663064-6-sboyd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/spmi-pmic-arb.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 2bc3b88f35c9..e19eaec30aa5 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -625,21 +625,26 @@ static void pmic_arb_chained_irq(struct irq_desc *desc) struct spmi_pmic_arb *pmic_arb = irq_desc_get_handler_data(desc); const struct pmic_arb_ver_ops *ver_ops = pmic_arb->ver_ops; struct irq_chip *chip = irq_desc_get_chip(desc); - int first = pmic_arb->min_apid >> 5; - int last = pmic_arb->max_apid >> 5; + int first = pmic_arb->min_apid; + int last = pmic_arb->max_apid; u8 ee = pmic_arb->ee; u32 status, enable, handled = 0; int i, id, apid; chained_irq_enter(chip, desc); - for (i = first; i <= last; ++i) { + for (i = first >> 5; i <= last >> 5; ++i) { status = readl_relaxed( ver_ops->owner_acc_status(pmic_arb, ee, i)); while (status) { id = ffs(status) - 1; status &= ~BIT(id); apid = id + i * 32; + if (apid < first || apid > last) { + WARN_ONCE(true, "spurious spmi irq received for apid=%d\n", + apid); + continue; + } enable = readl_relaxed( ver_ops->acc_enable(pmic_arb, apid)); if (enable & SPMI_PIC_ACC_ENABLE_BIT) -- cgit v1.2.3 From 4df88fe5b631bb9e381880b4cca73e91750afffe Mon Sep 17 00:00:00 2001 From: Ashay Jaiswal Date: Thu, 29 Sep 2022 17:50:15 -0700 Subject: spmi: pmic-arb: add support to dispatch interrupt based on IRQ status Current implementation of SPMI arbiter dispatches interrupt based on the Arbiter's accumulator status, in some cases the accumulator status may remain zero and the interrupt remains un-handled. Add logic to dispatch interrupts based Arbiter's IRQ status if the accumulator status is zero. Signed-off-by: Ashay Jaiswal Signed-off-by: David Collins Signed-off-by: Fenglin Wu Link: https://lore.kernel.org/r/1655004286-11493-6-git-send-email-quic_fenglinw@quicinc.com Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20220930005019.2663064-7-sboyd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/spmi-pmic-arb.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index e19eaec30aa5..56f22941d570 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -630,12 +630,18 @@ static void pmic_arb_chained_irq(struct irq_desc *desc) u8 ee = pmic_arb->ee; u32 status, enable, handled = 0; int i, id, apid; + /* status based dispatch */ + bool acc_valid = false; + u32 irq_status = 0; chained_irq_enter(chip, desc); for (i = first >> 5; i <= last >> 5; ++i) { status = readl_relaxed( ver_ops->owner_acc_status(pmic_arb, ee, i)); + if (status) + acc_valid = true; + while (status) { id = ffs(status) - 1; status &= ~BIT(id); @@ -653,6 +659,29 @@ static void pmic_arb_chained_irq(struct irq_desc *desc) } } + /* ACC_STATUS is empty but IRQ fired check IRQ_STATUS */ + if (!acc_valid) { + for (i = first; i <= last; i++) { + /* skip if APPS is not irq owner */ + if (pmic_arb->apid_data[i].irq_ee != pmic_arb->ee) + continue; + + irq_status = readl_relaxed( + ver_ops->irq_status(pmic_arb, i)); + if (irq_status) { + enable = readl_relaxed( + ver_ops->acc_enable(pmic_arb, i)); + if (enable & SPMI_PIC_ACC_ENABLE_BIT) { + dev_dbg(&pmic_arb->spmic->dev, + "Dispatching IRQ for apid=%d status=%x\n", + i, irq_status); + if (periph_interrupt(pmic_arb, i) != 0) + handled++; + } + } + } + } + if (handled == 0) handle_bad_irq(desc); -- cgit v1.2.3 From 1f1693118c2476cb1666ad357edcf3cf48bf9b16 Mon Sep 17 00:00:00 2001 From: David Collins Date: Thu, 29 Sep 2022 17:50:16 -0700 Subject: spmi: pmic-arb: correct duplicate APID to PPID mapping logic Correct the way that duplicate PPID mappings are handled for PMIC arbiter v5. The final APID mapped to a given PPID should be the one which has write owner = APPS EE, if it exists, or if not that, then the first APID mapped to the PPID, if it exists. Fixes: 40f318f0ed67 ("spmi: pmic-arb: add support for HW version 5") Signed-off-by: David Collins Signed-off-by: Fenglin Wu Link: https://lore.kernel.org/r/1655004286-11493-7-git-send-email-quic_fenglinw@quicinc.com Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20220930005019.2663064-8-sboyd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/spmi-pmic-arb.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 56f22941d570..cf92abc51689 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -1031,7 +1031,8 @@ static int pmic_arb_read_apid_map_v5(struct spmi_pmic_arb *pmic_arb) * version 5, there is more than one APID mapped to each PPID. * The owner field for each of these mappings specifies the EE which is * allowed to write to the APID. The owner of the last (highest) APID - * for a given PPID will receive interrupts from the PPID. + * which has the IRQ owner bit set for a given PPID will receive + * interrupts from the PPID. */ for (i = 0; ; i++, apidd++) { offset = pmic_arb->ver_ops->apid_map_offset(i); @@ -1054,16 +1055,16 @@ static int pmic_arb_read_apid_map_v5(struct spmi_pmic_arb *pmic_arb) apid = pmic_arb->ppid_to_apid[ppid] & ~PMIC_ARB_APID_VALID; prev_apidd = &pmic_arb->apid_data[apid]; - if (valid && is_irq_ee && - prev_apidd->write_ee == pmic_arb->ee) { + if (!valid || apidd->write_ee == pmic_arb->ee) { + /* First PPID mapping or one for this EE */ + pmic_arb->ppid_to_apid[ppid] = i | PMIC_ARB_APID_VALID; + } else if (valid && is_irq_ee && + prev_apidd->write_ee == pmic_arb->ee) { /* * Duplicate PPID mapping after the one for this EE; * override the irq owner */ prev_apidd->irq_ee = apidd->irq_ee; - } else if (!valid || is_irq_ee) { - /* First PPID mapping or duplicate for another EE */ - pmic_arb->ppid_to_apid[ppid] = i | PMIC_ARB_APID_VALID; } apidd->ppid = ppid; -- cgit v1.2.3 From c8669773c74e26ceb2412215a2db40399e3fe119 Mon Sep 17 00:00:00 2001 From: David Collins Date: Thu, 29 Sep 2022 17:50:17 -0700 Subject: spmi: pmic-arb: block access for invalid PMIC arbiter v5 SPMI writes The system crashes due to an access permission violation when writing to a PMIC peripheral which is not owned by the current ee. Add a check for PMIC arbiter version 5 for such invalid write requests and return an error instead of crashing the system. Signed-off-by: David Collins Signed-off-by: Fenglin Wu Link: https://lore.kernel.org/r/1655004286-11493-8-git-send-email-quic_fenglinw@quicinc.com Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20220930005019.2663064-9-sboyd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/spmi-pmic-arb.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index cf92abc51689..39f25bc26233 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -1133,6 +1133,11 @@ static int pmic_arb_offset_v5(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr, offset = 0x10000 * pmic_arb->ee + 0x80 * apid; break; case PMIC_ARB_CHANNEL_RW: + if (pmic_arb->apid_data[apid].write_ee != pmic_arb->ee) { + dev_err(&pmic_arb->spmic->dev, "disallowed SPMI write to sid=%u, addr=0x%04X\n", + sid, addr); + return -EPERM; + } offset = 0x10000 * apid; break; } -- cgit v1.2.3 From 9c573cdcf913e0a38bc260634017be3174595c56 Mon Sep 17 00:00:00 2001 From: David Collins Date: Thu, 29 Sep 2022 17:50:18 -0700 Subject: spmi: pmic-arb: increase SPMI transaction timeout delay Increase the SPMI transaction timeout delay from 100 us to 1000 us in order to account for the slower execution time found on some simulator targets. Signed-off-by: David Collins Signed-off-by: Fenglin Wu Link: https://lore.kernel.org/r/1655004286-11493-11-git-send-email-quic_fenglinw@quicinc.com Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20220930005019.2663064-10-sboyd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/spmi-pmic-arb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 39f25bc26233..2cf3203b2397 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -91,7 +91,7 @@ enum pmic_arb_channel { /* Maximum number of support PMIC peripherals */ #define PMIC_ARB_MAX_PERIPHS 512 -#define PMIC_ARB_TIMEOUT_US 100 +#define PMIC_ARB_TIMEOUT_US 1000 #define PMIC_ARB_MAX_TRANS_BYTES (8) #define PMIC_ARB_APID_MASK 0xFF -- cgit v1.2.3 From 7cd04013fbf3e6dcb67ca6b59aa813269a2ad9ce Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 26 Sep 2022 17:39:27 -0700 Subject: w1: Split memcpy() of struct cn_msg flexible array To work around a misbehavior of the compiler's ability to see into composite flexible array structs (as detailed in the coming memcpy() hardening series[1]), split the memcpy() of the header and the payload so no false positive run-time overflow warning will be generated. [1] https://lore.kernel.org/linux-hardening/20220901065914.1417829-2-keescook@chromium.org/ Cc: Evgeniy Polyakov Signed-off-by: Kees Cook Reviewed-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20220927003927.1942170-1-keescook@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/w1/w1_netlink.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/w1/w1_netlink.c b/drivers/w1/w1_netlink.c index fa490aa4407c..db110cc442b1 100644 --- a/drivers/w1/w1_netlink.c +++ b/drivers/w1/w1_netlink.c @@ -611,7 +611,8 @@ static void w1_cn_callback(struct cn_msg *cn, struct netlink_skb_parms *nsp) } atomic_set(&block->refcnt, 1); block->portid = nsp->portid; - memcpy(&block->request_cn, cn, sizeof(*cn) + cn->len); + block->request_cn = *cn; + memcpy(block->request_cn.data, cn->data, cn->len); node = (struct w1_cb_node *)(block->request_cn.data + cn->len); /* Sneeky, when not bundling, reply_size is the allocated space -- cgit v1.2.3