From f7f6c060547c51691f9b943e6c33f63675c1a0a9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 14 Mar 2017 18:46:52 +0200 Subject: mfd: exynos-lpass: Use common soc/exynos-regs-pmu.h header The MFD-specific header will go away because it duplicates defines from exynos-regs-pmu.h. Reported-by: kbuild test robot Signed-off-by: Krzysztof Kozlowski Reviewed-by: Bartlomiej Zolnierkiewicz Signed-off-by: Lee Jones --- drivers/mfd/exynos-lpass.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/exynos-lpass.c b/drivers/mfd/exynos-lpass.c index 2e064fb8826f..8bebad92a385 100644 --- a/drivers/mfd/exynos-lpass.c +++ b/drivers/mfd/exynos-lpass.c @@ -18,11 +18,11 @@ #include #include #include -#include #include #include #include #include +#include #include /* LPASS Top register definitions */ @@ -83,7 +83,7 @@ static void exynos_lpass_enable(struct exynos_lpass *lpass) /* Activate related PADs from retention state */ regmap_write(lpass->pmu, EXYNOS5433_PAD_RETENTION_AUD_OPTION, - EXYNOS5433_PAD_INITIATE_WAKEUP_FROM_LOWPWR); + EXYNOS_WAKEUP_FROM_LOWPWR); exynos_lpass_core_sw_reset(lpass, LPASS_I2S_SW_RESET); exynos_lpass_core_sw_reset(lpass, LPASS_DMA_SW_RESET); -- cgit v1.2.3-59-g8ed1b From f6dd8449cd50de25881b76cecf1086bebeb11fe8 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 17 Mar 2017 10:05:18 +0000 Subject: mfd: wm831x: Add basic device tree binding Add the basic ability to register the device through device tree, more work is needed to get each individual sub-driver functioning correctly but this is enough to get the device to probe from device tree. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/wm831x-core.c | 29 +++++++++++++++++++++-------- drivers/mfd/wm831x-i2c.c | 19 ++++++++++++++++++- drivers/mfd/wm831x-irq.c | 6 +++--- drivers/mfd/wm831x-spi.c | 18 ++++++++++++++++-- include/linux/mfd/wm831x/core.h | 9 ++++++++- 5 files changed, 66 insertions(+), 15 deletions(-) diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index 3e0e99ec5836..13a4c1190dca 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include @@ -1613,12 +1615,24 @@ struct regmap_config wm831x_regmap_config = { }; EXPORT_SYMBOL_GPL(wm831x_regmap_config); +const struct of_device_id wm831x_of_match[] = { + { .compatible = "wlf,wm8310", .data = (void *)WM8310 }, + { .compatible = "wlf,wm8311", .data = (void *)WM8311 }, + { .compatible = "wlf,wm8312", .data = (void *)WM8312 }, + { .compatible = "wlf,wm8320", .data = (void *)WM8320 }, + { .compatible = "wlf,wm8321", .data = (void *)WM8321 }, + { .compatible = "wlf,wm8325", .data = (void *)WM8325 }, + { .compatible = "wlf,wm8326", .data = (void *)WM8326 }, + { }, +}; +EXPORT_SYMBOL_GPL(wm831x_of_match); + /* * Instantiate the generic non-control parts of the device. */ -int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) +int wm831x_device_init(struct wm831x *wm831x, int irq) { - struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev); + struct wm831x_pdata *pdata = &wm831x->pdata; int rev, wm831x_num; enum wm831x_parent parent; int ret, i; @@ -1627,8 +1641,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) mutex_init(&wm831x->key_lock); dev_set_drvdata(wm831x->dev, wm831x); - if (pdata) - wm831x->soft_shutdown = pdata->soft_shutdown; + wm831x->soft_shutdown = pdata->soft_shutdown; ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID); if (ret < 0) { @@ -1663,7 +1676,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) */ if (ret == 0) { dev_info(wm831x->dev, "Device is an engineering sample\n"); - ret = id; + ret = wm831x->type; } switch (ret) { @@ -1736,9 +1749,9 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) /* This will need revisiting in future but is OK for all * current parts. */ - if (parent != id) - dev_warn(wm831x->dev, "Device was registered as a WM%lx\n", - id); + if (parent != wm831x->type) + dev_warn(wm831x->dev, "Device was registered as a WM%x\n", + wm831x->type); /* Bootstrap the user key */ ret = wm831x_reg_read(wm831x, WM831X_SECURITY_KEY); diff --git a/drivers/mfd/wm831x-i2c.c b/drivers/mfd/wm831x-i2c.c index 824bcbaa9624..781af060f32d 100644 --- a/drivers/mfd/wm831x-i2c.c +++ b/drivers/mfd/wm831x-i2c.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include @@ -27,15 +29,26 @@ static int wm831x_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + struct wm831x_pdata *pdata = dev_get_platdata(&i2c->dev); + const struct of_device_id *of_id; struct wm831x *wm831x; + enum wm831x_parent type; int ret; + if (i2c->dev.of_node) { + of_id = of_match_device(wm831x_of_match, &i2c->dev); + type = (enum wm831x_parent)of_id->data; + } else { + type = (enum wm831x_parent)id->driver_data; + } + wm831x = devm_kzalloc(&i2c->dev, sizeof(struct wm831x), GFP_KERNEL); if (wm831x == NULL) return -ENOMEM; i2c_set_clientdata(i2c, wm831x); wm831x->dev = &i2c->dev; + wm831x->type = type; wm831x->regmap = devm_regmap_init_i2c(i2c, &wm831x_regmap_config); if (IS_ERR(wm831x->regmap)) { @@ -45,7 +58,10 @@ static int wm831x_i2c_probe(struct i2c_client *i2c, return ret; } - return wm831x_device_init(wm831x, id->driver_data, i2c->irq); + if (pdata) + memcpy(&wm831x->pdata, pdata, sizeof(*pdata)); + + return wm831x_device_init(wm831x, i2c->irq); } static int wm831x_i2c_remove(struct i2c_client *i2c) @@ -94,6 +110,7 @@ static struct i2c_driver wm831x_i2c_driver = { .driver = { .name = "wm831x", .pm = &wm831x_pm_ops, + .of_match_table = of_match_ptr(wm831x_of_match), }, .probe = wm831x_i2c_probe, .remove = wm831x_i2c_remove, diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c index dfea8b9c2fe6..c01239a600db 100644 --- a/drivers/mfd/wm831x-irq.c +++ b/drivers/mfd/wm831x-irq.c @@ -564,7 +564,7 @@ static const struct irq_domain_ops wm831x_irq_domain_ops = { int wm831x_irq_init(struct wm831x *wm831x, int irq) { - struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev); + struct wm831x_pdata *pdata = &wm831x->pdata; struct irq_domain *domain; int i, ret, irq_base; @@ -579,7 +579,7 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq) } /* Try to dynamically allocate IRQs if no base is specified */ - if (pdata && pdata->irq_base) { + if (pdata->irq_base) { irq_base = irq_alloc_descs(pdata->irq_base, 0, WM831X_NUM_IRQS, 0); if (irq_base < 0) { @@ -608,7 +608,7 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq) return -EINVAL; } - if (pdata && pdata->irq_cmos) + if (pdata->irq_cmos) i = 0; else i = WM831X_IRQ_OD; diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c index 80482aeb246a..c332e2885b26 100644 --- a/drivers/mfd/wm831x-spi.c +++ b/drivers/mfd/wm831x-spi.c @@ -14,6 +14,8 @@ #include #include +#include +#include #include #include #include @@ -23,12 +25,19 @@ static int wm831x_spi_probe(struct spi_device *spi) { + struct wm831x_pdata *pdata = dev_get_platdata(&spi->dev); const struct spi_device_id *id = spi_get_device_id(spi); + const struct of_device_id *of_id; struct wm831x *wm831x; enum wm831x_parent type; int ret; - type = (enum wm831x_parent)id->driver_data; + if (spi->dev.of_node) { + of_id = of_match_device(wm831x_of_match, &spi->dev); + type = (enum wm831x_parent)of_id->data; + } else { + type = (enum wm831x_parent)id->driver_data; + } wm831x = devm_kzalloc(&spi->dev, sizeof(struct wm831x), GFP_KERNEL); if (wm831x == NULL) @@ -38,6 +47,7 @@ static int wm831x_spi_probe(struct spi_device *spi) spi_set_drvdata(spi, wm831x); wm831x->dev = &spi->dev; + wm831x->type = type; wm831x->regmap = devm_regmap_init_spi(spi, &wm831x_regmap_config); if (IS_ERR(wm831x->regmap)) { @@ -47,7 +57,10 @@ static int wm831x_spi_probe(struct spi_device *spi) return ret; } - return wm831x_device_init(wm831x, type, spi->irq); + if (pdata) + memcpy(&wm831x->pdata, pdata, sizeof(*pdata)); + + return wm831x_device_init(wm831x, spi->irq); } static int wm831x_spi_remove(struct spi_device *spi) @@ -97,6 +110,7 @@ static struct spi_driver wm831x_spi_driver = { .driver = { .name = "wm831x", .pm = &wm831x_spi_pm, + .of_match_table = of_match_ptr(wm831x_of_match), }, .id_table = wm831x_spi_ids, .probe = wm831x_spi_probe, diff --git a/include/linux/mfd/wm831x/core.h b/include/linux/mfd/wm831x/core.h index 76c22648436f..b49fa67612f1 100644 --- a/include/linux/mfd/wm831x/core.h +++ b/include/linux/mfd/wm831x/core.h @@ -21,6 +21,8 @@ #include #include #include +#include +#include /* * Register values. @@ -367,6 +369,9 @@ struct wm831x { struct regmap *regmap; + struct wm831x_pdata pdata; + enum wm831x_parent type; + int irq; /* Our chip IRQ */ struct mutex irq_lock; struct irq_domain *irq_domain; @@ -412,7 +417,7 @@ int wm831x_set_bits(struct wm831x *wm831x, unsigned short reg, int wm831x_bulk_read(struct wm831x *wm831x, unsigned short reg, int count, u16 *buf); -int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq); +int wm831x_device_init(struct wm831x *wm831x, int irq); void wm831x_device_exit(struct wm831x *wm831x); int wm831x_device_suspend(struct wm831x *wm831x); void wm831x_device_shutdown(struct wm831x *wm831x); @@ -427,4 +432,6 @@ static inline int wm831x_irq(struct wm831x *wm831x, int irq) extern struct regmap_config wm831x_regmap_config; +extern const struct of_device_id wm831x_of_match[]; + #endif -- cgit v1.2.3-59-g8ed1b From 9674a8dc5bd1f3cd27f4e6f668beadc64475f3e9 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 17 Mar 2017 10:05:19 +0000 Subject: gpio: wm831x: Add basic device tree support Now the wm831x-core has basic DT support we can update this driver to allow use of the GPIOs within a device tree system. Signed-off-by: Charles Keepax Acked-by: Linus Walleij Signed-off-by: Lee Jones --- drivers/gpio/gpio-wm831x.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-wm831x.c b/drivers/gpio/gpio-wm831x.c index 00e3839b3f96..938bbe3f831c 100644 --- a/drivers/gpio/gpio-wm831x.c +++ b/drivers/gpio/gpio-wm831x.c @@ -263,7 +263,7 @@ static const struct gpio_chip template_chip = { static int wm831x_gpio_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); - struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev); + struct wm831x_pdata *pdata = &wm831x->pdata; struct wm831x_gpio *wm831x_gpio; int ret; @@ -280,6 +280,9 @@ static int wm831x_gpio_probe(struct platform_device *pdev) wm831x_gpio->gpio_chip.base = pdata->gpio_base; else wm831x_gpio->gpio_chip.base = -1; +#ifdef CONFIG_OF_GPIO + wm831x_gpio->gpio_chip.of_node = wm831x->dev->of_node; +#endif ret = devm_gpiochip_add_data(&pdev->dev, &wm831x_gpio->gpio_chip, wm831x_gpio); -- cgit v1.2.3-59-g8ed1b From 9f7c7ceefe5e824e8333859bded12257a6d3548a Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 17 Mar 2017 10:05:20 +0000 Subject: mfd: wm831x: Add device tree binding document Add a device tree binding document for the wm831x series of PMICs. Currently only support for the registering the device and the GPIOs are actually implemented in the driver. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/wm831x.txt | 81 ++++++++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 82 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/wm831x.txt diff --git a/Documentation/devicetree/bindings/mfd/wm831x.txt b/Documentation/devicetree/bindings/mfd/wm831x.txt new file mode 100644 index 000000000000..9f8b7430673c --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/wm831x.txt @@ -0,0 +1,81 @@ +Cirrus Logic/Wolfson Microelectronics wm831x PMICs + +System PMICs with a wide range of additional features. + +Required properties: + + - compatible : One of the following chip-specific strings: + "wlf,wm8310" + "wlf,wm8311" + "wlf,wm8312" + "wlf,wm8320" + "wlf,wm8321" + "wlf,wm8325" + "wlf,wm8326" + + - reg : I2C slave address when connected using I2C, chip select number + when using SPI. + + - gpio-controller : Indicates this device is a GPIO controller. + - #gpio-cells : Must be 2. The first cell is the pin number and the + second cell is used to specify optional parameters (currently unused). + + - interrupts : The interrupt line the IRQ signal for the device is + connected to. + - interrupt-parent : The parent interrupt controller. + + - interrupt-controller : wm831x devices contain interrupt controllers and + may provide interrupt services to other devices. + - #interrupt-cells: Must be 2. The first cell is the IRQ number, and the + second cell is the flags, encoded as the trigger masks from + ../interrupt-controller/interrupts.txt + +Optional sub-nodes: + - regulators : Contains sub-nodes for each of the regulators supplied by + the device. The regulators are bound using their names listed below: + + dcdc1 : DCDC1 + dcdc2 : DCDC2 + dcdc3 : DCDC3 + dcdc4 : DCDC3 + isink1 : ISINK1 + isink2 : ISINK2 + ldo1 : LDO1 + ldo2 : LDO2 + ldo3 : LDO3 + ldo4 : LDO4 + ldo5 : LDO5 + ldo7 : LDO7 + ldo11 : LDO11 + + The bindings details of each regulator can be found in: + ../regulator/regulator.txt + +Example: + +wm8310: pmic@36 { + compatible = "wlf,wm8310"; + reg = <0x36>; + + gpio-controller; + #gpio-cells = <2>; + + interrupts = <347>; + interrupt-parent = <&gic>; + + interrupt-controller; + #interrupt-cells = <2>; + + regulators { + dcdc1: dcdc1 { + regulator-name = "DCDC1"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <600000>; + }; + ldo1: ldo1 { + regulator-name = "LDO1"; + regulator-min-microvolt = <1700000>; + regulator-max-microvolt = <1700000>; + }; + }; +}; diff --git a/MAINTAINERS b/MAINTAINERS index c265a5fe4848..dfe761bb4998 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13600,6 +13600,7 @@ F: Documentation/hwmon/wm83?? F: Documentation/devicetree/bindings/extcon/extcon-arizona.txt F: Documentation/devicetree/bindings/regulator/arizona-regulator.txt F: Documentation/devicetree/bindings/mfd/arizona.txt +F: Documentation/devicetree/bindings/mfd/wm831x.txt F: arch/arm/mach-s3c64xx/mach-crag6410* F: drivers/clk/clk-wm83*.c F: drivers/extcon/extcon-arizona.c -- cgit v1.2.3-59-g8ed1b From ead25133e9352896af4de68d2f33f1ef68997e16 Mon Sep 17 00:00:00 2001 From: Ksenija Stanojevic Date: Thu, 16 Mar 2017 13:27:09 +0100 Subject: mfd: mxs-lradc: Add support for mxs-lradc Add core files for low resolution analog-to-digital converter (mxs-lradc) MFD driver. Signed-off-by: Ksenija Stanojevic Reviewed-by: Marek Vasut Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 17 +++ drivers/mfd/Makefile | 1 + drivers/mfd/mxs-lradc.c | 267 ++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/mxs-lradc.h | 187 +++++++++++++++++++++++++++++ 4 files changed, 472 insertions(+) create mode 100644 drivers/mfd/mxs-lradc.c create mode 100644 include/linux/mfd/mxs-lradc.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 55ecdfb74d31..8bbc91b5186e 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -344,6 +344,23 @@ config MFD_MC13XXX_I2C help Select this if your MC13xxx is connected via an I2C bus. +config MFD_MXS_LRADC + tristate "Freescale i.MX23/i.MX28 LRADC" + depends on ARCH_MXS || COMPILE_TEST + select MFD_CORE + select STMP_DEVICE + help + Say yes here to build support for the Low Resolution + Analog-to-Digital Converter (LRADC) found on the i.MX23 and i.MX28 + processors. This driver provides common support for accessing the + device, additional drivers must be enabled in order to use the + functionality of the device: + mxs-lradc-adc for ADC readings + mxs-lradc-ts for touchscreen support + + This driver can also be built as a module. If so, the module will be + called mxs-lradc. + config MFD_MX25_TSADC tristate "Freescale i.MX25 integrated Touchscreen and ADC unit" select REGMAP_MMIO diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 31ce07611a6f..790698a892ba 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -215,3 +215,4 @@ obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o obj-$(CONFIG_MFD_SUN4I_GPADC) += sun4i-gpadc.o obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o +obj-$(CONFIG_MFD_MXS_LRADC) += mxs-lradc.o diff --git a/drivers/mfd/mxs-lradc.c b/drivers/mfd/mxs-lradc.c new file mode 100644 index 000000000000..630bd19b2c0a --- /dev/null +++ b/drivers/mfd/mxs-lradc.c @@ -0,0 +1,267 @@ +/* + * Freescale MXS Low Resolution Analog-to-Digital Converter driver + * + * Copyright (c) 2012 DENX Software Engineering, GmbH. + * Copyright (c) 2017 Ksenija Stanojevic + * + * Authors: + * Marek Vasut + * Ksenija Stanojevic + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ADC_CELL 0 +#define TSC_CELL 1 +#define RES_MEM 0 + +enum mx23_lradc_irqs { + MX23_LRADC_TS_IRQ = 0, + MX23_LRADC_CH0_IRQ, + MX23_LRADC_CH1_IRQ, + MX23_LRADC_CH2_IRQ, + MX23_LRADC_CH3_IRQ, + MX23_LRADC_CH4_IRQ, + MX23_LRADC_CH5_IRQ, + MX23_LRADC_CH6_IRQ, + MX23_LRADC_CH7_IRQ, +}; + +enum mx28_lradc_irqs { + MX28_LRADC_TS_IRQ = 0, + MX28_LRADC_TRESH0_IRQ, + MX28_LRADC_TRESH1_IRQ, + MX28_LRADC_CH0_IRQ, + MX28_LRADC_CH1_IRQ, + MX28_LRADC_CH2_IRQ, + MX28_LRADC_CH3_IRQ, + MX28_LRADC_CH4_IRQ, + MX28_LRADC_CH5_IRQ, + MX28_LRADC_CH6_IRQ, + MX28_LRADC_CH7_IRQ, + MX28_LRADC_BUTTON0_IRQ, + MX28_LRADC_BUTTON1_IRQ, +}; + +static struct resource mx23_adc_resources[] = { + DEFINE_RES_MEM(0x0, 0x0), + DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH0_IRQ, "mxs-lradc-channel0"), + DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH1_IRQ, "mxs-lradc-channel1"), + DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH2_IRQ, "mxs-lradc-channel2"), + DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH3_IRQ, "mxs-lradc-channel3"), + DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH4_IRQ, "mxs-lradc-channel4"), + DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH5_IRQ, "mxs-lradc-channel5"), +}; + +static struct resource mx23_touchscreen_resources[] = { + DEFINE_RES_MEM(0x0, 0x0), + DEFINE_RES_IRQ_NAMED(MX23_LRADC_TS_IRQ, "mxs-lradc-touchscreen"), + DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH6_IRQ, "mxs-lradc-channel6"), + DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH7_IRQ, "mxs-lradc-channel7"), +}; + +static struct resource mx28_adc_resources[] = { + DEFINE_RES_MEM(0x0, 0x0), + DEFINE_RES_IRQ_NAMED(MX28_LRADC_TRESH0_IRQ, "mxs-lradc-thresh0"), + DEFINE_RES_IRQ_NAMED(MX28_LRADC_TRESH1_IRQ, "mxs-lradc-thresh1"), + DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH0_IRQ, "mxs-lradc-channel0"), + DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH1_IRQ, "mxs-lradc-channel1"), + DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH2_IRQ, "mxs-lradc-channel2"), + DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH3_IRQ, "mxs-lradc-channel3"), + DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH4_IRQ, "mxs-lradc-channel4"), + DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH5_IRQ, "mxs-lradc-channel5"), + DEFINE_RES_IRQ_NAMED(MX28_LRADC_BUTTON0_IRQ, "mxs-lradc-button0"), + DEFINE_RES_IRQ_NAMED(MX28_LRADC_BUTTON1_IRQ, "mxs-lradc-button1"), +}; + +static struct resource mx28_touchscreen_resources[] = { + DEFINE_RES_MEM(0x0, 0x0), + DEFINE_RES_IRQ_NAMED(MX28_LRADC_TS_IRQ, "mxs-lradc-touchscreen"), + DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH6_IRQ, "mxs-lradc-channel6"), + DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH7_IRQ, "mxs-lradc-channel7"), +}; + +static struct mfd_cell mx23_cells[] = { + { + .name = "mxs-lradc-adc", + .resources = mx23_adc_resources, + .num_resources = ARRAY_SIZE(mx23_adc_resources), + }, + { + .name = "mxs-lradc-ts", + .resources = mx23_touchscreen_resources, + .num_resources = ARRAY_SIZE(mx23_touchscreen_resources), + }, +}; + +static struct mfd_cell mx28_cells[] = { + { + .name = "mxs-lradc-adc", + .resources = mx28_adc_resources, + .num_resources = ARRAY_SIZE(mx28_adc_resources), + }, + { + .name = "mxs-lradc-ts", + .resources = mx28_touchscreen_resources, + .num_resources = ARRAY_SIZE(mx28_touchscreen_resources), + } +}; + +static const struct of_device_id mxs_lradc_dt_ids[] = { + { .compatible = "fsl,imx23-lradc", .data = (void *)IMX23_LRADC, }, + { .compatible = "fsl,imx28-lradc", .data = (void *)IMX28_LRADC, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids); + +static int mxs_lradc_probe(struct platform_device *pdev) +{ + const struct of_device_id *of_id; + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct mxs_lradc *lradc; + struct mfd_cell *cells = NULL; + struct resource *res; + int ret = 0; + u32 ts_wires = 0; + + lradc = devm_kzalloc(&pdev->dev, sizeof(*lradc), GFP_KERNEL); + if (!lradc) + return -ENOMEM; + + of_id = of_match_device(mxs_lradc_dt_ids, &pdev->dev); + if (!of_id) + return -EINVAL; + + lradc->soc = (enum mxs_lradc_id)of_id->data; + + lradc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(lradc->clk)) { + dev_err(dev, "Failed to get the delay unit clock\n"); + return PTR_ERR(lradc->clk); + } + + ret = clk_prepare_enable(lradc->clk); + if (ret) { + dev_err(dev, "Failed to enable the delay unit clock\n"); + return ret; + } + + ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires", + &ts_wires); + + if (!ret) { + lradc->buffer_vchans = BUFFER_VCHANS_LIMITED; + + switch (ts_wires) { + case 4: + lradc->touchscreen_wire = MXS_LRADC_TOUCHSCREEN_4WIRE; + break; + case 5: + if (lradc->soc == IMX28_LRADC) { + lradc->touchscreen_wire = + MXS_LRADC_TOUCHSCREEN_5WIRE; + break; + } + /* fall through to an error message for i.MX23 */ + default: + dev_err(&pdev->dev, + "Unsupported number of touchscreen wires (%d)\n" + , ts_wires); + ret = -EINVAL; + goto err_clk; + } + } else { + lradc->buffer_vchans = BUFFER_VCHANS_ALL; + } + + platform_set_drvdata(pdev, lradc); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOMEM; + + switch (lradc->soc) { + case IMX23_LRADC: + mx23_adc_resources[RES_MEM] = *res; + mx23_touchscreen_resources[RES_MEM] = *res; + cells = mx23_cells; + break; + case IMX28_LRADC: + mx28_adc_resources[RES_MEM] = *res; + mx28_touchscreen_resources[RES_MEM] = *res; + cells = mx28_cells; + break; + default: + dev_err(dev, "Unsupported SoC\n"); + ret = -ENODEV; + goto err_clk; + } + + ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, + &cells[ADC_CELL], 1, NULL, 0, NULL); + if (ret) { + dev_err(&pdev->dev, "Failed to add the ADC subdevice\n"); + goto err_clk; + } + + if (!lradc->touchscreen_wire) + return 0; + + ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, + &cells[TSC_CELL], 1, NULL, 0, NULL); + if (ret) { + dev_err(&pdev->dev, + "Failed to add the touchscreen subdevice\n"); + goto err_clk; + } + + return 0; + +err_clk: + clk_disable_unprepare(lradc->clk); + + return ret; +} + +static int mxs_lradc_remove(struct platform_device *pdev) +{ + struct mxs_lradc *lradc = platform_get_drvdata(pdev); + + clk_disable_unprepare(lradc->clk); + + return 0; +} + +static struct platform_driver mxs_lradc_driver = { + .driver = { + .name = "mxs-lradc", + .of_match_table = mxs_lradc_dt_ids, + }, + .probe = mxs_lradc_probe, + .remove = mxs_lradc_remove, +}; +module_platform_driver(mxs_lradc_driver); + +MODULE_AUTHOR("Ksenija Stanojevic "); +MODULE_DESCRIPTION("Freescale i.MX23/i.MX28 LRADC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:mxs-lradc"); diff --git a/include/linux/mfd/mxs-lradc.h b/include/linux/mfd/mxs-lradc.h new file mode 100644 index 000000000000..661a4521f723 --- /dev/null +++ b/include/linux/mfd/mxs-lradc.h @@ -0,0 +1,187 @@ +/* + * Freescale MXS Low Resolution Analog-to-Digital Converter driver + * + * Copyright (c) 2012 DENX Software Engineering, GmbH. + * Copyright (c) 2016 Ksenija Stanojevic + * + * Author: Marek Vasut + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MFD_MXS_LRADC_H +#define __MFD_MXS_LRADC_H + +#include +#include +#include + +#define LRADC_MAX_DELAY_CHANS 4 +#define LRADC_MAX_MAPPED_CHANS 8 +#define LRADC_MAX_TOTAL_CHANS 16 + +#define LRADC_DELAY_TIMER_HZ 2000 + +#define LRADC_CTRL0 0x00 +# define LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE BIT(23) +# define LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE BIT(22) +# define LRADC_CTRL0_MX28_YNNSW /* YM */ BIT(21) +# define LRADC_CTRL0_MX28_YPNSW /* YP */ BIT(20) +# define LRADC_CTRL0_MX28_YPPSW /* YP */ BIT(19) +# define LRADC_CTRL0_MX28_XNNSW /* XM */ BIT(18) +# define LRADC_CTRL0_MX28_XNPSW /* XM */ BIT(17) +# define LRADC_CTRL0_MX28_XPPSW /* XP */ BIT(16) + +# define LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE BIT(20) +# define LRADC_CTRL0_MX23_YM BIT(19) +# define LRADC_CTRL0_MX23_XM BIT(18) +# define LRADC_CTRL0_MX23_YP BIT(17) +# define LRADC_CTRL0_MX23_XP BIT(16) + +# define LRADC_CTRL0_MX28_PLATE_MASK \ + (LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE | \ + LRADC_CTRL0_MX28_YNNSW | LRADC_CTRL0_MX28_YPNSW | \ + LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW | \ + LRADC_CTRL0_MX28_XNPSW | LRADC_CTRL0_MX28_XPPSW) + +# define LRADC_CTRL0_MX23_PLATE_MASK \ + (LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE | \ + LRADC_CTRL0_MX23_YM | LRADC_CTRL0_MX23_XM | \ + LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XP) + +#define LRADC_CTRL1 0x10 +#define LRADC_CTRL1_TOUCH_DETECT_IRQ_EN BIT(24) +#define LRADC_CTRL1_LRADC_IRQ_EN(n) (1 << ((n) + 16)) +#define LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK (0x1fff << 16) +#define LRADC_CTRL1_MX23_LRADC_IRQ_EN_MASK (0x01ff << 16) +#define LRADC_CTRL1_LRADC_IRQ_EN_OFFSET 16 +#define LRADC_CTRL1_TOUCH_DETECT_IRQ BIT(8) +#define LRADC_CTRL1_LRADC_IRQ(n) BIT(n) +#define LRADC_CTRL1_MX28_LRADC_IRQ_MASK 0x1fff +#define LRADC_CTRL1_MX23_LRADC_IRQ_MASK 0x01ff +#define LRADC_CTRL1_LRADC_IRQ_OFFSET 0 + +#define LRADC_CTRL2 0x20 +#define LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET 24 +#define LRADC_CTRL2_TEMPSENSE_PWD BIT(15) + +#define LRADC_STATUS 0x40 +#define LRADC_STATUS_TOUCH_DETECT_RAW BIT(0) + +#define LRADC_CH(n) (0x50 + (0x10 * (n))) +#define LRADC_CH_ACCUMULATE BIT(29) +#define LRADC_CH_NUM_SAMPLES_MASK (0x1f << 24) +#define LRADC_CH_NUM_SAMPLES_OFFSET 24 +#define LRADC_CH_NUM_SAMPLES(x) \ + ((x) << LRADC_CH_NUM_SAMPLES_OFFSET) +#define LRADC_CH_VALUE_MASK 0x3ffff +#define LRADC_CH_VALUE_OFFSET 0 + +#define LRADC_DELAY(n) (0xd0 + (0x10 * (n))) +#define LRADC_DELAY_TRIGGER_LRADCS_MASK (0xffUL << 24) +#define LRADC_DELAY_TRIGGER_LRADCS_OFFSET 24 +#define LRADC_DELAY_TRIGGER(x) \ + (((x) << LRADC_DELAY_TRIGGER_LRADCS_OFFSET) & \ + LRADC_DELAY_TRIGGER_LRADCS_MASK) +#define LRADC_DELAY_KICK BIT(20) +#define LRADC_DELAY_TRIGGER_DELAYS_MASK (0xf << 16) +#define LRADC_DELAY_TRIGGER_DELAYS_OFFSET 16 +#define LRADC_DELAY_TRIGGER_DELAYS(x) \ + (((x) << LRADC_DELAY_TRIGGER_DELAYS_OFFSET) & \ + LRADC_DELAY_TRIGGER_DELAYS_MASK) +#define LRADC_DELAY_LOOP_COUNT_MASK (0x1f << 11) +#define LRADC_DELAY_LOOP_COUNT_OFFSET 11 +#define LRADC_DELAY_LOOP(x) \ + (((x) << LRADC_DELAY_LOOP_COUNT_OFFSET) & \ + LRADC_DELAY_LOOP_COUNT_MASK) +#define LRADC_DELAY_DELAY_MASK 0x7ff +#define LRADC_DELAY_DELAY_OFFSET 0 +#define LRADC_DELAY_DELAY(x) \ + (((x) << LRADC_DELAY_DELAY_OFFSET) & \ + LRADC_DELAY_DELAY_MASK) + +#define LRADC_CTRL4 0x140 +#define LRADC_CTRL4_LRADCSELECT_MASK(n) (0xf << ((n) * 4)) +#define LRADC_CTRL4_LRADCSELECT_OFFSET(n) ((n) * 4) +#define LRADC_CTRL4_LRADCSELECT(n, x) \ + (((x) << LRADC_CTRL4_LRADCSELECT_OFFSET(n)) & \ + LRADC_CTRL4_LRADCSELECT_MASK(n)) + +#define LRADC_RESOLUTION 12 +#define LRADC_SINGLE_SAMPLE_MASK ((1 << LRADC_RESOLUTION) - 1) + +#define BUFFER_VCHANS_LIMITED 0x3f +#define BUFFER_VCHANS_ALL 0xff + + /* + * Certain LRADC channels are shared between touchscreen + * and/or touch-buttons and generic LRADC block. Therefore when using + * either of these, these channels are not available for the regular + * sampling. The shared channels are as follows: + * + * CH0 -- Touch button #0 + * CH1 -- Touch button #1 + * CH2 -- Touch screen XPUL + * CH3 -- Touch screen YPLL + * CH4 -- Touch screen XNUL + * CH5 -- Touch screen YNLR + * CH6 -- Touch screen WIPER (5-wire only) + * + * The bit fields below represents which parts of the LRADC block are + * switched into special mode of operation. These channels can not + * be sampled as regular LRADC channels. The driver will refuse any + * attempt to sample these channels. + */ +#define CHAN_MASK_TOUCHBUTTON (BIT(1) | BIT(0)) +#define CHAN_MASK_TOUCHSCREEN_4WIRE (0xf << 2) +#define CHAN_MASK_TOUCHSCREEN_5WIRE (0x1f << 2) + +enum mxs_lradc_id { + IMX23_LRADC, + IMX28_LRADC, +}; + +enum mxs_lradc_ts_wires { + MXS_LRADC_TOUCHSCREEN_NONE = 0, + MXS_LRADC_TOUCHSCREEN_4WIRE, + MXS_LRADC_TOUCHSCREEN_5WIRE, +}; + +/** + * struct mxs_lradc + * @soc: soc type (IMX23 or IMX28) + * @clk: 2 kHz clock for delay units + * @buffer_vchans: channels that can be used during buffered capture + * @touchscreen_wire: touchscreen type (4-wire or 5-wire) + * @use_touchbutton: button state (on or off) + */ +struct mxs_lradc { + enum mxs_lradc_id soc; + struct clk *clk; + u8 buffer_vchans; + + enum mxs_lradc_ts_wires touchscreen_wire; + bool use_touchbutton; +}; + +static inline u32 mxs_lradc_irq_mask(struct mxs_lradc *lradc) +{ + switch (lradc->soc) { + case IMX23_LRADC: + return LRADC_CTRL1_MX23_LRADC_IRQ_MASK; + case IMX28_LRADC: + return LRADC_CTRL1_MX28_LRADC_IRQ_MASK; + default: + return 0; + } +} + +#endif /* __MXS_LRADC_H */ -- cgit v1.2.3-59-g8ed1b From 6dd112b9f85e5f24ac8c15d892690cb44a4b7936 Mon Sep 17 00:00:00 2001 From: Ksenija Stanojevic Date: Thu, 16 Mar 2017 13:27:10 +0100 Subject: iio: adc: mxs-lradc: Add support for ADC driver Add support for sixteen-channel 12-bit resolution ADC and its functions, which include general-purpose ADC readings, battery voltage measurement, and die temperature measurement. Signed-off-by: Ksenija Stanojevic Reviewed-by: Jonathan Cameron Reviewed-by: Marek Vasut Signed-off-by: Lee Jones --- drivers/iio/adc/Kconfig | 13 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/mxs-lradc-adc.c | 843 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 857 insertions(+) create mode 100644 drivers/iio/adc/mxs-lradc-adc.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index dedae7adbce9..97a3803f0b34 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -229,6 +229,19 @@ config EXYNOS_ADC To compile this driver as a module, choose M here: the module will be called exynos_adc. +config MXS_LRADC_ADC + tristate "Freescale i.MX23/i.MX28 LRADC ADC" + depends on MFD_MXS_LRADC + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for the ADC functions of the + i.MX23/i.MX28 LRADC. This includes general-purpose ADC readings, + battery voltage measurement, and die temperature measurement. + + This driver can also be built as a module. If so, the module will be + called mxs-lradc-adc. + config FSL_MX25_ADC tristate "Freescale MX25 ADC driver" depends on MFD_MX25_TSADC diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index d0012620cd1c..38f0e153b481 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_MCP3422) += mcp3422.o obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o obj-$(CONFIG_MESON_SARADC) += meson_saradc.o +obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o obj-$(CONFIG_NAU7802) += nau7802.o obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o diff --git a/drivers/iio/adc/mxs-lradc-adc.c b/drivers/iio/adc/mxs-lradc-adc.c new file mode 100644 index 000000000000..b0c7d8ee5cb8 --- /dev/null +++ b/drivers/iio/adc/mxs-lradc-adc.c @@ -0,0 +1,843 @@ +/* + * Freescale MXS LRADC ADC driver + * + * Copyright (c) 2012 DENX Software Engineering, GmbH. + * Copyright (c) 2017 Ksenija Stanojevic + * + * Authors: + * Marek Vasut + * Ksenija Stanojevic + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * Make this runtime configurable if necessary. Currently, if the buffered mode + * is enabled, the LRADC takes LRADC_DELAY_TIMER_LOOP samples of data before + * triggering IRQ. The sampling happens every (LRADC_DELAY_TIMER_PER / 2000) + * seconds. The result is that the samples arrive every 500mS. + */ +#define LRADC_DELAY_TIMER_PER 200 +#define LRADC_DELAY_TIMER_LOOP 5 + +#define VREF_MV_BASE 1850 + +const char *mx23_lradc_adc_irq_names[] = { + "mxs-lradc-channel0", + "mxs-lradc-channel1", + "mxs-lradc-channel2", + "mxs-lradc-channel3", + "mxs-lradc-channel4", + "mxs-lradc-channel5", +}; + +const char *mx28_lradc_adc_irq_names[] = { + "mxs-lradc-thresh0", + "mxs-lradc-thresh1", + "mxs-lradc-channel0", + "mxs-lradc-channel1", + "mxs-lradc-channel2", + "mxs-lradc-channel3", + "mxs-lradc-channel4", + "mxs-lradc-channel5", + "mxs-lradc-button0", + "mxs-lradc-button1", +}; + +static const u32 mxs_lradc_adc_vref_mv[][LRADC_MAX_TOTAL_CHANS] = { + [IMX23_LRADC] = { + VREF_MV_BASE, /* CH0 */ + VREF_MV_BASE, /* CH1 */ + VREF_MV_BASE, /* CH2 */ + VREF_MV_BASE, /* CH3 */ + VREF_MV_BASE, /* CH4 */ + VREF_MV_BASE, /* CH5 */ + VREF_MV_BASE * 2, /* CH6 VDDIO */ + VREF_MV_BASE * 4, /* CH7 VBATT */ + VREF_MV_BASE, /* CH8 Temp sense 0 */ + VREF_MV_BASE, /* CH9 Temp sense 1 */ + VREF_MV_BASE, /* CH10 */ + VREF_MV_BASE, /* CH11 */ + VREF_MV_BASE, /* CH12 USB_DP */ + VREF_MV_BASE, /* CH13 USB_DN */ + VREF_MV_BASE, /* CH14 VBG */ + VREF_MV_BASE * 4, /* CH15 VDD5V */ + }, + [IMX28_LRADC] = { + VREF_MV_BASE, /* CH0 */ + VREF_MV_BASE, /* CH1 */ + VREF_MV_BASE, /* CH2 */ + VREF_MV_BASE, /* CH3 */ + VREF_MV_BASE, /* CH4 */ + VREF_MV_BASE, /* CH5 */ + VREF_MV_BASE, /* CH6 */ + VREF_MV_BASE * 4, /* CH7 VBATT */ + VREF_MV_BASE, /* CH8 Temp sense 0 */ + VREF_MV_BASE, /* CH9 Temp sense 1 */ + VREF_MV_BASE * 2, /* CH10 VDDIO */ + VREF_MV_BASE, /* CH11 VTH */ + VREF_MV_BASE * 2, /* CH12 VDDA */ + VREF_MV_BASE, /* CH13 VDDD */ + VREF_MV_BASE, /* CH14 VBG */ + VREF_MV_BASE * 4, /* CH15 VDD5V */ + }, +}; + +enum mxs_lradc_divbytwo { + MXS_LRADC_DIV_DISABLED = 0, + MXS_LRADC_DIV_ENABLED, +}; + +struct mxs_lradc_scale { + unsigned int integer; + unsigned int nano; +}; + +struct mxs_lradc_adc { + struct mxs_lradc *lradc; + struct device *dev; + + void __iomem *base; + u32 buffer[10]; + struct iio_trigger *trig; + struct completion completion; + spinlock_t lock; + + const u32 *vref_mv; + struct mxs_lradc_scale scale_avail[LRADC_MAX_TOTAL_CHANS][2]; + unsigned long is_divided; +}; + + +/* Raw I/O operations */ +static int mxs_lradc_adc_read_single(struct iio_dev *iio_dev, int chan, + int *val) +{ + struct mxs_lradc_adc *adc = iio_priv(iio_dev); + struct mxs_lradc *lradc = adc->lradc; + int ret; + + /* + * See if there is no buffered operation in progress. If there is simply + * bail out. This can be improved to support both buffered and raw IO at + * the same time, yet the code becomes horribly complicated. Therefore I + * applied KISS principle here. + */ + ret = iio_device_claim_direct_mode(iio_dev); + if (ret) + return ret; + + reinit_completion(&adc->completion); + + /* + * No buffered operation in progress, map the channel and trigger it. + * Virtual channel 0 is always used here as the others are always not + * used if doing raw sampling. + */ + if (lradc->soc == IMX28_LRADC) + writel(LRADC_CTRL1_LRADC_IRQ_EN(0), + adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + writel(0x1, adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR); + + /* Enable / disable the divider per requirement */ + if (test_bit(chan, &adc->is_divided)) + writel(1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET, + adc->base + LRADC_CTRL2 + STMP_OFFSET_REG_SET); + else + writel(1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET, + adc->base + LRADC_CTRL2 + STMP_OFFSET_REG_CLR); + + /* Clean the slot's previous content, then set new one. */ + writel(LRADC_CTRL4_LRADCSELECT_MASK(0), + adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR); + writel(chan, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET); + + writel(0, adc->base + LRADC_CH(0)); + + /* Enable the IRQ and start sampling the channel. */ + writel(LRADC_CTRL1_LRADC_IRQ_EN(0), + adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET); + writel(BIT(0), adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET); + + /* Wait for completion on the channel, 1 second max. */ + ret = wait_for_completion_killable_timeout(&adc->completion, HZ); + if (!ret) + ret = -ETIMEDOUT; + if (ret < 0) + goto err; + + /* Read the data. */ + *val = readl(adc->base + LRADC_CH(0)) & LRADC_CH_VALUE_MASK; + ret = IIO_VAL_INT; + +err: + writel(LRADC_CTRL1_LRADC_IRQ_EN(0), + adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + + iio_device_release_direct_mode(iio_dev); + + return ret; +} + +static int mxs_lradc_adc_read_temp(struct iio_dev *iio_dev, int *val) +{ + int ret, min, max; + + ret = mxs_lradc_adc_read_single(iio_dev, 8, &min); + if (ret != IIO_VAL_INT) + return ret; + + ret = mxs_lradc_adc_read_single(iio_dev, 9, &max); + if (ret != IIO_VAL_INT) + return ret; + + *val = max - min; + + return IIO_VAL_INT; +} + +static int mxs_lradc_adc_read_raw(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long m) +{ + struct mxs_lradc_adc *adc = iio_priv(iio_dev); + + switch (m) { + case IIO_CHAN_INFO_RAW: + if (chan->type == IIO_TEMP) + return mxs_lradc_adc_read_temp(iio_dev, val); + + return mxs_lradc_adc_read_single(iio_dev, chan->channel, val); + + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_TEMP) { + /* + * From the datasheet, we have to multiply by 1.012 and + * divide by 4 + */ + *val = 0; + *val2 = 253000; + return IIO_VAL_INT_PLUS_MICRO; + } + + *val = adc->vref_mv[chan->channel]; + *val2 = chan->scan_type.realbits - + test_bit(chan->channel, &adc->is_divided); + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_OFFSET: + if (chan->type == IIO_TEMP) { + /* + * The calculated value from the ADC is in Kelvin, we + * want Celsius for hwmon so the offset is -273.15 + * The offset is applied before scaling so it is + * actually -213.15 * 4 / 1.012 = -1079.644268 + */ + *val = -1079; + *val2 = 644268; + + return IIO_VAL_INT_PLUS_MICRO; + } + + return -EINVAL; + + default: + break; + } + + return -EINVAL; +} + +static int mxs_lradc_adc_write_raw(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + int val, int val2, long m) +{ + struct mxs_lradc_adc *adc = iio_priv(iio_dev); + struct mxs_lradc_scale *scale_avail = + adc->scale_avail[chan->channel]; + int ret; + + ret = iio_device_claim_direct_mode(iio_dev); + if (ret) + return ret; + + switch (m) { + case IIO_CHAN_INFO_SCALE: + ret = -EINVAL; + if (val == scale_avail[MXS_LRADC_DIV_DISABLED].integer && + val2 == scale_avail[MXS_LRADC_DIV_DISABLED].nano) { + /* divider by two disabled */ + clear_bit(chan->channel, &adc->is_divided); + ret = 0; + } else if (val == scale_avail[MXS_LRADC_DIV_ENABLED].integer && + val2 == scale_avail[MXS_LRADC_DIV_ENABLED].nano) { + /* divider by two enabled */ + set_bit(chan->channel, &adc->is_divided); + ret = 0; + } + + break; + default: + ret = -EINVAL; + break; + } + + iio_device_release_direct_mode(iio_dev); + + return ret; +} + +static int mxs_lradc_adc_write_raw_get_fmt(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + long m) +{ + return IIO_VAL_INT_PLUS_NANO; +} + +static ssize_t mxs_lradc_adc_show_scale_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *iio = dev_to_iio_dev(dev); + struct mxs_lradc_adc *adc = iio_priv(iio); + struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr); + int i, ch, len = 0; + + ch = iio_attr->address; + for (i = 0; i < ARRAY_SIZE(adc->scale_avail[ch]); i++) + len += sprintf(buf + len, "%u.%09u ", + adc->scale_avail[ch][i].integer, + adc->scale_avail[ch][i].nano); + + len += sprintf(buf + len, "\n"); + + return len; +} + +#define SHOW_SCALE_AVAILABLE_ATTR(ch)\ + IIO_DEVICE_ATTR(in_voltage##ch##_scale_available, 0444,\ + mxs_lradc_adc_show_scale_avail, NULL, ch) + +SHOW_SCALE_AVAILABLE_ATTR(0); +SHOW_SCALE_AVAILABLE_ATTR(1); +SHOW_SCALE_AVAILABLE_ATTR(2); +SHOW_SCALE_AVAILABLE_ATTR(3); +SHOW_SCALE_AVAILABLE_ATTR(4); +SHOW_SCALE_AVAILABLE_ATTR(5); +SHOW_SCALE_AVAILABLE_ATTR(6); +SHOW_SCALE_AVAILABLE_ATTR(7); +SHOW_SCALE_AVAILABLE_ATTR(10); +SHOW_SCALE_AVAILABLE_ATTR(11); +SHOW_SCALE_AVAILABLE_ATTR(12); +SHOW_SCALE_AVAILABLE_ATTR(13); +SHOW_SCALE_AVAILABLE_ATTR(14); +SHOW_SCALE_AVAILABLE_ATTR(15); + +static struct attribute *mxs_lradc_adc_attributes[] = { + &iio_dev_attr_in_voltage0_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage1_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage2_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage3_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage4_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage5_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage6_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage7_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage10_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage11_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage12_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage13_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage14_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage15_scale_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group mxs_lradc_adc_attribute_group = { + .attrs = mxs_lradc_adc_attributes, +}; + +static const struct iio_info mxs_lradc_adc_iio_info = { + .driver_module = THIS_MODULE, + .read_raw = mxs_lradc_adc_read_raw, + .write_raw = mxs_lradc_adc_write_raw, + .write_raw_get_fmt = mxs_lradc_adc_write_raw_get_fmt, + .attrs = &mxs_lradc_adc_attribute_group, +}; + +/* IRQ Handling */ +static irqreturn_t mxs_lradc_adc_handle_irq(int irq, void *data) +{ + struct iio_dev *iio = data; + struct mxs_lradc_adc *adc = iio_priv(iio); + struct mxs_lradc *lradc = adc->lradc; + unsigned long reg = readl(adc->base + LRADC_CTRL1); + unsigned long flags; + + if (!(reg & mxs_lradc_irq_mask(lradc))) + return IRQ_NONE; + + if (iio_buffer_enabled(iio)) { + if (reg & lradc->buffer_vchans) { + spin_lock_irqsave(&adc->lock, flags); + iio_trigger_poll(iio->trig); + spin_unlock_irqrestore(&adc->lock, flags); + } + } else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) { + complete(&adc->completion); + } + + writel(reg & mxs_lradc_irq_mask(lradc), + adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + + return IRQ_HANDLED; +} + + +/* Trigger handling */ +static irqreturn_t mxs_lradc_adc_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *iio = pf->indio_dev; + struct mxs_lradc_adc *adc = iio_priv(iio); + const u32 chan_value = LRADC_CH_ACCUMULATE | + ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET); + unsigned int i, j = 0; + + for_each_set_bit(i, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) { + adc->buffer[j] = readl(adc->base + LRADC_CH(j)); + writel(chan_value, adc->base + LRADC_CH(j)); + adc->buffer[j] &= LRADC_CH_VALUE_MASK; + adc->buffer[j] /= LRADC_DELAY_TIMER_LOOP; + j++; + } + + iio_push_to_buffers_with_timestamp(iio, adc->buffer, pf->timestamp); + + iio_trigger_notify_done(iio->trig); + + return IRQ_HANDLED; +} + +static int mxs_lradc_adc_configure_trigger(struct iio_trigger *trig, bool state) +{ + struct iio_dev *iio = iio_trigger_get_drvdata(trig); + struct mxs_lradc_adc *adc = iio_priv(iio); + const u32 st = state ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR; + + writel(LRADC_DELAY_KICK, adc->base + (LRADC_DELAY(0) + st)); + + return 0; +} + +static const struct iio_trigger_ops mxs_lradc_adc_trigger_ops = { + .owner = THIS_MODULE, + .set_trigger_state = &mxs_lradc_adc_configure_trigger, +}; + +static int mxs_lradc_adc_trigger_init(struct iio_dev *iio) +{ + int ret; + struct iio_trigger *trig; + struct mxs_lradc_adc *adc = iio_priv(iio); + + trig = devm_iio_trigger_alloc(&iio->dev, "%s-dev%i", iio->name, + iio->id); + + trig->dev.parent = adc->dev; + iio_trigger_set_drvdata(trig, iio); + trig->ops = &mxs_lradc_adc_trigger_ops; + + ret = iio_trigger_register(trig); + if (ret) + return ret; + + adc->trig = trig; + + return 0; +} + +static void mxs_lradc_adc_trigger_remove(struct iio_dev *iio) +{ + struct mxs_lradc_adc *adc = iio_priv(iio); + + iio_trigger_unregister(adc->trig); +} + +static int mxs_lradc_adc_buffer_preenable(struct iio_dev *iio) +{ + struct mxs_lradc_adc *adc = iio_priv(iio); + struct mxs_lradc *lradc = adc->lradc; + int chan, ofs = 0; + unsigned long enable = 0; + u32 ctrl4_set = 0; + u32 ctrl4_clr = 0; + u32 ctrl1_irq = 0; + const u32 chan_value = LRADC_CH_ACCUMULATE | + ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET); + + if (lradc->soc == IMX28_LRADC) + writel(lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET, + adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + writel(lradc->buffer_vchans, + adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR); + + for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) { + ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs); + ctrl4_clr |= LRADC_CTRL4_LRADCSELECT_MASK(ofs); + ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs); + writel(chan_value, adc->base + LRADC_CH(ofs)); + bitmap_set(&enable, ofs, 1); + ofs++; + } + + writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK, + adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR); + writel(ctrl4_clr, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR); + writel(ctrl4_set, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET); + writel(ctrl1_irq, adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET); + writel(enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET, + adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_SET); + + return 0; +} + +static int mxs_lradc_adc_buffer_postdisable(struct iio_dev *iio) +{ + struct mxs_lradc_adc *adc = iio_priv(iio); + struct mxs_lradc *lradc = adc->lradc; + + writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK, + adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR); + + writel(lradc->buffer_vchans, + adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR); + if (lradc->soc == IMX28_LRADC) + writel(lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET, + adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + + return 0; +} + +static bool mxs_lradc_adc_validate_scan_mask(struct iio_dev *iio, + const unsigned long *mask) +{ + struct mxs_lradc_adc *adc = iio_priv(iio); + struct mxs_lradc *lradc = adc->lradc; + const int map_chans = bitmap_weight(mask, LRADC_MAX_TOTAL_CHANS); + int rsvd_chans = 0; + unsigned long rsvd_mask = 0; + + if (lradc->use_touchbutton) + rsvd_mask |= CHAN_MASK_TOUCHBUTTON; + if (lradc->touchscreen_wire == MXS_LRADC_TOUCHSCREEN_4WIRE) + rsvd_mask |= CHAN_MASK_TOUCHSCREEN_4WIRE; + if (lradc->touchscreen_wire == MXS_LRADC_TOUCHSCREEN_5WIRE) + rsvd_mask |= CHAN_MASK_TOUCHSCREEN_5WIRE; + + if (lradc->use_touchbutton) + rsvd_chans++; + if (lradc->touchscreen_wire) + rsvd_chans += 2; + + /* Test for attempts to map channels with special mode of operation. */ + if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS)) + return false; + + /* Test for attempts to map more channels then available slots. */ + if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS) + return false; + + return true; +} + +static const struct iio_buffer_setup_ops mxs_lradc_adc_buffer_ops = { + .preenable = &mxs_lradc_adc_buffer_preenable, + .postenable = &iio_triggered_buffer_postenable, + .predisable = &iio_triggered_buffer_predisable, + .postdisable = &mxs_lradc_adc_buffer_postdisable, + .validate_scan_mask = &mxs_lradc_adc_validate_scan_mask, +}; + +/* Driver initialization */ +#define MXS_ADC_CHAN(idx, chan_type, name) { \ + .type = (chan_type), \ + .indexed = 1, \ + .scan_index = (idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .channel = (idx), \ + .address = (idx), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = LRADC_RESOLUTION, \ + .storagebits = 32, \ + }, \ + .datasheet_name = (name), \ +} + +static const struct iio_chan_spec mx23_lradc_chan_spec[] = { + MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"), + MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"), + MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"), + MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"), + MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"), + MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"), + MXS_ADC_CHAN(6, IIO_VOLTAGE, "VDDIO"), + MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"), + /* Combined Temperature sensors */ + { + .type = IIO_TEMP, + .indexed = 1, + .scan_index = 8, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE), + .channel = 8, + .scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,}, + .datasheet_name = "TEMP_DIE", + }, + /* Hidden channel to keep indexes */ + { + .type = IIO_TEMP, + .indexed = 1, + .scan_index = -1, + .channel = 9, + }, + MXS_ADC_CHAN(10, IIO_VOLTAGE, NULL), + MXS_ADC_CHAN(11, IIO_VOLTAGE, NULL), + MXS_ADC_CHAN(12, IIO_VOLTAGE, "USB_DP"), + MXS_ADC_CHAN(13, IIO_VOLTAGE, "USB_DN"), + MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"), + MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"), +}; + +static const struct iio_chan_spec mx28_lradc_chan_spec[] = { + MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"), + MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"), + MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"), + MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"), + MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"), + MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"), + MXS_ADC_CHAN(6, IIO_VOLTAGE, "LRADC6"), + MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"), + /* Combined Temperature sensors */ + { + .type = IIO_TEMP, + .indexed = 1, + .scan_index = 8, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE), + .channel = 8, + .scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,}, + .datasheet_name = "TEMP_DIE", + }, + /* Hidden channel to keep indexes */ + { + .type = IIO_TEMP, + .indexed = 1, + .scan_index = -1, + .channel = 9, + }, + MXS_ADC_CHAN(10, IIO_VOLTAGE, "VDDIO"), + MXS_ADC_CHAN(11, IIO_VOLTAGE, "VTH"), + MXS_ADC_CHAN(12, IIO_VOLTAGE, "VDDA"), + MXS_ADC_CHAN(13, IIO_VOLTAGE, "VDDD"), + MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"), + MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"), +}; + +static void mxs_lradc_adc_hw_init(struct mxs_lradc_adc *adc) +{ + /* The ADC always uses DELAY CHANNEL 0. */ + const u32 adc_cfg = + (1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + 0)) | + (LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET); + + /* Configure DELAY CHANNEL 0 for generic ADC sampling. */ + writel(adc_cfg, adc->base + LRADC_DELAY(0)); + + /* + * Start internal temperature sensing by clearing bit + * HW_LRADC_CTRL2_TEMPSENSE_PWD. This bit can be left cleared + * after power up. + */ + writel(0, adc->base + LRADC_CTRL2); +} + +static void mxs_lradc_adc_hw_stop(struct mxs_lradc_adc *adc) +{ + writel(0, adc->base + LRADC_DELAY(0)); +} + +static int mxs_lradc_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mxs_lradc *lradc = dev_get_drvdata(dev->parent); + struct mxs_lradc_adc *adc; + struct iio_dev *iio; + struct resource *iores; + int ret, irq, virq, i, s, n; + u64 scale_uv; + const char **irq_name; + + /* Allocate the IIO device. */ + iio = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!iio) { + dev_err(dev, "Failed to allocate IIO device\n"); + return -ENOMEM; + } + + adc = iio_priv(iio); + adc->lradc = lradc; + adc->dev = dev; + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + adc->base = devm_ioremap(dev, iores->start, resource_size(iores)); + if (IS_ERR(adc->base)) + return PTR_ERR(adc->base); + + init_completion(&adc->completion); + spin_lock_init(&adc->lock); + + platform_set_drvdata(pdev, iio); + + iio->name = pdev->name; + iio->dev.parent = dev; + iio->dev.of_node = dev->parent->of_node; + iio->info = &mxs_lradc_adc_iio_info; + iio->modes = INDIO_DIRECT_MODE; + iio->masklength = LRADC_MAX_TOTAL_CHANS; + + if (lradc->soc == IMX23_LRADC) { + iio->channels = mx23_lradc_chan_spec; + iio->num_channels = ARRAY_SIZE(mx23_lradc_chan_spec); + irq_name = mx23_lradc_adc_irq_names; + n = ARRAY_SIZE(mx23_lradc_adc_irq_names); + } else { + iio->channels = mx28_lradc_chan_spec; + iio->num_channels = ARRAY_SIZE(mx28_lradc_chan_spec); + irq_name = mx28_lradc_adc_irq_names; + n = ARRAY_SIZE(mx28_lradc_adc_irq_names); + } + + ret = stmp_reset_block(adc->base); + if (ret) + return ret; + + for (i = 0; i < n; i++) { + irq = platform_get_irq_byname(pdev, irq_name[i]); + if (irq < 0) + return irq; + + virq = irq_of_parse_and_map(dev->parent->of_node, irq); + + ret = devm_request_irq(dev, virq, mxs_lradc_adc_handle_irq, + 0, irq_name[i], iio); + if (ret) + return ret; + } + + ret = mxs_lradc_adc_trigger_init(iio); + if (ret) + goto err_trig; + + ret = iio_triggered_buffer_setup(iio, &iio_pollfunc_store_time, + &mxs_lradc_adc_trigger_handler, + &mxs_lradc_adc_buffer_ops); + if (ret) + return ret; + + adc->vref_mv = mxs_lradc_adc_vref_mv[lradc->soc]; + + /* Populate available ADC input ranges */ + for (i = 0; i < LRADC_MAX_TOTAL_CHANS; i++) { + for (s = 0; s < ARRAY_SIZE(adc->scale_avail[i]); s++) { + /* + * [s=0] = optional divider by two disabled (default) + * [s=1] = optional divider by two enabled + * + * The scale is calculated by doing: + * Vref >> (realbits - s) + * which multiplies by two on the second component + * of the array. + */ + scale_uv = ((u64)adc->vref_mv[i] * 100000000) >> + (LRADC_RESOLUTION - s); + adc->scale_avail[i][s].nano = + do_div(scale_uv, 100000000) * 10; + adc->scale_avail[i][s].integer = scale_uv; + } + } + + /* Configure the hardware. */ + mxs_lradc_adc_hw_init(adc); + + /* Register IIO device. */ + ret = iio_device_register(iio); + if (ret) { + dev_err(dev, "Failed to register IIO device\n"); + goto err_dev; + } + + return 0; + +err_dev: + mxs_lradc_adc_hw_stop(adc); + mxs_lradc_adc_trigger_remove(iio); +err_trig: + iio_triggered_buffer_cleanup(iio); + return ret; +} + +static int mxs_lradc_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *iio = platform_get_drvdata(pdev); + struct mxs_lradc_adc *adc = iio_priv(iio); + + iio_device_unregister(iio); + mxs_lradc_adc_hw_stop(adc); + mxs_lradc_adc_trigger_remove(iio); + iio_triggered_buffer_cleanup(iio); + + return 0; +} + +static struct platform_driver mxs_lradc_adc_driver = { + .driver = { + .name = "mxs-lradc-adc", + }, + .probe = mxs_lradc_adc_probe, + .remove = mxs_lradc_adc_remove, +}; +module_platform_driver(mxs_lradc_adc_driver); + +MODULE_AUTHOR("Marek Vasut "); +MODULE_DESCRIPTION("Freescale MXS LRADC driver general purpose ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:mxs-lradc-adc"); -- cgit v1.2.3-59-g8ed1b From d81ca730e3e40f4fd59ab3dfcad29aed6257944d Mon Sep 17 00:00:00 2001 From: Ksenija Stanojevic Date: Thu, 16 Mar 2017 13:27:11 +0100 Subject: input: touchscreen: mxs-lradc: Add support for touchscreen Add 4-wire/5-wire touchscreen controller. Signed-off-by: Ksenija Stanojevic Acked-by: Dmitry Torokhov Reviewed-by: Marek Vasut Signed-off-by: Lee Jones --- drivers/input/touchscreen/Kconfig | 10 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/mxs-lradc-ts.c | 714 +++++++++++++++++++++++++++++++ 3 files changed, 725 insertions(+) create mode 100644 drivers/input/touchscreen/mxs-lradc-ts.c diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 033599777651..52458cfdf017 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -829,6 +829,16 @@ config TOUCHSCREEN_USB_COMPOSITE To compile this driver as a module, choose M here: the module will be called usbtouchscreen. +config TOUCHSCREEN_MXS_LRADC + tristate "Freescale i.MX23/i.MX28 LRADC touchscreen" + depends on MFD_MXS_LRADC + help + Say Y here if you have a touchscreen connected to the low-resolution + analog-to-digital converter (LRADC) on an i.MX23 or i.MX28 processor. + + To compile this driver as a module, choose M here: the module will be + called mxs-lradc-ts. + config TOUCHSCREEN_MX25 tristate "Freescale i.MX25 touchscreen input driver" depends on MFD_MX25_TSADC diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index b622e5344137..96761ce2ee6d 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o obj-$(CONFIG_TOUCHSCREEN_IPROC) += bcm_iproc_tsc.o obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o +obj-$(CONFIG_TOUCHSCREEN_MXS_LRADC) += mxs-lradc-ts.o obj-$(CONFIG_TOUCHSCREEN_MX25) += fsl-imx25-tcq.o obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o diff --git a/drivers/input/touchscreen/mxs-lradc-ts.c b/drivers/input/touchscreen/mxs-lradc-ts.c new file mode 100644 index 000000000000..4b4aebfe3e7f --- /dev/null +++ b/drivers/input/touchscreen/mxs-lradc-ts.c @@ -0,0 +1,714 @@ +/* + * Freescale MXS LRADC touchscreen driver + * + * Copyright (c) 2012 DENX Software Engineering, GmbH. + * Copyright (c) 2017 Ksenija Stanojevic + * + * Authors: + * Marek Vasut + * Ksenija Stanojevic + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const char *mxs_lradc_ts_irq_names[] = { + "mxs-lradc-touchscreen", + "mxs-lradc-channel6", + "mxs-lradc-channel7", +}; + +/* + * Touchscreen handling + */ +enum mxs_lradc_ts_plate { + LRADC_TOUCH = 0, + LRADC_SAMPLE_X, + LRADC_SAMPLE_Y, + LRADC_SAMPLE_PRESSURE, + LRADC_SAMPLE_VALID, +}; + +struct mxs_lradc_ts { + struct mxs_lradc *lradc; + struct device *dev; + + void __iomem *base; + /* + * When the touchscreen is enabled, we give it two private virtual + * channels: #6 and #7. This means that only 6 virtual channels (instead + * of 8) will be available for buffered capture. + */ +#define TOUCHSCREEN_VCHANNEL1 7 +#define TOUCHSCREEN_VCHANNEL2 6 + + struct input_dev *ts_input; + + enum mxs_lradc_ts_plate cur_plate; /* state machine */ + bool ts_valid; + unsigned int ts_x_pos; + unsigned int ts_y_pos; + unsigned int ts_pressure; + + /* handle touchscreen's physical behaviour */ + /* samples per coordinate */ + unsigned int over_sample_cnt; + /* time clocks between samples */ + unsigned int over_sample_delay; + /* time in clocks to wait after the plates where switched */ + unsigned int settling_delay; + spinlock_t lock; +}; + +struct state_info { + u32 mask; + u32 bit; + u32 x_plate; + u32 y_plate; + u32 pressure; +}; + +static struct state_info info[] = { + {LRADC_CTRL0_MX23_PLATE_MASK, LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE, + LRADC_CTRL0_MX23_XP | LRADC_CTRL0_MX23_XM, + LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_YM, + LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XM}, + {LRADC_CTRL0_MX28_PLATE_MASK, LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE, + LRADC_CTRL0_MX28_XPPSW | LRADC_CTRL0_MX28_XNNSW, + LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_YNNSW, + LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW} +}; + +static bool mxs_lradc_check_touch_event(struct mxs_lradc_ts *ts) +{ + return !!(readl(ts->base + LRADC_STATUS) & + LRADC_STATUS_TOUCH_DETECT_RAW); +} + +static void mxs_lradc_map_ts_channel(struct mxs_lradc_ts *ts, unsigned int vch, + unsigned int ch) +{ + writel(LRADC_CTRL4_LRADCSELECT_MASK(vch), + ts->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR); + writel(LRADC_CTRL4_LRADCSELECT(vch, ch), + ts->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET); +} + +static void mxs_lradc_setup_ts_channel(struct mxs_lradc_ts *ts, unsigned int ch) +{ + /* + * prepare for oversampling conversion + * + * from the datasheet: + * "The ACCUMULATE bit in the appropriate channel register + * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0; + * otherwise, the IRQs will not fire." + */ + writel(LRADC_CH_ACCUMULATE | + LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1), + ts->base + LRADC_CH(ch)); + + /* from the datasheet: + * "Software must clear this register in preparation for a + * multi-cycle accumulation. + */ + writel(LRADC_CH_VALUE_MASK, + ts->base + LRADC_CH(ch) + STMP_OFFSET_REG_CLR); + + /* + * prepare the delay/loop unit according to the oversampling count + * + * from the datasheet: + * "The DELAY fields in HW_LRADC_DELAY0, HW_LRADC_DELAY1, + * HW_LRADC_DELAY2, and HW_LRADC_DELAY3 must be non-zero; otherwise, + * the LRADC will not trigger the delay group." + */ + writel(LRADC_DELAY_TRIGGER(1 << ch) | LRADC_DELAY_TRIGGER_DELAYS(0) | + LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) | + LRADC_DELAY_DELAY(ts->over_sample_delay - 1), + ts->base + LRADC_DELAY(3)); + + writel(LRADC_CTRL1_LRADC_IRQ(ch), + ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + + /* + * after changing the touchscreen plates setting + * the signals need some initial time to settle. Start the + * SoC's delay unit and start the conversion later + * and automatically. + */ + writel(LRADC_DELAY_TRIGGER(0) | LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | + LRADC_DELAY_KICK | LRADC_DELAY_DELAY(ts->settling_delay), + ts->base + LRADC_DELAY(2)); +} + +/* + * Pressure detection is special: + * We want to do both required measurements for the pressure detection in + * one turn. Use the hardware features to chain both conversions and let the + * hardware report one interrupt if both conversions are done + */ +static void mxs_lradc_setup_ts_pressure(struct mxs_lradc_ts *ts, + unsigned int ch1, unsigned int ch2) +{ + u32 reg; + + /* + * prepare for oversampling conversion + * + * from the datasheet: + * "The ACCUMULATE bit in the appropriate channel register + * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0; + * otherwise, the IRQs will not fire." + */ + reg = LRADC_CH_ACCUMULATE | + LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1); + writel(reg, ts->base + LRADC_CH(ch1)); + writel(reg, ts->base + LRADC_CH(ch2)); + + /* from the datasheet: + * "Software must clear this register in preparation for a + * multi-cycle accumulation. + */ + writel(LRADC_CH_VALUE_MASK, + ts->base + LRADC_CH(ch1) + STMP_OFFSET_REG_CLR); + writel(LRADC_CH_VALUE_MASK, + ts->base + LRADC_CH(ch2) + STMP_OFFSET_REG_CLR); + + /* prepare the delay/loop unit according to the oversampling count */ + writel(LRADC_DELAY_TRIGGER(1 << ch1) | LRADC_DELAY_TRIGGER(1 << ch2) | + LRADC_DELAY_TRIGGER_DELAYS(0) | + LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) | + LRADC_DELAY_DELAY(ts->over_sample_delay - 1), + ts->base + LRADC_DELAY(3)); + + writel(LRADC_CTRL1_LRADC_IRQ(ch2), + ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + + /* + * after changing the touchscreen plates setting + * the signals need some initial time to settle. Start the + * SoC's delay unit and start the conversion later + * and automatically. + */ + writel(LRADC_DELAY_TRIGGER(0) | LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | + LRADC_DELAY_KICK | LRADC_DELAY_DELAY(ts->settling_delay), + ts->base + LRADC_DELAY(2)); +} + +static unsigned int mxs_lradc_ts_read_raw_channel(struct mxs_lradc_ts *ts, + unsigned int channel) +{ + u32 reg; + unsigned int num_samples, val; + + reg = readl(ts->base + LRADC_CH(channel)); + if (reg & LRADC_CH_ACCUMULATE) + num_samples = ts->over_sample_cnt; + else + num_samples = 1; + + val = (reg & LRADC_CH_VALUE_MASK) >> LRADC_CH_VALUE_OFFSET; + return val / num_samples; +} + +static unsigned int mxs_lradc_read_ts_pressure(struct mxs_lradc_ts *ts, + unsigned int ch1, unsigned int ch2) +{ + u32 reg, mask; + unsigned int pressure, m1, m2; + + mask = LRADC_CTRL1_LRADC_IRQ(ch1) | LRADC_CTRL1_LRADC_IRQ(ch2); + reg = readl(ts->base + LRADC_CTRL1) & mask; + + while (reg != mask) { + reg = readl(ts->base + LRADC_CTRL1) & mask; + dev_dbg(ts->dev, "One channel is still busy: %X\n", reg); + } + + m1 = mxs_lradc_ts_read_raw_channel(ts, ch1); + m2 = mxs_lradc_ts_read_raw_channel(ts, ch2); + + if (m2 == 0) { + dev_warn(ts->dev, "Cannot calculate pressure\n"); + return 1 << (LRADC_RESOLUTION - 1); + } + + /* simply scale the value from 0 ... max ADC resolution */ + pressure = m1; + pressure *= (1 << LRADC_RESOLUTION); + pressure /= m2; + + dev_dbg(ts->dev, "Pressure = %u\n", pressure); + return pressure; +} + +#define TS_CH_XP 2 +#define TS_CH_YP 3 +#define TS_CH_XM 4 +#define TS_CH_YM 5 + +/* + * YP(open)--+-------------+ + * | |--+ + * | | | + * YM(-)--+-------------+ | + * +--------------+ + * | | + * XP(weak+) XM(open) + * + * "weak+" means 200k Ohm VDDIO + * (-) means GND + */ +static void mxs_lradc_setup_touch_detection(struct mxs_lradc_ts *ts) +{ + struct mxs_lradc *lradc = ts->lradc; + + /* + * In order to detect a touch event the 'touch detect enable' bit + * enables: + * - a weak pullup to the X+ connector + * - a strong ground at the Y- connector + */ + writel(info[lradc->soc].mask, + ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR); + writel(info[lradc->soc].bit, + ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET); +} + +/* + * YP(meas)--+-------------+ + * | |--+ + * | | | + * YM(open)--+-------------+ | + * +--------------+ + * | | + * XP(+) XM(-) + * + * (+) means here 1.85 V + * (-) means here GND + */ +static void mxs_lradc_prepare_x_pos(struct mxs_lradc_ts *ts) +{ + struct mxs_lradc *lradc = ts->lradc; + + writel(info[lradc->soc].mask, + ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR); + writel(info[lradc->soc].x_plate, + ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET); + + ts->cur_plate = LRADC_SAMPLE_X; + mxs_lradc_map_ts_channel(ts, TOUCHSCREEN_VCHANNEL1, TS_CH_YP); + mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1); +} + +/* + * YP(+)--+-------------+ + * | |--+ + * | | | + * YM(-)--+-------------+ | + * +--------------+ + * | | + * XP(open) XM(meas) + * + * (+) means here 1.85 V + * (-) means here GND + */ +static void mxs_lradc_prepare_y_pos(struct mxs_lradc_ts *ts) +{ + struct mxs_lradc *lradc = ts->lradc; + + writel(info[lradc->soc].mask, + ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR); + writel(info[lradc->soc].y_plate, + ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET); + + ts->cur_plate = LRADC_SAMPLE_Y; + mxs_lradc_map_ts_channel(ts, TOUCHSCREEN_VCHANNEL1, TS_CH_XM); + mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1); +} + +/* + * YP(+)--+-------------+ + * | |--+ + * | | | + * YM(meas)--+-------------+ | + * +--------------+ + * | | + * XP(meas) XM(-) + * + * (+) means here 1.85 V + * (-) means here GND + */ +static void mxs_lradc_prepare_pressure(struct mxs_lradc_ts *ts) +{ + struct mxs_lradc *lradc = ts->lradc; + + writel(info[lradc->soc].mask, + ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR); + writel(info[lradc->soc].pressure, + ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET); + + ts->cur_plate = LRADC_SAMPLE_PRESSURE; + mxs_lradc_map_ts_channel(ts, TOUCHSCREEN_VCHANNEL1, TS_CH_YM); + mxs_lradc_map_ts_channel(ts, TOUCHSCREEN_VCHANNEL2, TS_CH_XP); + mxs_lradc_setup_ts_pressure(ts, TOUCHSCREEN_VCHANNEL2, + TOUCHSCREEN_VCHANNEL1); +} + +static void mxs_lradc_enable_touch_detection(struct mxs_lradc_ts *ts) +{ + mxs_lradc_setup_touch_detection(ts); + + ts->cur_plate = LRADC_TOUCH; + writel(LRADC_CTRL1_TOUCH_DETECT_IRQ | LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, + ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, + ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET); +} + +static void mxs_lradc_start_touch_event(struct mxs_lradc_ts *ts) +{ + writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, + ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + writel(LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1), + ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET); + /* + * start with the Y-pos, because it uses nearly the same plate + * settings like the touch detection + */ + mxs_lradc_prepare_y_pos(ts); +} + +static void mxs_lradc_report_ts_event(struct mxs_lradc_ts *ts) +{ + input_report_abs(ts->ts_input, ABS_X, ts->ts_x_pos); + input_report_abs(ts->ts_input, ABS_Y, ts->ts_y_pos); + input_report_abs(ts->ts_input, ABS_PRESSURE, ts->ts_pressure); + input_report_key(ts->ts_input, BTN_TOUCH, 1); + input_sync(ts->ts_input); +} + +static void mxs_lradc_complete_touch_event(struct mxs_lradc_ts *ts) +{ + mxs_lradc_setup_touch_detection(ts); + ts->cur_plate = LRADC_SAMPLE_VALID; + /* + * start a dummy conversion to burn time to settle the signals + * note: we are not interested in the conversion's value + */ + writel(0, ts->base + LRADC_CH(TOUCHSCREEN_VCHANNEL1)); + writel(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) | + LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2), + ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + writel(LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) | + LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), + ts->base + LRADC_DELAY(2)); +} + +/* + * in order to avoid false measurements, report only samples where + * the surface is still touched after the position measurement + */ +static void mxs_lradc_finish_touch_event(struct mxs_lradc_ts *ts, bool valid) +{ + /* if it is still touched, report the sample */ + if (valid && mxs_lradc_check_touch_event(ts)) { + ts->ts_valid = true; + mxs_lradc_report_ts_event(ts); + } + + /* if it is even still touched, continue with the next measurement */ + if (mxs_lradc_check_touch_event(ts)) { + mxs_lradc_prepare_y_pos(ts); + return; + } + + if (ts->ts_valid) { + /* signal the release */ + ts->ts_valid = false; + input_report_key(ts->ts_input, BTN_TOUCH, 0); + input_sync(ts->ts_input); + } + + /* if it is released, wait for the next touch via IRQ */ + ts->cur_plate = LRADC_TOUCH; + writel(0, ts->base + LRADC_DELAY(2)); + writel(0, ts->base + LRADC_DELAY(3)); + writel(LRADC_CTRL1_TOUCH_DETECT_IRQ | + LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) | + LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1), + ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, + ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET); +} + +/* touchscreen's state machine */ +static void mxs_lradc_handle_touch(struct mxs_lradc_ts *ts) +{ + switch (ts->cur_plate) { + case LRADC_TOUCH: + if (mxs_lradc_check_touch_event(ts)) + mxs_lradc_start_touch_event(ts); + writel(LRADC_CTRL1_TOUCH_DETECT_IRQ, + ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + return; + + case LRADC_SAMPLE_Y: + ts->ts_y_pos = + mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1); + mxs_lradc_prepare_x_pos(ts); + return; + + case LRADC_SAMPLE_X: + ts->ts_x_pos = + mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1); + mxs_lradc_prepare_pressure(ts); + return; + + case LRADC_SAMPLE_PRESSURE: + ts->ts_pressure = + mxs_lradc_read_ts_pressure(ts, + TOUCHSCREEN_VCHANNEL2, + TOUCHSCREEN_VCHANNEL1); + mxs_lradc_complete_touch_event(ts); + return; + + case LRADC_SAMPLE_VALID: + mxs_lradc_finish_touch_event(ts, 1); + break; + } +} + +/* IRQ Handling */ +static irqreturn_t mxs_lradc_ts_handle_irq(int irq, void *data) +{ + struct mxs_lradc_ts *ts = data; + struct mxs_lradc *lradc = ts->lradc; + unsigned long reg = readl(ts->base + LRADC_CTRL1); + u32 clr_irq = mxs_lradc_irq_mask(lradc); + const u32 ts_irq_mask = + LRADC_CTRL1_TOUCH_DETECT_IRQ | + LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) | + LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2); + unsigned long flags; + + if (!(reg & mxs_lradc_irq_mask(lradc))) + return IRQ_NONE; + + if (reg & ts_irq_mask) { + spin_lock_irqsave(&ts->lock, flags); + mxs_lradc_handle_touch(ts); + spin_unlock_irqrestore(&ts->lock, flags); + /* Make sure we don't clear the next conversion's interrupt. */ + clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) | + LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2)); + writel(reg & clr_irq, + ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + } + + return IRQ_HANDLED; +} + +static int mxs_lradc_ts_open(struct input_dev *dev) +{ + struct mxs_lradc_ts *ts = input_get_drvdata(dev); + + /* Enable the touch-detect circuitry. */ + mxs_lradc_enable_touch_detection(ts); + + return 0; +} + +static void mxs_lradc_ts_stop(struct mxs_lradc_ts *ts) +{ + int i; + struct mxs_lradc *lradc = ts->lradc; + + /* stop all interrupts from firing */ + writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN | + LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) | + LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2), + ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + + /* Power-down touchscreen touch-detect circuitry. */ + writel(info[lradc->soc].mask, + ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR); + + writel(lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET, + ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + + for (i = 1; i < LRADC_MAX_DELAY_CHANS; i++) + writel(0, ts->base + LRADC_DELAY(i)); +} + +static void mxs_lradc_ts_close(struct input_dev *dev) +{ + struct mxs_lradc_ts *ts = input_get_drvdata(dev); + + mxs_lradc_ts_stop(ts); +} + +static void mxs_lradc_ts_hw_init(struct mxs_lradc_ts *ts) +{ + struct mxs_lradc *lradc = ts->lradc; + + /* Configure the touchscreen type */ + if (lradc->soc == IMX28_LRADC) { + writel(LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE, + ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR); + + if (lradc->touchscreen_wire == MXS_LRADC_TOUCHSCREEN_5WIRE) + writel(LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE, + ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET); + } +} + +static int mxs_lradc_ts_register(struct mxs_lradc_ts *ts) +{ + struct input_dev *input = ts->ts_input; + struct device *dev = ts->dev; + + input = devm_input_allocate_device(dev); + if (!input) + return -ENOMEM; + + input->name = "mxs-lradc-ts"; + input->id.bustype = BUS_HOST; + input->open = mxs_lradc_ts_open; + input->close = mxs_lradc_ts_close; + + __set_bit(INPUT_PROP_DIRECT, input->propbit); + input_set_capability(input, EV_KEY, BTN_TOUCH); + input_set_abs_params(input, ABS_X, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0); + input_set_abs_params(input, ABS_Y, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0); + input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_SINGLE_SAMPLE_MASK, + 0, 0); + + ts->ts_input = input; + input_set_drvdata(input, ts); + + return input_register_device(input); +} + +static int mxs_lradc_ts_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->parent->of_node; + struct mxs_lradc *lradc = dev_get_drvdata(dev->parent); + struct mxs_lradc_ts *ts; + struct resource *iores; + int ret, irq, virq, i; + u32 ts_wires = 0, adapt; + + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + platform_set_drvdata(pdev, ts); + + ts->lradc = lradc; + ts->dev = dev; + spin_lock_init(&ts->lock); + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ts->base = devm_ioremap(dev, iores->start, resource_size(iores)); + if (IS_ERR(ts->base)) + return PTR_ERR(ts->base); + + ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires", + &ts_wires); + if (ret) + return ret; + + if (of_property_read_u32(node, "fsl,ave-ctrl", &adapt)) { + ts->over_sample_cnt = 4; + } else { + if (adapt >= 1 || adapt <= 32) { + ts->over_sample_cnt = adapt; + } else { + dev_err(ts->dev, "Invalid sample count (%u)\n", + adapt); + return -EINVAL; + } + } + + if (of_property_read_u32(node, "fsl,ave-delay", &adapt)) { + ts->over_sample_delay = 2; + } else { + if (adapt >= 2 || adapt <= LRADC_DELAY_DELAY_MASK + 1) { + ts->over_sample_delay = adapt; + } else { + dev_err(ts->dev, "Invalid sample delay (%u)\n", + adapt); + return -EINVAL; + } + } + + if (of_property_read_u32(node, "fsl,settling", &adapt)) { + ts->settling_delay = 10; + } else { + if (adapt >= 1 || adapt <= LRADC_DELAY_DELAY_MASK) { + ts->settling_delay = adapt; + } else { + dev_err(ts->dev, "Invalid settling delay (%u)\n", + adapt); + return -EINVAL; + } + } + + ret = stmp_reset_block(ts->base); + if (ret) + return ret; + + mxs_lradc_ts_hw_init(ts); + + for (i = 0; i < 3; i++) { + irq = platform_get_irq_byname(pdev, mxs_lradc_ts_irq_names[i]); + if (irq < 0) + return irq; + + virq = irq_of_parse_and_map(node, irq); + + mxs_lradc_ts_stop(ts); + + ret = devm_request_irq(dev, virq, + mxs_lradc_ts_handle_irq, + 0, mxs_lradc_ts_irq_names[i], ts); + if (ret) + return ret; + } + + return mxs_lradc_ts_register(ts); +} + +static struct platform_driver mxs_lradc_ts_driver = { + .driver = { + .name = "mxs-lradc-ts", + }, + .probe = mxs_lradc_ts_probe, +}; +module_platform_driver(mxs_lradc_ts_driver); + +MODULE_AUTHOR("Marek Vasut "); +MODULE_DESCRIPTION("Freescale MXS LRADC touchscreen driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:mxs-lradc-ts"); -- cgit v1.2.3-59-g8ed1b From 0d690cc41b164d420fc8696b86f4f259e105f65a Mon Sep 17 00:00:00 2001 From: Ksenija Stanojevic Date: Thu, 16 Mar 2017 13:27:12 +0100 Subject: iio: adc: mxs-lradc: Remove driver Since the driver has been split into MFD there is no reason for it to stay, so remove it. Signed-off-by: Ksenija Stanojevic Acked-by: Jonathan Cameron Reviewed-by: Marek Vasut Signed-off-by: Lee Jones --- drivers/iio/adc/Kconfig | 14 - drivers/iio/adc/Makefile | 1 - drivers/iio/adc/mxs-lradc.c | 1750 ------------------------------------------- 3 files changed, 1765 deletions(-) delete mode 100644 drivers/iio/adc/mxs-lradc.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 97a3803f0b34..5d134053a1bc 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -424,20 +424,6 @@ config MESON_SARADC To compile this driver as a module, choose M here: the module will be called meson_saradc. -config MXS_LRADC - tristate "Freescale i.MX23/i.MX28 LRADC" - depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM - depends on INPUT - select STMP_DEVICE - select IIO_BUFFER - select IIO_TRIGGERED_BUFFER - help - Say yes here to build support for i.MX23/i.MX28 LRADC convertor - built into these chips. - - To compile this driver as a module, choose M here: the - module will be called mxs-lradc. - config NAU7802 tristate "Nuvoton NAU7802 ADC driver" depends on I2C diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 38f0e153b481..54b5f7259063 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -40,7 +40,6 @@ obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o obj-$(CONFIG_MESON_SARADC) += meson_saradc.o obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o -obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o obj-$(CONFIG_NAU7802) += nau7802.o obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o diff --git a/drivers/iio/adc/mxs-lradc.c b/drivers/iio/adc/mxs-lradc.c deleted file mode 100644 index b84d37c80a94..000000000000 --- a/drivers/iio/adc/mxs-lradc.c +++ /dev/null @@ -1,1750 +0,0 @@ -/* - * Freescale MXS LRADC driver - * - * Copyright (c) 2012 DENX Software Engineering, GmbH. - * Marek Vasut - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#define DRIVER_NAME "mxs-lradc" - -#define LRADC_MAX_DELAY_CHANS 4 -#define LRADC_MAX_MAPPED_CHANS 8 -#define LRADC_MAX_TOTAL_CHANS 16 - -#define LRADC_DELAY_TIMER_HZ 2000 - -/* - * Make this runtime configurable if necessary. Currently, if the buffered mode - * is enabled, the LRADC takes LRADC_DELAY_TIMER_LOOP samples of data before - * triggering IRQ. The sampling happens every (LRADC_DELAY_TIMER_PER / 2000) - * seconds. The result is that the samples arrive every 500mS. - */ -#define LRADC_DELAY_TIMER_PER 200 -#define LRADC_DELAY_TIMER_LOOP 5 - -/* - * Once the pen touches the touchscreen, the touchscreen switches from - * IRQ-driven mode to polling mode to prevent interrupt storm. The polling - * is realized by worker thread, which is called every 20 or so milliseconds. - * This gives the touchscreen enough fluency and does not strain the system - * too much. - */ -#define LRADC_TS_SAMPLE_DELAY_MS 5 - -/* - * The LRADC reads the following amount of samples from each touchscreen - * channel and the driver then computes average of these. - */ -#define LRADC_TS_SAMPLE_AMOUNT 4 - -enum mxs_lradc_id { - IMX23_LRADC, - IMX28_LRADC, -}; - -static const char * const mx23_lradc_irq_names[] = { - "mxs-lradc-touchscreen", - "mxs-lradc-channel0", - "mxs-lradc-channel1", - "mxs-lradc-channel2", - "mxs-lradc-channel3", - "mxs-lradc-channel4", - "mxs-lradc-channel5", - "mxs-lradc-channel6", - "mxs-lradc-channel7", -}; - -static const char * const mx28_lradc_irq_names[] = { - "mxs-lradc-touchscreen", - "mxs-lradc-thresh0", - "mxs-lradc-thresh1", - "mxs-lradc-channel0", - "mxs-lradc-channel1", - "mxs-lradc-channel2", - "mxs-lradc-channel3", - "mxs-lradc-channel4", - "mxs-lradc-channel5", - "mxs-lradc-channel6", - "mxs-lradc-channel7", - "mxs-lradc-button0", - "mxs-lradc-button1", -}; - -struct mxs_lradc_of_config { - const int irq_count; - const char * const *irq_name; - const u32 *vref_mv; -}; - -#define VREF_MV_BASE 1850 - -static const u32 mx23_vref_mv[LRADC_MAX_TOTAL_CHANS] = { - VREF_MV_BASE, /* CH0 */ - VREF_MV_BASE, /* CH1 */ - VREF_MV_BASE, /* CH2 */ - VREF_MV_BASE, /* CH3 */ - VREF_MV_BASE, /* CH4 */ - VREF_MV_BASE, /* CH5 */ - VREF_MV_BASE * 2, /* CH6 VDDIO */ - VREF_MV_BASE * 4, /* CH7 VBATT */ - VREF_MV_BASE, /* CH8 Temp sense 0 */ - VREF_MV_BASE, /* CH9 Temp sense 1 */ - VREF_MV_BASE, /* CH10 */ - VREF_MV_BASE, /* CH11 */ - VREF_MV_BASE, /* CH12 USB_DP */ - VREF_MV_BASE, /* CH13 USB_DN */ - VREF_MV_BASE, /* CH14 VBG */ - VREF_MV_BASE * 4, /* CH15 VDD5V */ -}; - -static const u32 mx28_vref_mv[LRADC_MAX_TOTAL_CHANS] = { - VREF_MV_BASE, /* CH0 */ - VREF_MV_BASE, /* CH1 */ - VREF_MV_BASE, /* CH2 */ - VREF_MV_BASE, /* CH3 */ - VREF_MV_BASE, /* CH4 */ - VREF_MV_BASE, /* CH5 */ - VREF_MV_BASE, /* CH6 */ - VREF_MV_BASE * 4, /* CH7 VBATT */ - VREF_MV_BASE, /* CH8 Temp sense 0 */ - VREF_MV_BASE, /* CH9 Temp sense 1 */ - VREF_MV_BASE * 2, /* CH10 VDDIO */ - VREF_MV_BASE, /* CH11 VTH */ - VREF_MV_BASE * 2, /* CH12 VDDA */ - VREF_MV_BASE, /* CH13 VDDD */ - VREF_MV_BASE, /* CH14 VBG */ - VREF_MV_BASE * 4, /* CH15 VDD5V */ -}; - -static const struct mxs_lradc_of_config mxs_lradc_of_config[] = { - [IMX23_LRADC] = { - .irq_count = ARRAY_SIZE(mx23_lradc_irq_names), - .irq_name = mx23_lradc_irq_names, - .vref_mv = mx23_vref_mv, - }, - [IMX28_LRADC] = { - .irq_count = ARRAY_SIZE(mx28_lradc_irq_names), - .irq_name = mx28_lradc_irq_names, - .vref_mv = mx28_vref_mv, - }, -}; - -enum mxs_lradc_ts { - MXS_LRADC_TOUCHSCREEN_NONE = 0, - MXS_LRADC_TOUCHSCREEN_4WIRE, - MXS_LRADC_TOUCHSCREEN_5WIRE, -}; - -/* - * Touchscreen handling - */ -enum lradc_ts_plate { - LRADC_TOUCH = 0, - LRADC_SAMPLE_X, - LRADC_SAMPLE_Y, - LRADC_SAMPLE_PRESSURE, - LRADC_SAMPLE_VALID, -}; - -enum mxs_lradc_divbytwo { - MXS_LRADC_DIV_DISABLED = 0, - MXS_LRADC_DIV_ENABLED, -}; - -struct mxs_lradc_scale { - unsigned int integer; - unsigned int nano; -}; - -struct mxs_lradc { - struct device *dev; - void __iomem *base; - int irq[13]; - - struct clk *clk; - - u32 *buffer; - struct iio_trigger *trig; - - struct mutex lock; - - struct completion completion; - - const u32 *vref_mv; - struct mxs_lradc_scale scale_avail[LRADC_MAX_TOTAL_CHANS][2]; - unsigned long is_divided; - - /* - * When the touchscreen is enabled, we give it two private virtual - * channels: #6 and #7. This means that only 6 virtual channels (instead - * of 8) will be available for buffered capture. - */ -#define TOUCHSCREEN_VCHANNEL1 7 -#define TOUCHSCREEN_VCHANNEL2 6 -#define BUFFER_VCHANS_LIMITED 0x3f -#define BUFFER_VCHANS_ALL 0xff - u8 buffer_vchans; - - /* - * Furthermore, certain LRADC channels are shared between touchscreen - * and/or touch-buttons and generic LRADC block. Therefore when using - * either of these, these channels are not available for the regular - * sampling. The shared channels are as follows: - * - * CH0 -- Touch button #0 - * CH1 -- Touch button #1 - * CH2 -- Touch screen XPUL - * CH3 -- Touch screen YPLL - * CH4 -- Touch screen XNUL - * CH5 -- Touch screen YNLR - * CH6 -- Touch screen WIPER (5-wire only) - * - * The bit fields below represents which parts of the LRADC block are - * switched into special mode of operation. These channels can not - * be sampled as regular LRADC channels. The driver will refuse any - * attempt to sample these channels. - */ -#define CHAN_MASK_TOUCHBUTTON (BIT(1) | BIT(0)) -#define CHAN_MASK_TOUCHSCREEN_4WIRE (0xf << 2) -#define CHAN_MASK_TOUCHSCREEN_5WIRE (0x1f << 2) - enum mxs_lradc_ts use_touchscreen; - bool use_touchbutton; - - struct input_dev *ts_input; - - enum mxs_lradc_id soc; - enum lradc_ts_plate cur_plate; /* state machine */ - bool ts_valid; - unsigned ts_x_pos; - unsigned ts_y_pos; - unsigned ts_pressure; - - /* handle touchscreen's physical behaviour */ - /* samples per coordinate */ - unsigned over_sample_cnt; - /* time clocks between samples */ - unsigned over_sample_delay; - /* time in clocks to wait after the plates where switched */ - unsigned settling_delay; -}; - -#define LRADC_CTRL0 0x00 -# define LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE BIT(23) -# define LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE BIT(22) -# define LRADC_CTRL0_MX28_YNNSW /* YM */ BIT(21) -# define LRADC_CTRL0_MX28_YPNSW /* YP */ BIT(20) -# define LRADC_CTRL0_MX28_YPPSW /* YP */ BIT(19) -# define LRADC_CTRL0_MX28_XNNSW /* XM */ BIT(18) -# define LRADC_CTRL0_MX28_XNPSW /* XM */ BIT(17) -# define LRADC_CTRL0_MX28_XPPSW /* XP */ BIT(16) - -# define LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE BIT(20) -# define LRADC_CTRL0_MX23_YM BIT(19) -# define LRADC_CTRL0_MX23_XM BIT(18) -# define LRADC_CTRL0_MX23_YP BIT(17) -# define LRADC_CTRL0_MX23_XP BIT(16) - -# define LRADC_CTRL0_MX28_PLATE_MASK \ - (LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE | \ - LRADC_CTRL0_MX28_YNNSW | LRADC_CTRL0_MX28_YPNSW | \ - LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW | \ - LRADC_CTRL0_MX28_XNPSW | LRADC_CTRL0_MX28_XPPSW) - -# define LRADC_CTRL0_MX23_PLATE_MASK \ - (LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE | \ - LRADC_CTRL0_MX23_YM | LRADC_CTRL0_MX23_XM | \ - LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XP) - -#define LRADC_CTRL1 0x10 -#define LRADC_CTRL1_TOUCH_DETECT_IRQ_EN BIT(24) -#define LRADC_CTRL1_LRADC_IRQ_EN(n) (1 << ((n) + 16)) -#define LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK (0x1fff << 16) -#define LRADC_CTRL1_MX23_LRADC_IRQ_EN_MASK (0x01ff << 16) -#define LRADC_CTRL1_LRADC_IRQ_EN_OFFSET 16 -#define LRADC_CTRL1_TOUCH_DETECT_IRQ BIT(8) -#define LRADC_CTRL1_LRADC_IRQ(n) (1 << (n)) -#define LRADC_CTRL1_MX28_LRADC_IRQ_MASK 0x1fff -#define LRADC_CTRL1_MX23_LRADC_IRQ_MASK 0x01ff -#define LRADC_CTRL1_LRADC_IRQ_OFFSET 0 - -#define LRADC_CTRL2 0x20 -#define LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET 24 -#define LRADC_CTRL2_TEMPSENSE_PWD BIT(15) - -#define LRADC_STATUS 0x40 -#define LRADC_STATUS_TOUCH_DETECT_RAW BIT(0) - -#define LRADC_CH(n) (0x50 + (0x10 * (n))) -#define LRADC_CH_ACCUMULATE BIT(29) -#define LRADC_CH_NUM_SAMPLES_MASK (0x1f << 24) -#define LRADC_CH_NUM_SAMPLES_OFFSET 24 -#define LRADC_CH_NUM_SAMPLES(x) \ - ((x) << LRADC_CH_NUM_SAMPLES_OFFSET) -#define LRADC_CH_VALUE_MASK 0x3ffff -#define LRADC_CH_VALUE_OFFSET 0 - -#define LRADC_DELAY(n) (0xd0 + (0x10 * (n))) -#define LRADC_DELAY_TRIGGER_LRADCS_MASK (0xffUL << 24) -#define LRADC_DELAY_TRIGGER_LRADCS_OFFSET 24 -#define LRADC_DELAY_TRIGGER(x) \ - (((x) << LRADC_DELAY_TRIGGER_LRADCS_OFFSET) & \ - LRADC_DELAY_TRIGGER_LRADCS_MASK) -#define LRADC_DELAY_KICK BIT(20) -#define LRADC_DELAY_TRIGGER_DELAYS_MASK (0xf << 16) -#define LRADC_DELAY_TRIGGER_DELAYS_OFFSET 16 -#define LRADC_DELAY_TRIGGER_DELAYS(x) \ - (((x) << LRADC_DELAY_TRIGGER_DELAYS_OFFSET) & \ - LRADC_DELAY_TRIGGER_DELAYS_MASK) -#define LRADC_DELAY_LOOP_COUNT_MASK (0x1f << 11) -#define LRADC_DELAY_LOOP_COUNT_OFFSET 11 -#define LRADC_DELAY_LOOP(x) \ - (((x) << LRADC_DELAY_LOOP_COUNT_OFFSET) & \ - LRADC_DELAY_LOOP_COUNT_MASK) -#define LRADC_DELAY_DELAY_MASK 0x7ff -#define LRADC_DELAY_DELAY_OFFSET 0 -#define LRADC_DELAY_DELAY(x) \ - (((x) << LRADC_DELAY_DELAY_OFFSET) & \ - LRADC_DELAY_DELAY_MASK) - -#define LRADC_CTRL4 0x140 -#define LRADC_CTRL4_LRADCSELECT_MASK(n) (0xf << ((n) * 4)) -#define LRADC_CTRL4_LRADCSELECT_OFFSET(n) ((n) * 4) -#define LRADC_CTRL4_LRADCSELECT(n, x) \ - (((x) << LRADC_CTRL4_LRADCSELECT_OFFSET(n)) & \ - LRADC_CTRL4_LRADCSELECT_MASK(n)) - -#define LRADC_RESOLUTION 12 -#define LRADC_SINGLE_SAMPLE_MASK ((1 << LRADC_RESOLUTION) - 1) - -static void mxs_lradc_reg_set(struct mxs_lradc *lradc, u32 val, u32 reg) -{ - writel(val, lradc->base + reg + STMP_OFFSET_REG_SET); -} - -static void mxs_lradc_reg_clear(struct mxs_lradc *lradc, u32 val, u32 reg) -{ - writel(val, lradc->base + reg + STMP_OFFSET_REG_CLR); -} - -static void mxs_lradc_reg_wrt(struct mxs_lradc *lradc, u32 val, u32 reg) -{ - writel(val, lradc->base + reg); -} - -static u32 mxs_lradc_plate_mask(struct mxs_lradc *lradc) -{ - if (lradc->soc == IMX23_LRADC) - return LRADC_CTRL0_MX23_PLATE_MASK; - return LRADC_CTRL0_MX28_PLATE_MASK; -} - -static u32 mxs_lradc_irq_mask(struct mxs_lradc *lradc) -{ - if (lradc->soc == IMX23_LRADC) - return LRADC_CTRL1_MX23_LRADC_IRQ_MASK; - return LRADC_CTRL1_MX28_LRADC_IRQ_MASK; -} - -static u32 mxs_lradc_touch_detect_bit(struct mxs_lradc *lradc) -{ - if (lradc->soc == IMX23_LRADC) - return LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE; - return LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE; -} - -static u32 mxs_lradc_drive_x_plate(struct mxs_lradc *lradc) -{ - if (lradc->soc == IMX23_LRADC) - return LRADC_CTRL0_MX23_XP | LRADC_CTRL0_MX23_XM; - return LRADC_CTRL0_MX28_XPPSW | LRADC_CTRL0_MX28_XNNSW; -} - -static u32 mxs_lradc_drive_y_plate(struct mxs_lradc *lradc) -{ - if (lradc->soc == IMX23_LRADC) - return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_YM; - return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_YNNSW; -} - -static u32 mxs_lradc_drive_pressure(struct mxs_lradc *lradc) -{ - if (lradc->soc == IMX23_LRADC) - return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XM; - return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW; -} - -static bool mxs_lradc_check_touch_event(struct mxs_lradc *lradc) -{ - return !!(readl(lradc->base + LRADC_STATUS) & - LRADC_STATUS_TOUCH_DETECT_RAW); -} - -static void mxs_lradc_map_channel(struct mxs_lradc *lradc, unsigned vch, - unsigned ch) -{ - mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(vch), - LRADC_CTRL4); - mxs_lradc_reg_set(lradc, LRADC_CTRL4_LRADCSELECT(vch, ch), LRADC_CTRL4); -} - -static void mxs_lradc_setup_ts_channel(struct mxs_lradc *lradc, unsigned ch) -{ - /* - * prepare for oversampling conversion - * - * from the datasheet: - * "The ACCUMULATE bit in the appropriate channel register - * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0; - * otherwise, the IRQs will not fire." - */ - mxs_lradc_reg_wrt(lradc, LRADC_CH_ACCUMULATE | - LRADC_CH_NUM_SAMPLES(lradc->over_sample_cnt - 1), - LRADC_CH(ch)); - - /* - * from the datasheet: - * "Software must clear this register in preparation for a - * multi-cycle accumulation. - */ - mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch)); - - /* - * prepare the delay/loop unit according to the oversampling count - * - * from the datasheet: - * "The DELAY fields in HW_LRADC_DELAY0, HW_LRADC_DELAY1, - * HW_LRADC_DELAY2, and HW_LRADC_DELAY3 must be non-zero; otherwise, - * the LRADC will not trigger the delay group." - */ - mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << ch) | - LRADC_DELAY_TRIGGER_DELAYS(0) | - LRADC_DELAY_LOOP(lradc->over_sample_cnt - 1) | - LRADC_DELAY_DELAY(lradc->over_sample_delay - 1), - LRADC_DELAY(3)); - - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch), LRADC_CTRL1); - - /* - * after changing the touchscreen plates setting - * the signals need some initial time to settle. Start the - * SoC's delay unit and start the conversion later - * and automatically. - */ - mxs_lradc_reg_wrt( - lradc, - LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */ - LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */ - LRADC_DELAY_KICK | - LRADC_DELAY_DELAY(lradc->settling_delay), - LRADC_DELAY(2)); -} - -/* - * Pressure detection is special: - * We want to do both required measurements for the pressure detection in - * one turn. Use the hardware features to chain both conversions and let the - * hardware report one interrupt if both conversions are done - */ -static void mxs_lradc_setup_ts_pressure(struct mxs_lradc *lradc, unsigned ch1, - unsigned ch2) -{ - u32 reg; - - /* - * prepare for oversampling conversion - * - * from the datasheet: - * "The ACCUMULATE bit in the appropriate channel register - * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0; - * otherwise, the IRQs will not fire." - */ - reg = LRADC_CH_ACCUMULATE | - LRADC_CH_NUM_SAMPLES(lradc->over_sample_cnt - 1); - mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch1)); - mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch2)); - - /* - * from the datasheet: - * "Software must clear this register in preparation for a - * multi-cycle accumulation. - */ - mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch1)); - mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch2)); - - /* prepare the delay/loop unit according to the oversampling count */ - mxs_lradc_reg_wrt( - lradc, - LRADC_DELAY_TRIGGER(1 << ch1) | - LRADC_DELAY_TRIGGER(1 << ch2) | /* start both channels */ - LRADC_DELAY_TRIGGER_DELAYS(0) | - LRADC_DELAY_LOOP(lradc->over_sample_cnt - 1) | - LRADC_DELAY_DELAY(lradc->over_sample_delay - 1), - LRADC_DELAY(3)); - - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch2), LRADC_CTRL1); - - /* - * after changing the touchscreen plates setting - * the signals need some initial time to settle. Start the - * SoC's delay unit and start the conversion later - * and automatically. - */ - mxs_lradc_reg_wrt( - lradc, - LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */ - LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */ - LRADC_DELAY_KICK | - LRADC_DELAY_DELAY(lradc->settling_delay), LRADC_DELAY(2)); -} - -static unsigned mxs_lradc_read_raw_channel(struct mxs_lradc *lradc, - unsigned channel) -{ - u32 reg; - unsigned num_samples, val; - - reg = readl(lradc->base + LRADC_CH(channel)); - if (reg & LRADC_CH_ACCUMULATE) - num_samples = lradc->over_sample_cnt; - else - num_samples = 1; - - val = (reg & LRADC_CH_VALUE_MASK) >> LRADC_CH_VALUE_OFFSET; - return val / num_samples; -} - -static unsigned mxs_lradc_read_ts_pressure(struct mxs_lradc *lradc, - unsigned ch1, unsigned ch2) -{ - u32 reg, mask; - unsigned pressure, m1, m2; - - mask = LRADC_CTRL1_LRADC_IRQ(ch1) | LRADC_CTRL1_LRADC_IRQ(ch2); - reg = readl(lradc->base + LRADC_CTRL1) & mask; - - while (reg != mask) { - reg = readl(lradc->base + LRADC_CTRL1) & mask; - dev_dbg(lradc->dev, "One channel is still busy: %X\n", reg); - } - - m1 = mxs_lradc_read_raw_channel(lradc, ch1); - m2 = mxs_lradc_read_raw_channel(lradc, ch2); - - if (m2 == 0) { - dev_warn(lradc->dev, "Cannot calculate pressure\n"); - return 1 << (LRADC_RESOLUTION - 1); - } - - /* simply scale the value from 0 ... max ADC resolution */ - pressure = m1; - pressure *= (1 << LRADC_RESOLUTION); - pressure /= m2; - - dev_dbg(lradc->dev, "Pressure = %u\n", pressure); - return pressure; -} - -#define TS_CH_XP 2 -#define TS_CH_YP 3 -#define TS_CH_XM 4 -#define TS_CH_YM 5 - -/* - * YP(open)--+-------------+ - * | |--+ - * | | | - * YM(-)--+-------------+ | - * +--------------+ - * | | - * XP(weak+) XM(open) - * - * "weak+" means 200k Ohm VDDIO - * (-) means GND - */ -static void mxs_lradc_setup_touch_detection(struct mxs_lradc *lradc) -{ - /* - * In order to detect a touch event the 'touch detect enable' bit - * enables: - * - a weak pullup to the X+ connector - * - a strong ground at the Y- connector - */ - mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0); - mxs_lradc_reg_set(lradc, mxs_lradc_touch_detect_bit(lradc), - LRADC_CTRL0); -} - -/* - * YP(meas)--+-------------+ - * | |--+ - * | | | - * YM(open)--+-------------+ | - * +--------------+ - * | | - * XP(+) XM(-) - * - * (+) means here 1.85 V - * (-) means here GND - */ -static void mxs_lradc_prepare_x_pos(struct mxs_lradc *lradc) -{ - mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0); - mxs_lradc_reg_set(lradc, mxs_lradc_drive_x_plate(lradc), LRADC_CTRL0); - - lradc->cur_plate = LRADC_SAMPLE_X; - mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YP); - mxs_lradc_setup_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1); -} - -/* - * YP(+)--+-------------+ - * | |--+ - * | | | - * YM(-)--+-------------+ | - * +--------------+ - * | | - * XP(open) XM(meas) - * - * (+) means here 1.85 V - * (-) means here GND - */ -static void mxs_lradc_prepare_y_pos(struct mxs_lradc *lradc) -{ - mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0); - mxs_lradc_reg_set(lradc, mxs_lradc_drive_y_plate(lradc), LRADC_CTRL0); - - lradc->cur_plate = LRADC_SAMPLE_Y; - mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_XM); - mxs_lradc_setup_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1); -} - -/* - * YP(+)--+-------------+ - * | |--+ - * | | | - * YM(meas)--+-------------+ | - * +--------------+ - * | | - * XP(meas) XM(-) - * - * (+) means here 1.85 V - * (-) means here GND - */ -static void mxs_lradc_prepare_pressure(struct mxs_lradc *lradc) -{ - mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0); - mxs_lradc_reg_set(lradc, mxs_lradc_drive_pressure(lradc), LRADC_CTRL0); - - lradc->cur_plate = LRADC_SAMPLE_PRESSURE; - mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YM); - mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL2, TS_CH_XP); - mxs_lradc_setup_ts_pressure(lradc, TOUCHSCREEN_VCHANNEL2, - TOUCHSCREEN_VCHANNEL1); -} - -static void mxs_lradc_enable_touch_detection(struct mxs_lradc *lradc) -{ - /* Configure the touchscreen type */ - if (lradc->soc == IMX28_LRADC) { - mxs_lradc_reg_clear(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE, - LRADC_CTRL0); - - if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE) - mxs_lradc_reg_set(lradc, - LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE, - LRADC_CTRL0); - } - - mxs_lradc_setup_touch_detection(lradc); - - lradc->cur_plate = LRADC_TOUCH; - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ | - LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1); - mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1); -} - -static void mxs_lradc_start_touch_event(struct mxs_lradc *lradc) -{ - mxs_lradc_reg_clear(lradc, - LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, - LRADC_CTRL1); - mxs_lradc_reg_set(lradc, - LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1), - LRADC_CTRL1); - /* - * start with the Y-pos, because it uses nearly the same plate - * settings like the touch detection - */ - mxs_lradc_prepare_y_pos(lradc); -} - -static void mxs_lradc_report_ts_event(struct mxs_lradc *lradc) -{ - input_report_abs(lradc->ts_input, ABS_X, lradc->ts_x_pos); - input_report_abs(lradc->ts_input, ABS_Y, lradc->ts_y_pos); - input_report_abs(lradc->ts_input, ABS_PRESSURE, lradc->ts_pressure); - input_report_key(lradc->ts_input, BTN_TOUCH, 1); - input_sync(lradc->ts_input); -} - -static void mxs_lradc_complete_touch_event(struct mxs_lradc *lradc) -{ - mxs_lradc_setup_touch_detection(lradc); - lradc->cur_plate = LRADC_SAMPLE_VALID; - /* - * start a dummy conversion to burn time to settle the signals - * note: we are not interested in the conversion's value - */ - mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(TOUCHSCREEN_VCHANNEL1)); - mxs_lradc_reg_clear(lradc, - LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) | - LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2), - LRADC_CTRL1); - mxs_lradc_reg_wrt( - lradc, - LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) | - LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), /* waste 5 ms */ - LRADC_DELAY(2)); -} - -/* - * in order to avoid false measurements, report only samples where - * the surface is still touched after the position measurement - */ -static void mxs_lradc_finish_touch_event(struct mxs_lradc *lradc, bool valid) -{ - /* if it is still touched, report the sample */ - if (valid && mxs_lradc_check_touch_event(lradc)) { - lradc->ts_valid = true; - mxs_lradc_report_ts_event(lradc); - } - - /* if it is even still touched, continue with the next measurement */ - if (mxs_lradc_check_touch_event(lradc)) { - mxs_lradc_prepare_y_pos(lradc); - return; - } - - if (lradc->ts_valid) { - /* signal the release */ - lradc->ts_valid = false; - input_report_key(lradc->ts_input, BTN_TOUCH, 0); - input_sync(lradc->ts_input); - } - - /* if it is released, wait for the next touch via IRQ */ - lradc->cur_plate = LRADC_TOUCH; - mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2)); - mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3)); - mxs_lradc_reg_clear(lradc, - LRADC_CTRL1_TOUCH_DETECT_IRQ | - LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) | - LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1), - LRADC_CTRL1); - mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1); -} - -/* touchscreen's state machine */ -static void mxs_lradc_handle_touch(struct mxs_lradc *lradc) -{ - switch (lradc->cur_plate) { - case LRADC_TOUCH: - if (mxs_lradc_check_touch_event(lradc)) - mxs_lradc_start_touch_event(lradc); - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ, - LRADC_CTRL1); - return; - - case LRADC_SAMPLE_Y: - lradc->ts_y_pos = - mxs_lradc_read_raw_channel(lradc, - TOUCHSCREEN_VCHANNEL1); - mxs_lradc_prepare_x_pos(lradc); - return; - - case LRADC_SAMPLE_X: - lradc->ts_x_pos = - mxs_lradc_read_raw_channel(lradc, - TOUCHSCREEN_VCHANNEL1); - mxs_lradc_prepare_pressure(lradc); - return; - - case LRADC_SAMPLE_PRESSURE: - lradc->ts_pressure = - mxs_lradc_read_ts_pressure(lradc, - TOUCHSCREEN_VCHANNEL2, - TOUCHSCREEN_VCHANNEL1); - mxs_lradc_complete_touch_event(lradc); - return; - - case LRADC_SAMPLE_VALID: - mxs_lradc_finish_touch_event(lradc, 1); - break; - } -} - -/* - * Raw I/O operations - */ -static int mxs_lradc_read_single(struct iio_dev *iio_dev, int chan, int *val) -{ - struct mxs_lradc *lradc = iio_priv(iio_dev); - int ret; - - /* - * See if there is no buffered operation in progress. If there is, simply - * bail out. This can be improved to support both buffered and raw IO at - * the same time, yet the code becomes horribly complicated. Therefore I - * applied KISS principle here. - */ - ret = mutex_trylock(&lradc->lock); - if (!ret) - return -EBUSY; - - reinit_completion(&lradc->completion); - - /* - * No buffered operation in progress, map the channel and trigger it. - * Virtual channel 0 is always used here as the others are always not - * used if doing raw sampling. - */ - if (lradc->soc == IMX28_LRADC) - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), - LRADC_CTRL1); - mxs_lradc_reg_clear(lradc, 0x1, LRADC_CTRL0); - - /* Enable / disable the divider per requirement */ - if (test_bit(chan, &lradc->is_divided)) - mxs_lradc_reg_set(lradc, - 1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET, - LRADC_CTRL2); - else - mxs_lradc_reg_clear(lradc, - 1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET, - LRADC_CTRL2); - - /* Clean the slot's previous content, then set new one. */ - mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(0), - LRADC_CTRL4); - mxs_lradc_reg_set(lradc, chan, LRADC_CTRL4); - - mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(0)); - - /* Enable the IRQ and start sampling the channel. */ - mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1); - mxs_lradc_reg_set(lradc, BIT(0), LRADC_CTRL0); - - /* Wait for completion on the channel, 1 second max. */ - ret = wait_for_completion_killable_timeout(&lradc->completion, HZ); - if (!ret) - ret = -ETIMEDOUT; - if (ret < 0) - goto err; - - /* Read the data. */ - *val = readl(lradc->base + LRADC_CH(0)) & LRADC_CH_VALUE_MASK; - ret = IIO_VAL_INT; - -err: - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1); - - mutex_unlock(&lradc->lock); - - return ret; -} - -static int mxs_lradc_read_temp(struct iio_dev *iio_dev, int *val) -{ - int ret, min, max; - - ret = mxs_lradc_read_single(iio_dev, 8, &min); - if (ret != IIO_VAL_INT) - return ret; - - ret = mxs_lradc_read_single(iio_dev, 9, &max); - if (ret != IIO_VAL_INT) - return ret; - - *val = max - min; - - return IIO_VAL_INT; -} - -static int mxs_lradc_read_raw(struct iio_dev *iio_dev, - const struct iio_chan_spec *chan, - int *val, int *val2, long m) -{ - struct mxs_lradc *lradc = iio_priv(iio_dev); - - switch (m) { - case IIO_CHAN_INFO_RAW: - if (chan->type == IIO_TEMP) - return mxs_lradc_read_temp(iio_dev, val); - - return mxs_lradc_read_single(iio_dev, chan->channel, val); - - case IIO_CHAN_INFO_SCALE: - if (chan->type == IIO_TEMP) { - /* - * From the datasheet, we have to multiply by 1.012 and - * divide by 4 - */ - *val = 0; - *val2 = 253000; - return IIO_VAL_INT_PLUS_MICRO; - } - - *val = lradc->vref_mv[chan->channel]; - *val2 = chan->scan_type.realbits - - test_bit(chan->channel, &lradc->is_divided); - return IIO_VAL_FRACTIONAL_LOG2; - - case IIO_CHAN_INFO_OFFSET: - if (chan->type == IIO_TEMP) { - /* - * The calculated value from the ADC is in Kelvin, we - * want Celsius for hwmon so the offset is -273.15 - * The offset is applied before scaling so it is - * actually -213.15 * 4 / 1.012 = -1079.644268 - */ - *val = -1079; - *val2 = 644268; - - return IIO_VAL_INT_PLUS_MICRO; - } - - return -EINVAL; - - default: - break; - } - - return -EINVAL; -} - -static int mxs_lradc_write_raw(struct iio_dev *iio_dev, - const struct iio_chan_spec *chan, - int val, int val2, long m) -{ - struct mxs_lradc *lradc = iio_priv(iio_dev); - struct mxs_lradc_scale *scale_avail = - lradc->scale_avail[chan->channel]; - int ret; - - ret = mutex_trylock(&lradc->lock); - if (!ret) - return -EBUSY; - - switch (m) { - case IIO_CHAN_INFO_SCALE: - ret = -EINVAL; - if (val == scale_avail[MXS_LRADC_DIV_DISABLED].integer && - val2 == scale_avail[MXS_LRADC_DIV_DISABLED].nano) { - /* divider by two disabled */ - clear_bit(chan->channel, &lradc->is_divided); - ret = 0; - } else if (val == scale_avail[MXS_LRADC_DIV_ENABLED].integer && - val2 == scale_avail[MXS_LRADC_DIV_ENABLED].nano) { - /* divider by two enabled */ - set_bit(chan->channel, &lradc->is_divided); - ret = 0; - } - - break; - default: - ret = -EINVAL; - break; - } - - mutex_unlock(&lradc->lock); - - return ret; -} - -static int mxs_lradc_write_raw_get_fmt(struct iio_dev *iio_dev, - const struct iio_chan_spec *chan, - long m) -{ - return IIO_VAL_INT_PLUS_NANO; -} - -static ssize_t mxs_lradc_show_scale_available_ch(struct device *dev, - struct device_attribute *attr, - char *buf, - int ch) -{ - struct iio_dev *iio = dev_to_iio_dev(dev); - struct mxs_lradc *lradc = iio_priv(iio); - int i, len = 0; - - for (i = 0; i < ARRAY_SIZE(lradc->scale_avail[ch]); i++) - len += sprintf(buf + len, "%u.%09u ", - lradc->scale_avail[ch][i].integer, - lradc->scale_avail[ch][i].nano); - - len += sprintf(buf + len, "\n"); - - return len; -} - -static ssize_t mxs_lradc_show_scale_available(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr); - - return mxs_lradc_show_scale_available_ch(dev, attr, buf, - iio_attr->address); -} - -#define SHOW_SCALE_AVAILABLE_ATTR(ch) \ -static IIO_DEVICE_ATTR(in_voltage##ch##_scale_available, S_IRUGO, \ - mxs_lradc_show_scale_available, NULL, ch) - -SHOW_SCALE_AVAILABLE_ATTR(0); -SHOW_SCALE_AVAILABLE_ATTR(1); -SHOW_SCALE_AVAILABLE_ATTR(2); -SHOW_SCALE_AVAILABLE_ATTR(3); -SHOW_SCALE_AVAILABLE_ATTR(4); -SHOW_SCALE_AVAILABLE_ATTR(5); -SHOW_SCALE_AVAILABLE_ATTR(6); -SHOW_SCALE_AVAILABLE_ATTR(7); -SHOW_SCALE_AVAILABLE_ATTR(10); -SHOW_SCALE_AVAILABLE_ATTR(11); -SHOW_SCALE_AVAILABLE_ATTR(12); -SHOW_SCALE_AVAILABLE_ATTR(13); -SHOW_SCALE_AVAILABLE_ATTR(14); -SHOW_SCALE_AVAILABLE_ATTR(15); - -static struct attribute *mxs_lradc_attributes[] = { - &iio_dev_attr_in_voltage0_scale_available.dev_attr.attr, - &iio_dev_attr_in_voltage1_scale_available.dev_attr.attr, - &iio_dev_attr_in_voltage2_scale_available.dev_attr.attr, - &iio_dev_attr_in_voltage3_scale_available.dev_attr.attr, - &iio_dev_attr_in_voltage4_scale_available.dev_attr.attr, - &iio_dev_attr_in_voltage5_scale_available.dev_attr.attr, - &iio_dev_attr_in_voltage6_scale_available.dev_attr.attr, - &iio_dev_attr_in_voltage7_scale_available.dev_attr.attr, - &iio_dev_attr_in_voltage10_scale_available.dev_attr.attr, - &iio_dev_attr_in_voltage11_scale_available.dev_attr.attr, - &iio_dev_attr_in_voltage12_scale_available.dev_attr.attr, - &iio_dev_attr_in_voltage13_scale_available.dev_attr.attr, - &iio_dev_attr_in_voltage14_scale_available.dev_attr.attr, - &iio_dev_attr_in_voltage15_scale_available.dev_attr.attr, - NULL -}; - -static const struct attribute_group mxs_lradc_attribute_group = { - .attrs = mxs_lradc_attributes, -}; - -static const struct iio_info mxs_lradc_iio_info = { - .driver_module = THIS_MODULE, - .read_raw = mxs_lradc_read_raw, - .write_raw = mxs_lradc_write_raw, - .write_raw_get_fmt = mxs_lradc_write_raw_get_fmt, - .attrs = &mxs_lradc_attribute_group, -}; - -static int mxs_lradc_ts_open(struct input_dev *dev) -{ - struct mxs_lradc *lradc = input_get_drvdata(dev); - - /* Enable the touch-detect circuitry. */ - mxs_lradc_enable_touch_detection(lradc); - - return 0; -} - -static void mxs_lradc_disable_ts(struct mxs_lradc *lradc) -{ - /* stop all interrupts from firing */ - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN | - LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) | - LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1); - - /* Power-down touchscreen touch-detect circuitry. */ - mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0); -} - -static void mxs_lradc_ts_close(struct input_dev *dev) -{ - struct mxs_lradc *lradc = input_get_drvdata(dev); - - mxs_lradc_disable_ts(lradc); -} - -static int mxs_lradc_ts_register(struct mxs_lradc *lradc) -{ - struct input_dev *input; - struct device *dev = lradc->dev; - - if (!lradc->use_touchscreen) - return 0; - - input = devm_input_allocate_device(dev); - if (!input) - return -ENOMEM; - - input->name = DRIVER_NAME; - input->id.bustype = BUS_HOST; - input->open = mxs_lradc_ts_open; - input->close = mxs_lradc_ts_close; - - __set_bit(EV_ABS, input->evbit); - __set_bit(EV_KEY, input->evbit); - __set_bit(BTN_TOUCH, input->keybit); - __set_bit(INPUT_PROP_DIRECT, input->propbit); - input_set_abs_params(input, ABS_X, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0); - input_set_abs_params(input, ABS_Y, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0); - input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_SINGLE_SAMPLE_MASK, - 0, 0); - - lradc->ts_input = input; - input_set_drvdata(input, lradc); - - return input_register_device(input); -} - -/* - * IRQ Handling - */ -static irqreturn_t mxs_lradc_handle_irq(int irq, void *data) -{ - struct iio_dev *iio = data; - struct mxs_lradc *lradc = iio_priv(iio); - unsigned long reg = readl(lradc->base + LRADC_CTRL1); - u32 clr_irq = mxs_lradc_irq_mask(lradc); - const u32 ts_irq_mask = - LRADC_CTRL1_TOUCH_DETECT_IRQ | - LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) | - LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2); - - if (!(reg & mxs_lradc_irq_mask(lradc))) - return IRQ_NONE; - - if (lradc->use_touchscreen && (reg & ts_irq_mask)) { - mxs_lradc_handle_touch(lradc); - - /* Make sure we don't clear the next conversion's interrupt. */ - clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) | - LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2)); - } - - if (iio_buffer_enabled(iio)) { - if (reg & lradc->buffer_vchans) - iio_trigger_poll(iio->trig); - } else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) { - complete(&lradc->completion); - } - - mxs_lradc_reg_clear(lradc, reg & clr_irq, LRADC_CTRL1); - - return IRQ_HANDLED; -} - -/* - * Trigger handling - */ -static irqreturn_t mxs_lradc_trigger_handler(int irq, void *p) -{ - struct iio_poll_func *pf = p; - struct iio_dev *iio = pf->indio_dev; - struct mxs_lradc *lradc = iio_priv(iio); - const u32 chan_value = LRADC_CH_ACCUMULATE | - ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET); - unsigned int i, j = 0; - - for_each_set_bit(i, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) { - lradc->buffer[j] = readl(lradc->base + LRADC_CH(j)); - mxs_lradc_reg_wrt(lradc, chan_value, LRADC_CH(j)); - lradc->buffer[j] &= LRADC_CH_VALUE_MASK; - lradc->buffer[j] /= LRADC_DELAY_TIMER_LOOP; - j++; - } - - iio_push_to_buffers_with_timestamp(iio, lradc->buffer, pf->timestamp); - - iio_trigger_notify_done(iio->trig); - - return IRQ_HANDLED; -} - -static int mxs_lradc_configure_trigger(struct iio_trigger *trig, bool state) -{ - struct iio_dev *iio = iio_trigger_get_drvdata(trig); - struct mxs_lradc *lradc = iio_priv(iio); - const u32 st = state ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR; - - mxs_lradc_reg_wrt(lradc, LRADC_DELAY_KICK, LRADC_DELAY(0) + st); - - return 0; -} - -static const struct iio_trigger_ops mxs_lradc_trigger_ops = { - .owner = THIS_MODULE, - .set_trigger_state = &mxs_lradc_configure_trigger, -}; - -static int mxs_lradc_trigger_init(struct iio_dev *iio) -{ - int ret; - struct iio_trigger *trig; - struct mxs_lradc *lradc = iio_priv(iio); - - trig = iio_trigger_alloc("%s-dev%i", iio->name, iio->id); - if (!trig) - return -ENOMEM; - - trig->dev.parent = lradc->dev; - iio_trigger_set_drvdata(trig, iio); - trig->ops = &mxs_lradc_trigger_ops; - - ret = iio_trigger_register(trig); - if (ret) { - iio_trigger_free(trig); - return ret; - } - - lradc->trig = trig; - - return 0; -} - -static void mxs_lradc_trigger_remove(struct iio_dev *iio) -{ - struct mxs_lradc *lradc = iio_priv(iio); - - iio_trigger_unregister(lradc->trig); - iio_trigger_free(lradc->trig); -} - -static int mxs_lradc_buffer_preenable(struct iio_dev *iio) -{ - struct mxs_lradc *lradc = iio_priv(iio); - int ret = 0, chan, ofs = 0; - unsigned long enable = 0; - u32 ctrl4_set = 0; - u32 ctrl4_clr = 0; - u32 ctrl1_irq = 0; - const u32 chan_value = LRADC_CH_ACCUMULATE | - ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET); - const int len = bitmap_weight(iio->active_scan_mask, - LRADC_MAX_TOTAL_CHANS); - - if (!len) - return -EINVAL; - - /* - * Lock the driver so raw access can not be done during buffered - * operation. This simplifies the code a lot. - */ - ret = mutex_trylock(&lradc->lock); - if (!ret) - return -EBUSY; - - lradc->buffer = kmalloc_array(len, sizeof(*lradc->buffer), GFP_KERNEL); - if (!lradc->buffer) { - ret = -ENOMEM; - goto err_mem; - } - - if (lradc->soc == IMX28_LRADC) - mxs_lradc_reg_clear( - lradc, - lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET, - LRADC_CTRL1); - mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0); - - for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) { - ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs); - ctrl4_clr |= LRADC_CTRL4_LRADCSELECT_MASK(ofs); - ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs); - mxs_lradc_reg_wrt(lradc, chan_value, LRADC_CH(ofs)); - bitmap_set(&enable, ofs, 1); - ofs++; - } - - mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK | - LRADC_DELAY_KICK, LRADC_DELAY(0)); - mxs_lradc_reg_clear(lradc, ctrl4_clr, LRADC_CTRL4); - mxs_lradc_reg_set(lradc, ctrl4_set, LRADC_CTRL4); - mxs_lradc_reg_set(lradc, ctrl1_irq, LRADC_CTRL1); - mxs_lradc_reg_set(lradc, enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET, - LRADC_DELAY(0)); - - return 0; - -err_mem: - mutex_unlock(&lradc->lock); - return ret; -} - -static int mxs_lradc_buffer_postdisable(struct iio_dev *iio) -{ - struct mxs_lradc *lradc = iio_priv(iio); - - mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK | - LRADC_DELAY_KICK, LRADC_DELAY(0)); - - mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0); - if (lradc->soc == IMX28_LRADC) - mxs_lradc_reg_clear( - lradc, - lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET, - LRADC_CTRL1); - - kfree(lradc->buffer); - mutex_unlock(&lradc->lock); - - return 0; -} - -static bool mxs_lradc_validate_scan_mask(struct iio_dev *iio, - const unsigned long *mask) -{ - struct mxs_lradc *lradc = iio_priv(iio); - const int map_chans = bitmap_weight(mask, LRADC_MAX_TOTAL_CHANS); - int rsvd_chans = 0; - unsigned long rsvd_mask = 0; - - if (lradc->use_touchbutton) - rsvd_mask |= CHAN_MASK_TOUCHBUTTON; - if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_4WIRE) - rsvd_mask |= CHAN_MASK_TOUCHSCREEN_4WIRE; - if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE) - rsvd_mask |= CHAN_MASK_TOUCHSCREEN_5WIRE; - - if (lradc->use_touchbutton) - rsvd_chans++; - if (lradc->use_touchscreen) - rsvd_chans += 2; - - /* Test for attempts to map channels with special mode of operation. */ - if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS)) - return false; - - /* Test for attempts to map more channels then available slots. */ - if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS) - return false; - - return true; -} - -static const struct iio_buffer_setup_ops mxs_lradc_buffer_ops = { - .preenable = &mxs_lradc_buffer_preenable, - .postenable = &iio_triggered_buffer_postenable, - .predisable = &iio_triggered_buffer_predisable, - .postdisable = &mxs_lradc_buffer_postdisable, - .validate_scan_mask = &mxs_lradc_validate_scan_mask, -}; - -/* - * Driver initialization - */ - -#define MXS_ADC_CHAN(idx, chan_type, name) { \ - .type = (chan_type), \ - .indexed = 1, \ - .scan_index = (idx), \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ - BIT(IIO_CHAN_INFO_SCALE), \ - .channel = (idx), \ - .address = (idx), \ - .scan_type = { \ - .sign = 'u', \ - .realbits = LRADC_RESOLUTION, \ - .storagebits = 32, \ - }, \ - .datasheet_name = (name), \ -} - -static const struct iio_chan_spec mx23_lradc_chan_spec[] = { - MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"), - MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"), - MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"), - MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"), - MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"), - MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"), - MXS_ADC_CHAN(6, IIO_VOLTAGE, "VDDIO"), - MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"), - /* Combined Temperature sensors */ - { - .type = IIO_TEMP, - .indexed = 1, - .scan_index = 8, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_OFFSET) | - BIT(IIO_CHAN_INFO_SCALE), - .channel = 8, - .scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,}, - .datasheet_name = "TEMP_DIE", - }, - /* Hidden channel to keep indexes */ - { - .type = IIO_TEMP, - .indexed = 1, - .scan_index = -1, - .channel = 9, - }, - MXS_ADC_CHAN(10, IIO_VOLTAGE, NULL), - MXS_ADC_CHAN(11, IIO_VOLTAGE, NULL), - MXS_ADC_CHAN(12, IIO_VOLTAGE, "USB_DP"), - MXS_ADC_CHAN(13, IIO_VOLTAGE, "USB_DN"), - MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"), - MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"), -}; - -static const struct iio_chan_spec mx28_lradc_chan_spec[] = { - MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"), - MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"), - MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"), - MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"), - MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"), - MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"), - MXS_ADC_CHAN(6, IIO_VOLTAGE, "LRADC6"), - MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"), - /* Combined Temperature sensors */ - { - .type = IIO_TEMP, - .indexed = 1, - .scan_index = 8, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_OFFSET) | - BIT(IIO_CHAN_INFO_SCALE), - .channel = 8, - .scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,}, - .datasheet_name = "TEMP_DIE", - }, - /* Hidden channel to keep indexes */ - { - .type = IIO_TEMP, - .indexed = 1, - .scan_index = -1, - .channel = 9, - }, - MXS_ADC_CHAN(10, IIO_VOLTAGE, "VDDIO"), - MXS_ADC_CHAN(11, IIO_VOLTAGE, "VTH"), - MXS_ADC_CHAN(12, IIO_VOLTAGE, "VDDA"), - MXS_ADC_CHAN(13, IIO_VOLTAGE, "VDDD"), - MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"), - MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"), -}; - -static void mxs_lradc_hw_init(struct mxs_lradc *lradc) -{ - /* The ADC always uses DELAY CHANNEL 0. */ - const u32 adc_cfg = - (1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + 0)) | - (LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET); - - /* Configure DELAY CHANNEL 0 for generic ADC sampling. */ - mxs_lradc_reg_wrt(lradc, adc_cfg, LRADC_DELAY(0)); - - /* Disable remaining DELAY CHANNELs */ - mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(1)); - mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2)); - mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3)); - - /* Start internal temperature sensing. */ - mxs_lradc_reg_wrt(lradc, 0, LRADC_CTRL2); -} - -static void mxs_lradc_hw_stop(struct mxs_lradc *lradc) -{ - int i; - - mxs_lradc_reg_clear(lradc, - lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET, - LRADC_CTRL1); - - for (i = 0; i < LRADC_MAX_DELAY_CHANS; i++) - mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(i)); -} - -static const struct of_device_id mxs_lradc_dt_ids[] = { - { .compatible = "fsl,imx23-lradc", .data = (void *)IMX23_LRADC, }, - { .compatible = "fsl,imx28-lradc", .data = (void *)IMX28_LRADC, }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids); - -static int mxs_lradc_probe_touchscreen(struct mxs_lradc *lradc, - struct device_node *lradc_node) -{ - int ret; - u32 ts_wires = 0, adapt; - - ret = of_property_read_u32(lradc_node, "fsl,lradc-touchscreen-wires", - &ts_wires); - if (ret) - return -ENODEV; /* touchscreen feature disabled */ - - switch (ts_wires) { - case 4: - lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_4WIRE; - break; - case 5: - if (lradc->soc == IMX28_LRADC) { - lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_5WIRE; - break; - } - /* fall through an error message for i.MX23 */ - default: - dev_err(lradc->dev, - "Unsupported number of touchscreen wires (%d)\n", - ts_wires); - return -EINVAL; - } - - if (of_property_read_u32(lradc_node, "fsl,ave-ctrl", &adapt)) { - lradc->over_sample_cnt = 4; - } else { - if (adapt < 1 || adapt > 32) { - dev_err(lradc->dev, "Invalid sample count (%u)\n", - adapt); - return -EINVAL; - } - lradc->over_sample_cnt = adapt; - } - - if (of_property_read_u32(lradc_node, "fsl,ave-delay", &adapt)) { - lradc->over_sample_delay = 2; - } else { - if (adapt < 2 || adapt > LRADC_DELAY_DELAY_MASK + 1) { - dev_err(lradc->dev, "Invalid sample delay (%u)\n", - adapt); - return -EINVAL; - } - lradc->over_sample_delay = adapt; - } - - if (of_property_read_u32(lradc_node, "fsl,settling", &adapt)) { - lradc->settling_delay = 10; - } else { - if (adapt < 1 || adapt > LRADC_DELAY_DELAY_MASK) { - dev_err(lradc->dev, "Invalid settling delay (%u)\n", - adapt); - return -EINVAL; - } - lradc->settling_delay = adapt; - } - - return 0; -} - -static int mxs_lradc_probe(struct platform_device *pdev) -{ - const struct of_device_id *of_id = - of_match_device(mxs_lradc_dt_ids, &pdev->dev); - const struct mxs_lradc_of_config *of_cfg = - &mxs_lradc_of_config[(enum mxs_lradc_id)of_id->data]; - struct device *dev = &pdev->dev; - struct device_node *node = dev->of_node; - struct mxs_lradc *lradc; - struct iio_dev *iio; - struct resource *iores; - int ret = 0, touch_ret; - int i, s; - u64 scale_uv; - - /* Allocate the IIO device. */ - iio = devm_iio_device_alloc(dev, sizeof(*lradc)); - if (!iio) { - dev_err(dev, "Failed to allocate IIO device\n"); - return -ENOMEM; - } - - lradc = iio_priv(iio); - lradc->soc = (enum mxs_lradc_id)of_id->data; - - /* Grab the memory area */ - iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); - lradc->dev = &pdev->dev; - lradc->base = devm_ioremap_resource(dev, iores); - if (IS_ERR(lradc->base)) - return PTR_ERR(lradc->base); - - lradc->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(lradc->clk)) { - dev_err(dev, "Failed to get the delay unit clock\n"); - return PTR_ERR(lradc->clk); - } - ret = clk_prepare_enable(lradc->clk); - if (ret != 0) { - dev_err(dev, "Failed to enable the delay unit clock\n"); - return ret; - } - - touch_ret = mxs_lradc_probe_touchscreen(lradc, node); - - if (touch_ret == 0) - lradc->buffer_vchans = BUFFER_VCHANS_LIMITED; - else - lradc->buffer_vchans = BUFFER_VCHANS_ALL; - - /* Grab all IRQ sources */ - for (i = 0; i < of_cfg->irq_count; i++) { - lradc->irq[i] = platform_get_irq(pdev, i); - if (lradc->irq[i] < 0) { - ret = lradc->irq[i]; - goto err_clk; - } - - ret = devm_request_irq(dev, lradc->irq[i], - mxs_lradc_handle_irq, 0, - of_cfg->irq_name[i], iio); - if (ret) - goto err_clk; - } - - lradc->vref_mv = of_cfg->vref_mv; - - platform_set_drvdata(pdev, iio); - - init_completion(&lradc->completion); - mutex_init(&lradc->lock); - - iio->name = pdev->name; - iio->dev.parent = &pdev->dev; - iio->info = &mxs_lradc_iio_info; - iio->modes = INDIO_DIRECT_MODE; - iio->masklength = LRADC_MAX_TOTAL_CHANS; - - if (lradc->soc == IMX23_LRADC) { - iio->channels = mx23_lradc_chan_spec; - iio->num_channels = ARRAY_SIZE(mx23_lradc_chan_spec); - } else { - iio->channels = mx28_lradc_chan_spec; - iio->num_channels = ARRAY_SIZE(mx28_lradc_chan_spec); - } - - ret = iio_triggered_buffer_setup(iio, &iio_pollfunc_store_time, - &mxs_lradc_trigger_handler, - &mxs_lradc_buffer_ops); - if (ret) - goto err_clk; - - ret = mxs_lradc_trigger_init(iio); - if (ret) - goto err_trig; - - /* Populate available ADC input ranges */ - for (i = 0; i < LRADC_MAX_TOTAL_CHANS; i++) { - for (s = 0; s < ARRAY_SIZE(lradc->scale_avail[i]); s++) { - /* - * [s=0] = optional divider by two disabled (default) - * [s=1] = optional divider by two enabled - * - * The scale is calculated by doing: - * Vref >> (realbits - s) - * which multiplies by two on the second component - * of the array. - */ - scale_uv = ((u64)lradc->vref_mv[i] * 100000000) >> - (LRADC_RESOLUTION - s); - lradc->scale_avail[i][s].nano = - do_div(scale_uv, 100000000) * 10; - lradc->scale_avail[i][s].integer = scale_uv; - } - } - - ret = stmp_reset_block(lradc->base); - if (ret) - goto err_dev; - - /* Configure the hardware. */ - mxs_lradc_hw_init(lradc); - - /* Register the touchscreen input device. */ - if (touch_ret == 0) { - ret = mxs_lradc_ts_register(lradc); - if (ret) - goto err_ts_register; - } - - /* Register IIO device. */ - ret = iio_device_register(iio); - if (ret) { - dev_err(dev, "Failed to register IIO device\n"); - return ret; - } - - return 0; - -err_ts_register: - mxs_lradc_hw_stop(lradc); -err_dev: - mxs_lradc_trigger_remove(iio); -err_trig: - iio_triggered_buffer_cleanup(iio); -err_clk: - clk_disable_unprepare(lradc->clk); - return ret; -} - -static int mxs_lradc_remove(struct platform_device *pdev) -{ - struct iio_dev *iio = platform_get_drvdata(pdev); - struct mxs_lradc *lradc = iio_priv(iio); - - iio_device_unregister(iio); - mxs_lradc_hw_stop(lradc); - mxs_lradc_trigger_remove(iio); - iio_triggered_buffer_cleanup(iio); - - clk_disable_unprepare(lradc->clk); - - return 0; -} - -static struct platform_driver mxs_lradc_driver = { - .driver = { - .name = DRIVER_NAME, - .of_match_table = mxs_lradc_dt_ids, - }, - .probe = mxs_lradc_probe, - .remove = mxs_lradc_remove, -}; - -module_platform_driver(mxs_lradc_driver); - -MODULE_AUTHOR("Marek Vasut "); -MODULE_DESCRIPTION("Freescale MXS LRADC driver"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:" DRIVER_NAME); -- cgit v1.2.3-59-g8ed1b From b2d476404601806c5a94a5f9bfc441d3898a5541 Mon Sep 17 00:00:00 2001 From: Ksenija Stanojevic Date: Thu, 16 Mar 2017 13:27:13 +0100 Subject: dt-bindings: mfd: Move mxs-lradc binding document from IIO to MFD The bindings, which are now used in MFD, need also to be documented in the MFD binding document. Signed-off-by: Ksenija Stanojevic Reviewed-by: Marek Vasut Signed-off-by: Lee Jones --- .../devicetree/bindings/iio/adc/mxs-lradc.txt | 47 ---------------------- .../devicetree/bindings/mfd/mxs-lradc.txt | 47 ++++++++++++++++++++++ 2 files changed, 47 insertions(+), 47 deletions(-) delete mode 100644 Documentation/devicetree/bindings/iio/adc/mxs-lradc.txt create mode 100644 Documentation/devicetree/bindings/mfd/mxs-lradc.txt diff --git a/Documentation/devicetree/bindings/iio/adc/mxs-lradc.txt b/Documentation/devicetree/bindings/iio/adc/mxs-lradc.txt deleted file mode 100644 index 555fb117d4fa..000000000000 --- a/Documentation/devicetree/bindings/iio/adc/mxs-lradc.txt +++ /dev/null @@ -1,47 +0,0 @@ -* Freescale MXS LRADC device driver - -Required properties: -- compatible: Should be "fsl,imx23-lradc" for i.MX23 SoC and "fsl,imx28-lradc" - for i.MX28 SoC -- reg: Address and length of the register set for the device -- interrupts: Should contain the LRADC interrupts - -Optional properties: -- fsl,lradc-touchscreen-wires: Number of wires used to connect the touchscreen - to LRADC. Valid value is either 4 or 5. If this - property is not present, then the touchscreen is - disabled. 5 wires is valid for i.MX28 SoC only. -- fsl,ave-ctrl: number of samples per direction to calculate an average value. - Allowed value is 1 ... 32, default is 4 -- fsl,ave-delay: delay between consecutive samples. Allowed value is - 2 ... 2048. It is used if 'fsl,ave-ctrl' > 1, counts at - 2 kHz and its default is 2 (= 1 ms) -- fsl,settling: delay between plate switch to next sample. Allowed value is - 1 ... 2047. It counts at 2 kHz and its default is - 10 (= 5 ms) - -Example for i.MX23 SoC: - - lradc@80050000 { - compatible = "fsl,imx23-lradc"; - reg = <0x80050000 0x2000>; - interrupts = <36 37 38 39 40 41 42 43 44>; - status = "okay"; - fsl,lradc-touchscreen-wires = <4>; - fsl,ave-ctrl = <4>; - fsl,ave-delay = <2>; - fsl,settling = <10>; - }; - -Example for i.MX28 SoC: - - lradc@80050000 { - compatible = "fsl,imx28-lradc"; - reg = <0x80050000 0x2000>; - interrupts = <10 14 15 16 17 18 19 20 21 22 23 24 25>; - status = "okay"; - fsl,lradc-touchscreen-wires = <5>; - fsl,ave-ctrl = <4>; - fsl,ave-delay = <2>; - fsl,settling = <10>; - }; diff --git a/Documentation/devicetree/bindings/mfd/mxs-lradc.txt b/Documentation/devicetree/bindings/mfd/mxs-lradc.txt new file mode 100644 index 000000000000..555fb117d4fa --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/mxs-lradc.txt @@ -0,0 +1,47 @@ +* Freescale MXS LRADC device driver + +Required properties: +- compatible: Should be "fsl,imx23-lradc" for i.MX23 SoC and "fsl,imx28-lradc" + for i.MX28 SoC +- reg: Address and length of the register set for the device +- interrupts: Should contain the LRADC interrupts + +Optional properties: +- fsl,lradc-touchscreen-wires: Number of wires used to connect the touchscreen + to LRADC. Valid value is either 4 or 5. If this + property is not present, then the touchscreen is + disabled. 5 wires is valid for i.MX28 SoC only. +- fsl,ave-ctrl: number of samples per direction to calculate an average value. + Allowed value is 1 ... 32, default is 4 +- fsl,ave-delay: delay between consecutive samples. Allowed value is + 2 ... 2048. It is used if 'fsl,ave-ctrl' > 1, counts at + 2 kHz and its default is 2 (= 1 ms) +- fsl,settling: delay between plate switch to next sample. Allowed value is + 1 ... 2047. It counts at 2 kHz and its default is + 10 (= 5 ms) + +Example for i.MX23 SoC: + + lradc@80050000 { + compatible = "fsl,imx23-lradc"; + reg = <0x80050000 0x2000>; + interrupts = <36 37 38 39 40 41 42 43 44>; + status = "okay"; + fsl,lradc-touchscreen-wires = <4>; + fsl,ave-ctrl = <4>; + fsl,ave-delay = <2>; + fsl,settling = <10>; + }; + +Example for i.MX28 SoC: + + lradc@80050000 { + compatible = "fsl,imx28-lradc"; + reg = <0x80050000 0x2000>; + interrupts = <10 14 15 16 17 18 19 20 21 22 23 24 25>; + status = "okay"; + fsl,lradc-touchscreen-wires = <5>; + fsl,ave-ctrl = <4>; + fsl,ave-delay = <2>; + fsl,settling = <10>; + }; -- cgit v1.2.3-59-g8ed1b From ab781ec0e5e781849bd14291608c8626bac871e1 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 29 Mar 2017 14:18:20 +0200 Subject: mfd: cpcap: Implement IRQ sense helper CPCAP can sense if IRQ is currently set or not. This functionality is required for a few subdevices, such as the power button and usb phy modules. Signed-off-by: Sebastian Reichel Acked-by: Tony Lindgren Signed-off-by: Lee Jones --- drivers/mfd/motorola-cpcap.c | 28 ++++++++++++++++++++++++++++ include/linux/mfd/motorola-cpcap.h | 2 ++ 2 files changed, 30 insertions(+) diff --git a/drivers/mfd/motorola-cpcap.c b/drivers/mfd/motorola-cpcap.c index 6aeada7d7ce5..a9097efcefa5 100644 --- a/drivers/mfd/motorola-cpcap.c +++ b/drivers/mfd/motorola-cpcap.c @@ -23,6 +23,8 @@ #define CPCAP_NR_IRQ_REG_BANKS 6 #define CPCAP_NR_IRQ_CHIPS 3 +#define CPCAP_REGISTER_SIZE 4 +#define CPCAP_REGISTER_BITS 16 struct cpcap_ddata { struct spi_device *spi; @@ -32,6 +34,32 @@ struct cpcap_ddata { struct regmap *regmap; }; +static int cpcap_sense_irq(struct regmap *regmap, int irq) +{ + int regnum = irq / CPCAP_REGISTER_BITS; + int mask = BIT(irq % CPCAP_REGISTER_BITS); + int reg = CPCAP_REG_INTS1 + (regnum * CPCAP_REGISTER_SIZE); + int err, val; + + if (reg < CPCAP_REG_INTS1 || reg > CPCAP_REG_INTS4) + return -EINVAL; + + err = regmap_read(regmap, reg, &val); + if (err) + return err; + + return !!(val & mask); +} + +int cpcap_sense_virq(struct regmap *regmap, int virq) +{ + struct regmap_irq_chip_data *d = irq_get_chip_data(virq); + int irq_base = regmap_irq_chip_get_base(d); + + return cpcap_sense_irq(regmap, virq - irq_base); +} +EXPORT_SYMBOL_GPL(cpcap_sense_virq); + static int cpcap_check_revision(struct cpcap_ddata *cpcap) { u16 vendor, rev; diff --git a/include/linux/mfd/motorola-cpcap.h b/include/linux/mfd/motorola-cpcap.h index b4031c2b2214..793aa695faa0 100644 --- a/include/linux/mfd/motorola-cpcap.h +++ b/include/linux/mfd/motorola-cpcap.h @@ -290,3 +290,5 @@ static inline int cpcap_get_vendor(struct device *dev, return 0; } + +extern int cpcap_sense_virq(struct regmap *regmap, int virq); -- cgit v1.2.3-59-g8ed1b From 6d99971842f6b0779738d8c168d9ed92ef1ff5fc Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 21 Mar 2017 23:50:42 +0100 Subject: input: cpcap-pwrbutton: New driver Motorola CPCAP is a PMIC found in multiple smartphones. This driver adds support for the power/on button and has been tested in Droid 4. Acked-by: Rob Herring Acked-by: Dmitry Torokhov Tested-by: Tony Lindgren Signed-off-by: Sebastian Reichel Signed-off-by: Lee Jones --- .../devicetree/bindings/input/cpcap-pwrbutton.txt | 20 ++++ drivers/input/misc/Kconfig | 10 ++ drivers/input/misc/Makefile | 1 + drivers/input/misc/cpcap-pwrbutton.c | 117 +++++++++++++++++++++ 4 files changed, 148 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/cpcap-pwrbutton.txt create mode 100644 drivers/input/misc/cpcap-pwrbutton.c diff --git a/Documentation/devicetree/bindings/input/cpcap-pwrbutton.txt b/Documentation/devicetree/bindings/input/cpcap-pwrbutton.txt new file mode 100644 index 000000000000..0dd0076daf71 --- /dev/null +++ b/Documentation/devicetree/bindings/input/cpcap-pwrbutton.txt @@ -0,0 +1,20 @@ +Motorola CPCAP on key + +This module is part of the CPCAP. For more details about the whole +chip see Documentation/devicetree/bindings/mfd/motorola-cpcap.txt. + +This module provides a simple power button event via an Interrupt. + +Required properties: +- compatible: should be one of the following + - "motorola,cpcap-pwrbutton" +- interrupts: irq specifier for CPCAP's ON IRQ + +Example: + +&cpcap { + cpcap_pwrbutton: pwrbutton { + compatible = "motorola,cpcap-pwrbutton"; + interrupts = <23 IRQ_TYPE_NONE>; + }; +}; diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 5b6c52210d20..9f7b72249eac 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -316,6 +316,16 @@ config INPUT_COBALT_BTNS To compile this driver as a module, choose M here: the module will be called cobalt_btns. +config INPUT_CPCAP_PWRBUTTON + tristate "CPCAP OnKey" + depends on MFD_CPCAP + help + Say Y here if you want to enable power key reporting via the + Motorola CPCAP chip. + + To compile this driver as a module, choose M here. The module will + be called cpcap-pwrbutton. + config INPUT_WISTRON_BTNS tristate "x86 Wistron laptop button interface" depends on X86_32 diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index b10523f2878e..b923a9828c88 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_INPUT_CM109) += cm109.o obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o +obj-$(CONFIG_INPUT_CPCAP_PWRBUTTON) += cpcap-pwrbutton.o obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o obj-$(CONFIG_INPUT_DA9063_ONKEY) += da9063_onkey.o diff --git a/drivers/input/misc/cpcap-pwrbutton.c b/drivers/input/misc/cpcap-pwrbutton.c new file mode 100644 index 000000000000..0abef63217e2 --- /dev/null +++ b/drivers/input/misc/cpcap-pwrbutton.c @@ -0,0 +1,117 @@ +/** + * CPCAP Power Button Input Driver + * + * Copyright (C) 2017 Sebastian Reichel + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CPCAP_IRQ_ON 23 +#define CPCAP_IRQ_ON_BITMASK (1 << (CPCAP_IRQ_ON % 16)) + +struct cpcap_power_button { + struct regmap *regmap; + struct input_dev *idev; + struct device *dev; +}; + +static irqreturn_t powerbutton_irq(int irq, void *_button) +{ + struct cpcap_power_button *button = _button; + int val; + + val = cpcap_sense_virq(button->regmap, irq); + if (val < 0) { + dev_err(button->dev, "irq read failed: %d", val); + return IRQ_HANDLED; + } + + pm_wakeup_event(button->dev, 0); + input_report_key(button->idev, KEY_POWER, val); + input_sync(button->idev); + + return IRQ_HANDLED; +} + +static int cpcap_power_button_probe(struct platform_device *pdev) +{ + struct cpcap_power_button *button; + int irq = platform_get_irq(pdev, 0); + int err; + + button = devm_kmalloc(&pdev->dev, sizeof(*button), GFP_KERNEL); + if (!button) + return -ENOMEM; + + button->idev = devm_input_allocate_device(&pdev->dev); + if (!button->idev) + return -ENOMEM; + + button->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!button->regmap) + return -ENODEV; + + button->dev = &pdev->dev; + + button->idev->name = "cpcap-pwrbutton"; + button->idev->phys = "cpcap-pwrbutton/input0"; + button->idev->dev.parent = button->dev; + input_set_capability(button->idev, EV_KEY, KEY_POWER); + + err = devm_request_threaded_irq(&pdev->dev, irq, NULL, + powerbutton_irq, IRQF_ONESHOT, "cpcap_pwrbutton", button); + if (err < 0) { + dev_err(&pdev->dev, "IRQ request failed: %d\n", err); + return err; + } + + err = input_register_device(button->idev); + if (err) { + dev_err(&pdev->dev, "Input register failed: %d\n", err); + return err; + } + + device_init_wakeup(&pdev->dev, true); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id cpcap_pwrbutton_dt_match_table[] = { + { .compatible = "motorola,cpcap-pwrbutton" }, + {}, +}; +MODULE_DEVICE_TABLE(of, cpcap_pwrbutton_dt_match_table); +#endif + +static struct platform_driver cpcap_power_button_driver = { + .probe = cpcap_power_button_probe, + .driver = { + .name = "cpcap-pwrbutton", + .of_match_table = of_match_ptr(cpcap_pwrbutton_dt_match_table), + }, +}; +module_platform_driver(cpcap_power_button_driver); + +MODULE_ALIAS("platform:cpcap-pwrbutton"); +MODULE_DESCRIPTION("CPCAP Power Button"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sebastian Reichel "); -- cgit v1.2.3-59-g8ed1b From e04653a9dcf4d98defe2149c885382e5cc72082f Mon Sep 17 00:00:00 2001 From: Archana Patni Date: Wed, 1 Feb 2017 17:22:03 +0100 Subject: mfd: cros_ec: Add ACPI GPE handler for LID0 devices This patch installs an ACPI GPE handler for LID0 ACPI device to indicate ACPI core that this GPE should stay enabled for lid to work in suspend to idle path. Signed-off-by: Archana Patni Signed-off-by: Thierry Escande Signed-off-by: Lee Jones --- drivers/mfd/Makefile | 4 +- drivers/mfd/cros_ec.c | 15 ++++-- drivers/mfd/cros_ec_acpi_gpe.c | 103 +++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/cros_ec.h | 18 +++++++ 4 files changed, 136 insertions(+), 4 deletions(-) create mode 100644 drivers/mfd/cros_ec_acpi_gpe.c diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 790698a892ba..e314ba1058dc 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -10,7 +10,9 @@ obj-$(CONFIG_MFD_ACT8945A) += act8945a.o obj-$(CONFIG_MFD_SM501) += sm501.o obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o -obj-$(CONFIG_MFD_CROS_EC) += cros_ec.o +cros_ec_core-objs := cros_ec.o +cros_ec_core-$(CONFIG_ACPI) += cros_ec_acpi_gpe.o +obj-$(CONFIG_MFD_CROS_EC) += cros_ec_core.o obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.o obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index 9b66a98ba4bf..d4a407e466b5 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -166,6 +166,8 @@ int cros_ec_register(struct cros_ec_device *ec_dev) dev_info(dev, "Chrome EC device registered\n"); + cros_ec_acpi_install_gpe_handler(dev); + return 0; fail_mfd: @@ -179,6 +181,8 @@ int cros_ec_remove(struct cros_ec_device *ec_dev) { mfd_remove_devices(ec_dev->dev); + cros_ec_acpi_remove_gpe_handler(); + return 0; } EXPORT_SYMBOL(cros_ec_remove); @@ -190,9 +194,14 @@ int cros_ec_suspend(struct cros_ec_device *ec_dev) int ret; u8 sleep_event; - sleep_event = (!IS_ENABLED(CONFIG_ACPI) || pm_suspend_via_firmware()) ? - HOST_SLEEP_EVENT_S3_RESUME : - HOST_SLEEP_EVENT_S0IX_RESUME; + if (!IS_ENABLED(CONFIG_ACPI) || pm_suspend_via_firmware()) { + sleep_event = HOST_SLEEP_EVENT_S3_SUSPEND; + } else { + sleep_event = HOST_SLEEP_EVENT_S0IX_SUSPEND; + + /* Clearing the GPE status for any pending event */ + cros_ec_acpi_clear_gpe(); + } ret = cros_ec_sleep_event(ec_dev, sleep_event); if (ret < 0) diff --git a/drivers/mfd/cros_ec_acpi_gpe.c b/drivers/mfd/cros_ec_acpi_gpe.c new file mode 100644 index 000000000000..56d305dab2d4 --- /dev/null +++ b/drivers/mfd/cros_ec_acpi_gpe.c @@ -0,0 +1,103 @@ +/* + * ChromeOS EC multi-function device + * + * Copyright (C) 2017 Google, Inc + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * The ChromeOS EC multi function device is used to mux all the requests + * to the EC device for its multiple features: keyboard controller, + * battery charging and regulator control, firmware update. + */ +#include + +#define ACPI_LID_DEVICE "LID0" + +static int ec_wake_gpe = -EINVAL; + +/* + * This handler indicates to ACPI core that this GPE should stay enabled for + * lid to work in suspend to idle path. + */ +static u32 cros_ec_gpe_handler(acpi_handle gpe_device, u32 gpe_number, + void *data) +{ + return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE; +} + +/* + * Get ACPI GPE for LID0 device. + */ +static int cros_ec_get_ec_wake_gpe(struct device *dev) +{ + struct acpi_device *cros_acpi_dev; + struct acpi_device *adev; + acpi_handle handle; + acpi_status status; + int ret; + + cros_acpi_dev = ACPI_COMPANION(dev); + + if (!cros_acpi_dev || !cros_acpi_dev->parent || + !cros_acpi_dev->parent->handle) + return -EINVAL; + + status = acpi_get_handle(cros_acpi_dev->parent->handle, ACPI_LID_DEVICE, + &handle); + if (ACPI_FAILURE(status)) + return -EINVAL; + + ret = acpi_bus_get_device(handle, &adev); + if (ret) + return ret; + + return adev->wakeup.gpe_number; +} + +int cros_ec_acpi_install_gpe_handler(struct device *dev) +{ + acpi_status status; + + ec_wake_gpe = cros_ec_get_ec_wake_gpe(dev); + + if (ec_wake_gpe < 0) + return ec_wake_gpe; + + status = acpi_install_gpe_handler(NULL, ec_wake_gpe, + ACPI_GPE_EDGE_TRIGGERED, + &cros_ec_gpe_handler, NULL); + if (ACPI_FAILURE(status)) + return -ENODEV; + + dev_info(dev, "Initialized, GPE = 0x%x\n", ec_wake_gpe); + + return 0; +} + +void cros_ec_acpi_remove_gpe_handler(void) +{ + acpi_status status; + + if (ec_wake_gpe < 0) + return; + + status = acpi_remove_gpe_handler(NULL, ec_wake_gpe, + &cros_ec_gpe_handler); + if (ACPI_FAILURE(status)) + pr_err("failed to remove gpe handler\n"); +} + +void cros_ec_acpi_clear_gpe(void) +{ + if (ec_wake_gpe < 0) + return; + + acpi_clear_gpe(NULL, ec_wake_gpe); +} diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index 7a01c94496f1..b3d04de684d4 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -304,4 +304,22 @@ extern struct attribute_group cros_ec_attr_group; extern struct attribute_group cros_ec_lightbar_attr_group; extern struct attribute_group cros_ec_vbc_attr_group; +/* ACPI GPE handler */ +#ifdef CONFIG_ACPI + +int cros_ec_acpi_install_gpe_handler(struct device *dev); +void cros_ec_acpi_remove_gpe_handler(void); +void cros_ec_acpi_clear_gpe(void); + +#else /* CONFIG_ACPI */ + +static inline int cros_ec_acpi_install_gpe_handler(struct device *dev) +{ + return -ENODEV; +} +static inline void cros_ec_acpi_remove_gpe_handler(void) {} +static inline void cros_ec_acpi_clear_gpe(void) {} + +#endif /* CONFIG_ACPI */ + #endif /* __LINUX_MFD_CROS_EC_H */ -- cgit v1.2.3-59-g8ed1b From f36c1f62700ef9b2e913339e3b9e0d5ef8c618b0 Mon Sep 17 00:00:00 2001 From: Priyalee Kushwaha Date: Fri, 3 Feb 2017 18:05:43 +0000 Subject: mfd: lpc_ich: Add PCI ID for Intel Cougar Mountain SoC This patches adds the first minimal support to the upstream Linux tree. Signed-off-by: Priyalee Kushwaha Signed-off-by: Alan Cox Signed-off-by: Lee Jones --- drivers/mfd/lpc_ich.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index d98a5d974092..7c1b0a32310c 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -227,6 +227,7 @@ enum lpc_chipsets { LPC_LEWISBURG, /* Lewisburg */ LPC_9S, /* 9 Series */ LPC_APL, /* Apollo Lake SoC */ + LPC_COUGARMOUNTAIN,/* Cougar Mountain SoC*/ }; static struct lpc_ich_info lpc_chipset_info[] = { @@ -554,6 +555,10 @@ static struct lpc_ich_info lpc_chipset_info[] = { .iTCO_version = 5, .spi_type = INTEL_SPI_BXT, }, + [LPC_COUGARMOUNTAIN] = { + .name = "Cougar Mountain SoC", + .iTCO_version = 3, + }, }; /* @@ -682,6 +687,7 @@ static const struct pci_device_id lpc_ich_ids[] = { { PCI_VDEVICE(INTEL, 0x2917), LPC_ICH9ME}, { PCI_VDEVICE(INTEL, 0x2918), LPC_ICH9}, { PCI_VDEVICE(INTEL, 0x2919), LPC_ICH9M}, + { PCI_VDEVICE(INTEL, 0x2b9c), LPC_COUGARMOUNTAIN}, { PCI_VDEVICE(INTEL, 0x3a14), LPC_ICH10DO}, { PCI_VDEVICE(INTEL, 0x3a16), LPC_ICH10R}, { PCI_VDEVICE(INTEL, 0x3a18), LPC_ICH10}, -- cgit v1.2.3-59-g8ed1b From f8b7ce67070ae6fe643e1319174ffe02e2e51cf7 Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Wed, 1 Feb 2017 17:41:46 +0100 Subject: mfd: stm32-timers: Fix driver removal Add missing of_platform_depopulate() upon driver removal. Signed-off-by: Fabrice Gasnier Signed-off-by: Lee Jones --- drivers/mfd/stm32-timers.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/mfd/stm32-timers.c b/drivers/mfd/stm32-timers.c index 41bd9017f3d0..192cbb60fb09 100644 --- a/drivers/mfd/stm32-timers.c +++ b/drivers/mfd/stm32-timers.c @@ -61,6 +61,13 @@ static int stm32_timers_probe(struct platform_device *pdev) return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); } +static int stm32_timers_remove(struct platform_device *pdev) +{ + of_platform_depopulate(&pdev->dev); + + return 0; +} + static const struct of_device_id stm32_timers_of_match[] = { { .compatible = "st,stm32-timers", }, { /* end node */ }, @@ -69,6 +76,7 @@ MODULE_DEVICE_TABLE(of, stm32_timers_of_match); static struct platform_driver stm32_timers_driver = { .probe = stm32_timers_probe, + .remove = stm32_timers_remove, .driver = { .name = "stm32-timers", .of_match_table = stm32_timers_of_match, -- cgit v1.2.3-59-g8ed1b From ad48ed0c5763dc08931407e455dff5acdbe96e81 Mon Sep 17 00:00:00 2001 From: Adam Ford Date: Sun, 29 Jan 2017 06:40:15 -0600 Subject: mfd: twl4030-power: Fix pmic for boards that need vmmc1 on reboot At least two different omap3630/3730 boards booting from MMC1 fail to reboot if the "ti,twl4030-power-idle-osc-off" or "ti,twl4030-power-idle" compatible flags are set. This patch will keep the vmmc1 powered up during reboot allowing the bootloader to load. Signed-off-by: Adam Ford Signed-off-by: Lee Jones --- drivers/mfd/twl4030-power.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c index 1beb722f6080..e1e69a480c56 100644 --- a/drivers/mfd/twl4030-power.c +++ b/drivers/mfd/twl4030-power.c @@ -701,6 +701,7 @@ static struct twl4030_ins omap3_wrst_seq[] = { TWL_RESOURCE_RESET(RES_MAIN_REF), TWL_RESOURCE_GROUP_RESET(RES_GRP_ALL, RES_TYPE_R0, RES_TYPE2_R2), TWL_RESOURCE_RESET(RES_VUSB_3V1), + TWL_RESOURCE_RESET(RES_VMMC1), TWL_RESOURCE_GROUP_RESET(RES_GRP_ALL, RES_TYPE_R0, RES_TYPE2_R1), TWL_RESOURCE_GROUP_RESET(RES_GRP_RC, RES_TYPE_ALL, RES_TYPE2_R0), TWL_RESOURCE_ON(RES_RESET), -- cgit v1.2.3-59-g8ed1b From 118f6523793d99b3634455e9721836ea5e0592b9 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Thu, 2 Feb 2017 21:59:35 +0100 Subject: mfd: rtsx: Convert forgotten dev_info() statement to pcr_dbg() It is a debugging statement so make it be issued only then. No functionality change. Signed-off-by: Borislav Petkov Signed-off-by: Lee Jones --- drivers/mfd/rtsx_pcr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 98029ee0959e..850590aac008 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -927,7 +927,7 @@ static irqreturn_t rtsx_pci_isr(int irq, void *dev_id) static int rtsx_pci_acquire_irq(struct rtsx_pcr *pcr) { - dev_info(&(pcr->pci->dev), "%s: pcr->msi_en = %d, pci->irq = %d\n", + pcr_dbg(pcr, "%s: pcr->msi_en = %d, pci->irq = %d\n", __func__, pcr->msi_en, pcr->pci->irq); if (request_irq(pcr->pci->irq, rtsx_pci_isr, -- cgit v1.2.3-59-g8ed1b From ae6816ba0f594edb194d108cfa0dedceb59fb0ff Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Wed, 15 Feb 2017 09:04:32 +0100 Subject: mfd: stm32-timers: Fix max register STM32 timers register bank size is 0x400. Fix regmap max_register. Signed-off-by: Fabrice Gasnier Acked-by: Benjamin Gaignard Signed-off-by: Lee Jones --- drivers/mfd/stm32-timers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/stm32-timers.c b/drivers/mfd/stm32-timers.c index 192cbb60fb09..2182f00db101 100644 --- a/drivers/mfd/stm32-timers.c +++ b/drivers/mfd/stm32-timers.c @@ -15,7 +15,7 @@ static const struct regmap_config stm32_timers_regmap_cfg = { .reg_bits = 32, .val_bits = 32, .reg_stride = sizeof(u32), - .max_register = 0x400, + .max_register = 0x3fc, }; static void stm32_timers_get_arr_size(struct stm32_timers *ddata) -- cgit v1.2.3-59-g8ed1b From 159ee7578a7453f3496268950a78a507cf7ac379 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 21 Feb 2017 13:09:14 +0200 Subject: mfd: intel_soc_pmic_core: Remove unnecessary function Since commit 845c877009cf ("i2c / ACPI: Assign IRQ for devices that have GpioInt automatically") I2C core assigns interrupt line to I2C slave devices with regarding to GpioInt() resources. There is no need to repeat this in the driver. Signed-off-by: Andy Shevchenko Signed-off-by: Lee Jones --- drivers/mfd/intel_soc_pmic_core.c | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/drivers/mfd/intel_soc_pmic_core.c b/drivers/mfd/intel_soc_pmic_core.c index 12d6ebb4ae5d..3b5583586662 100644 --- a/drivers/mfd/intel_soc_pmic_core.c +++ b/drivers/mfd/intel_soc_pmic_core.c @@ -44,22 +44,6 @@ static struct pwm_lookup crc_pwm_lookup[] = { PWM_LOOKUP("crystal_cove_pwm", 0, "0000:00:02.0", "pwm_backlight", 0, PWM_POLARITY_NORMAL), }; -static int intel_soc_pmic_find_gpio_irq(struct device *dev) -{ - struct gpio_desc *desc; - int irq; - - desc = devm_gpiod_get_index(dev, "intel_soc_pmic", 0, GPIOD_IN); - if (IS_ERR(desc)) - return PTR_ERR(desc); - - irq = gpiod_to_irq(desc); - if (irq < 0) - dev_warn(dev, "Can't get irq: %d\n", irq); - - return irq; -} - static int intel_soc_pmic_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *i2c_id) { @@ -68,7 +52,6 @@ static int intel_soc_pmic_i2c_probe(struct i2c_client *i2c, struct intel_soc_pmic_config *config; struct intel_soc_pmic *pmic; int ret; - int irq; id = acpi_match_device(dev->driver->acpi_match_table, dev); if (!id || !id->driver_data) @@ -83,14 +66,7 @@ static int intel_soc_pmic_i2c_probe(struct i2c_client *i2c, dev_set_drvdata(dev, pmic); pmic->regmap = devm_regmap_init_i2c(i2c, config->regmap_config); - - /* - * On some boards the PMIC interrupt may come from a GPIO line. Try to - * lookup the ACPI table for a such connection and setup a GPIO - * interrupt if it exists. Otherwise use the IRQ provided by I2C - */ - irq = intel_soc_pmic_find_gpio_irq(dev); - pmic->irq = (irq < 0) ? i2c->irq : irq; + pmic->irq = i2c->irq; ret = regmap_add_irq_chip(pmic->regmap, pmic->irq, config->irq_flags | IRQF_ONESHOT, -- cgit v1.2.3-59-g8ed1b From 8461cf20d17e0090e9236b73d25b31be4f7fadc5 Mon Sep 17 00:00:00 2001 From: Rask Ingemann Lambertsen Date: Wed, 22 Feb 2017 20:41:02 +0100 Subject: dt-bindings: mfd: axp20x: Add "xpowers,master-mode" property for AXP806 PMICs commit b101829a029a ("mfd: axp20x: Fix AXP806 access errors on cold boot") was intended to fix the case where a board uses an AXP806 in slave mode, but the boot loader leaves it in master mode for lack of AXP806 support. But now the driver breaks on boards where the PMIC is operating in master mode. To let the device tree describe which mode of operation is needed, this patch introduces a new property "xpowers,master-mode". Fixes: 204ae2963e10 ("mfd: axp20x: Add bindings for AXP806 PMIC") Signed-off-by: Rask Ingemann Lambertsen Acked-by: Chen-Yu Tsai Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/axp20x.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/axp20x.txt b/Documentation/devicetree/bindings/mfd/axp20x.txt index 8f3ad9ab4637..b41d2601c6ba 100644 --- a/Documentation/devicetree/bindings/mfd/axp20x.txt +++ b/Documentation/devicetree/bindings/mfd/axp20x.txt @@ -28,6 +28,9 @@ Optional properties: regulator to drive the OTG VBus, rather then as an input pin which signals whether the board is driving OTG VBus or not. +- x-powers,master-mode: Boolean (axp806 only). Set this when the PMIC is + wired for master mode. The default is slave mode. + - -supply: a phandle to the regulator supply node. May be omitted if inputs are unregulated, such as using the IPSOUT output from the PMIC. -- cgit v1.2.3-59-g8ed1b From c0369698e6455c734a772e3acb09cff9a0c8ed9f Mon Sep 17 00:00:00 2001 From: Rask Ingemann Lambertsen Date: Wed, 22 Feb 2017 20:42:02 +0100 Subject: mfd: axp20x: Add support for dts property "xpowers,master-mode" commit b101829a029a ("mfd: axp20x: Fix AXP806 access errors on cold boot") was intended to fix the case where a board uses an AXP806 in slave mode, but the boot loader leaves it in master mode for lack of AXP806 support. But now the driver breaks on boards where the PMIC is operating in master mode. This patch lets the driver use the new device tree property "xpowers,master-mode" to set the correct operating mode for the board. Fixes: 8824ee857348 ("mfd: axp20x: Add support for AXP806 PMIC") Signed-off-by: Rask Ingemann Lambertsen Signed-off-by: Lee Jones --- drivers/mfd/axp20x.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 25115fe2acdf..05129004ed90 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -31,6 +31,7 @@ #define AXP20X_OFF 0x80 +#define AXP806_REG_ADDR_EXT_ADDR_MASTER_MODE 0 #define AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE BIT(4) static const char * const axp20x_model_names[] = { @@ -877,15 +878,19 @@ int axp20x_device_probe(struct axp20x_dev *axp20x) * the these device addressing bits (in the upper 4 bits of the * registers) match. * - * Since we only support an AXP806 chained to an AXP809 in slave - * mode, and there isn't any existing hardware which uses AXP806 - * in master mode, or has 2 AXP806s in the same system, we can - * just program the register address extension to the slave mode - * address. + * By default we support an AXP806 chained to an AXP809 in slave + * mode. Boards which use an AXP806 in master mode can set the + * property "x-powers,master-mode" to override the default. */ - if (axp20x->variant == AXP806_ID) - regmap_write(axp20x->regmap, AXP806_REG_ADDR_EXT, - AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE); + if (axp20x->variant == AXP806_ID) { + if (of_property_read_bool(axp20x->dev->of_node, + "x-powers,master-mode")) + regmap_write(axp20x->regmap, AXP806_REG_ADDR_EXT, + AXP806_REG_ADDR_EXT_ADDR_MASTER_MODE); + else + regmap_write(axp20x->regmap, AXP806_REG_ADDR_EXT, + AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE); + } ret = regmap_add_irq_chip(axp20x->regmap, axp20x->irq, IRQF_ONESHOT | IRQF_SHARED | axp20x->irq_flags, -- cgit v1.2.3-59-g8ed1b From 287cce719d85311f61d1b6b7f7b0d93f7907cd46 Mon Sep 17 00:00:00 2001 From: Milo Kim Date: Tue, 28 Feb 2017 15:45:14 +0900 Subject: dt-bindings: mfd: Add TI LMU device binding information This patch describes overall binding for TI LMU MFD devices. Signed-off-by: Milo Kim Acked-by: Rob Herring Acked-by: Tony Lindgren Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/ti-lmu.txt | 243 +++++++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/ti-lmu.txt diff --git a/Documentation/devicetree/bindings/mfd/ti-lmu.txt b/Documentation/devicetree/bindings/mfd/ti-lmu.txt new file mode 100644 index 000000000000..c885cf89b8ce --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/ti-lmu.txt @@ -0,0 +1,243 @@ +TI LMU (Lighting Management Unit) device tree bindings + +TI LMU driver supports lighting devices below. + + Name Child nodes + ------ --------------------------------- + LM3532 Backlight + LM3631 Backlight and regulator + LM3632 Backlight and regulator + LM3633 Backlight, LED and fault monitor + LM3695 Backlight + LM3697 Backlight and fault monitor + +Required properties: + - compatible: Should be one of: + "ti,lm3532" + "ti,lm3631" + "ti,lm3632" + "ti,lm3633" + "ti,lm3695" + "ti,lm3697" + - reg: I2C slave address. + 0x11 for LM3632 + 0x29 for LM3631 + 0x36 for LM3633, LM3697 + 0x38 for LM3532 + 0x63 for LM3695 + +Optional property: + - enable-gpios: A GPIO specifier for hardware enable pin. + +Required node: + - backlight: All LMU devices have backlight child nodes. + For the properties, please refer to [1]. + +Optional nodes: + - fault-monitor: Hardware fault monitoring driver for LM3633 and LM3697. + Required properties: + - compatible: Should be one of: + "ti,lm3633-fault-monitor" + "ti,lm3697-fault-monitor" + - leds: LED properties for LM3633. Please refer to [2]. + - regulators: Regulator properties for LM3631 and LM3632. + Please refer to [3]. + +[1] ../leds/backlight/ti-lmu-backlight.txt +[2] ../leds/leds-lm3633.txt +[3] ../regulator/lm363x-regulator.txt + +lm3532@38 { + compatible = "ti,lm3532"; + reg = <0x38>; + + enable-gpios = <&pioC 2 GPIO_ACTIVE_HIGH>; + + backlight { + compatible = "ti,lm3532-backlight"; + + lcd { + led-sources = <0 1 2>; + ramp-up-msec = <30>; + ramp-down-msec = <0>; + }; + }; +}; + +lm3631@29 { + compatible = "ti,lm3631"; + reg = <0x29>; + + regulators { + compatible = "ti,lm363x-regulator"; + + vboost { + regulator-name = "lcd_boost"; + regulator-min-microvolt = <4500000>; + regulator-max-microvolt = <6350000>; + regulator-always-on; + }; + + vcont { + regulator-name = "lcd_vcont"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + }; + + voref { + regulator-name = "lcd_voref"; + regulator-min-microvolt = <4000000>; + regulator-max-microvolt = <6000000>; + }; + + vpos { + regulator-name = "lcd_vpos"; + regulator-min-microvolt = <4000000>; + regulator-max-microvolt = <6000000>; + regulator-boot-on; + }; + + vneg { + regulator-name = "lcd_vneg"; + regulator-min-microvolt = <4000000>; + regulator-max-microvolt = <6000000>; + regulator-boot-on; + }; + }; + + backlight { + compatible = "ti,lm3631-backlight"; + + lcd_bl { + led-sources = <0 1>; + ramp-up-msec = <300>; + }; + }; +}; + +lm3632@11 { + compatible = "ti,lm3632"; + reg = <0x11>; + + enable-gpios = <&pioC 2 GPIO_ACTIVE_HIGH>; /* PC2 */ + + regulators { + compatible = "ti,lm363x-regulator"; + + ti,lcm-en1-gpio = <&pioC 0 GPIO_ACTIVE_HIGH>; /* PC0 */ + ti,lcm-en2-gpio = <&pioC 1 GPIO_ACTIVE_HIGH>; /* PC1 */ + + vboost { + regulator-name = "lcd_boost"; + regulator-min-microvolt = <4500000>; + regulator-max-microvolt = <6400000>; + regulator-always-on; + }; + + vpos { + regulator-name = "lcd_vpos"; + regulator-min-microvolt = <4000000>; + regulator-max-microvolt = <6000000>; + }; + + vneg { + regulator-name = "lcd_vneg"; + regulator-min-microvolt = <4000000>; + regulator-max-microvolt = <6000000>; + }; + }; + + backlight { + compatible = "ti,lm3632-backlight"; + + pwms = <&pwm0 0 10000 0>; /* pwm number, period, polarity */ + pwm-names = "lmu-backlight"; + + lcd { + led-sources = <0 1>; + pwm-period = <10000>; + }; + }; +}; + +lm3633@36 { + compatible = "ti,lm3633"; + reg = <0x36>; + + enable-gpios = <&pioC 2 GPIO_ACTIVE_HIGH>; + + backlight { + compatible = "ti,lm3633-backlight"; + + main { + label = "main_lcd"; + led-sources = <1 2>; + ramp-up-msec = <500>; + ramp-down-msec = <500>; + }; + + front { + label = "front_lcd"; + led-sources = <0>; + ramp-up-msec = <1000>; + ramp-down-msec = <0>; + }; + }; + + leds { + compatible = "ti,lm3633-leds"; + + chan1 { + label = "status"; + led-sources = <1>; + led-max-microamp = <6000>; + }; + + chan345 { + label = "rgb"; + led-sources = <3 4 5>; + led-max-microamp = <10000>; + }; + }; + + fault-monitor { + compatible = "ti,lm3633-fault-monitor"; + }; +}; + +lm3695@63 { + compatible = "ti,lm3695"; + reg = <0x63>; + + enable-gpios = <&pioC 2 GPIO_ACTIVE_HIGH>; + + backlight { + compatible = "ti,lm3695-backlight"; + + lcd { + label = "bl"; + led-sources = <0 1>; + }; + }; +}; + +lm3697@36 { + compatible = "ti,lm3697"; + reg = <0x36>; + + enable-gpios = <&pioC 2 GPIO_ACTIVE_HIGH>; + + backlight { + compatible = "ti,lm3697-backlight"; + + lcd { + led-sources = <0 1 2>; + ramp-up-msec = <200>; + ramp-down-msec = <200>; + }; + }; + + fault-monitor { + compatible = "ti,lm3697-fault-monitor"; + }; +}; -- cgit v1.2.3-59-g8ed1b From d5aa11bfe9cebb4a3912b11748fd84aa15454229 Mon Sep 17 00:00:00 2001 From: Milo Kim Date: Tue, 28 Feb 2017 15:45:15 +0900 Subject: mfd: Add TI LMU driver TI LMU (Lighting Management Unit) driver supports lighting devices below. LM3532, LM3631, LM3632, LM3633, LM3695 and LM3697. LMU devices have common features. - I2C interface for accessing device registers - Hardware enable pin control - Backlight brightness control - Notifier for hardware fault monitoring - Regulators for LCD display bias It contains fault monitor, backlight, LED and regulator driver. LMU fault monitor ----------------- LM3633 and LM3697 provide hardware monitoring feature. It enables open or short circuit detection. After monitoring is done, each device should be re-initialized. Notifier is used for this case. Separate patch for 'ti-lmu-fault-monitor' will be sent later. Backlight --------- It's handled by TI LMU backlight consolidated driver and chip dependent data. Separate patchset will be sent later. LED indicator ------------- LM3633 has 6 indicator LEDs. Programmable dimming pattern is also supported. Separate patch for 'leds-lm3633' will be sent later. Regulator --------- LM3631 has 5 regulators for the display bias. LM3632 supports 3 regulators. One consolidated driver enables it. The lm363x regulator driver is already upstreamed. Signed-off-by: Milo Kim Tested-by: Tony Lindgren Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 12 ++ drivers/mfd/Makefile | 2 + drivers/mfd/ti-lmu.c | 259 +++++++++++++++++++++++++++++++++ include/linux/mfd/ti-lmu-register.h | 280 ++++++++++++++++++++++++++++++++++++ include/linux/mfd/ti-lmu.h | 87 +++++++++++ 5 files changed, 640 insertions(+) create mode 100644 drivers/mfd/ti-lmu.c create mode 100644 include/linux/mfd/ti-lmu-register.h create mode 100644 include/linux/mfd/ti-lmu.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 8bbc91b5186e..add0c35c38c5 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1181,6 +1181,18 @@ config MFD_LP8788 TI LP8788 PMU supports regulators, battery charger, RTC, ADC, backlight driver and current sinks. +config MFD_TI_LMU + tristate "TI Lighting Management Unit driver" + depends on I2C + select MFD_CORE + select REGMAP_I2C + help + Say yes here to enable support for TI LMU chips. + + TI LMU MFD supports LM3532, LM3631, LM3632, LM3633, LM3695 and LM3697. + It consists of backlight, LED and regulator driver. + It provides consistent device controls for lighting functions. + config MFD_OMAP_USB_HOST bool "TI OMAP USBHS core and TLL driver" depends on USB_EHCI_HCD_OMAP || USB_OHCI_HCD_OMAP3 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index e314ba1058dc..294a0525aeb1 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -127,6 +127,8 @@ obj-$(CONFIG_MFD_AXP20X_RSB) += axp20x-rsb.o obj-$(CONFIG_MFD_LP3943) += lp3943.o obj-$(CONFIG_MFD_LP8788) += lp8788.o lp8788-irq.o +obj-$(CONFIG_MFD_TI_LMU) += ti-lmu.o + da9055-objs := da9055-core.o da9055-i2c.o obj-$(CONFIG_MFD_DA9055) += da9055.o obj-$(CONFIG_MFD_DA9062) += da9062-core.o diff --git a/drivers/mfd/ti-lmu.c b/drivers/mfd/ti-lmu.c new file mode 100644 index 000000000000..cfb411cde51c --- /dev/null +++ b/drivers/mfd/ti-lmu.c @@ -0,0 +1,259 @@ +/* + * TI LMU (Lighting Management Unit) Core Driver + * + * Copyright 2017 Texas Instruments + * + * Author: Milo Kim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ti_lmu_data { + struct mfd_cell *cells; + int num_cells; + unsigned int max_register; +}; + +static int ti_lmu_enable_hw(struct ti_lmu *lmu, enum ti_lmu_id id) +{ + int ret; + + if (gpio_is_valid(lmu->en_gpio)) { + ret = devm_gpio_request_one(lmu->dev, lmu->en_gpio, + GPIOF_OUT_INIT_HIGH, "lmu_hwen"); + if (ret) { + dev_err(lmu->dev, "Can not request enable GPIO: %d\n", + ret); + return ret; + } + } + + /* Delay about 1ms after HW enable pin control */ + usleep_range(1000, 1500); + + /* LM3631 has additional power up sequence - enable LCD_EN bit. */ + if (id == LM3631) { + return regmap_update_bits(lmu->regmap, LM3631_REG_DEVCTRL, + LM3631_LCD_EN_MASK, + LM3631_LCD_EN_MASK); + } + + return 0; +} + +static void ti_lmu_disable_hw(struct ti_lmu *lmu) +{ + if (gpio_is_valid(lmu->en_gpio)) + gpio_set_value(lmu->en_gpio, 0); +} + +static struct mfd_cell lm3532_devices[] = { + { + .name = "ti-lmu-backlight", + .id = LM3532, + .of_compatible = "ti,lm3532-backlight", + }, +}; + +#define LM363X_REGULATOR(_id) \ +{ \ + .name = "lm363x-regulator", \ + .id = _id, \ + .of_compatible = "ti,lm363x-regulator", \ +} \ + +static struct mfd_cell lm3631_devices[] = { + LM363X_REGULATOR(LM3631_BOOST), + LM363X_REGULATOR(LM3631_LDO_CONT), + LM363X_REGULATOR(LM3631_LDO_OREF), + LM363X_REGULATOR(LM3631_LDO_POS), + LM363X_REGULATOR(LM3631_LDO_NEG), + { + .name = "ti-lmu-backlight", + .id = LM3631, + .of_compatible = "ti,lm3631-backlight", + }, +}; + +static struct mfd_cell lm3632_devices[] = { + LM363X_REGULATOR(LM3632_BOOST), + LM363X_REGULATOR(LM3632_LDO_POS), + LM363X_REGULATOR(LM3632_LDO_NEG), + { + .name = "ti-lmu-backlight", + .id = LM3632, + .of_compatible = "ti,lm3632-backlight", + }, +}; + +static struct mfd_cell lm3633_devices[] = { + { + .name = "ti-lmu-backlight", + .id = LM3633, + .of_compatible = "ti,lm3633-backlight", + }, + { + .name = "lm3633-leds", + .of_compatible = "ti,lm3633-leds", + }, + /* Monitoring driver for open/short circuit detection */ + { + .name = "ti-lmu-fault-monitor", + .id = LM3633, + .of_compatible = "ti,lm3633-fault-monitor", + }, +}; + +static struct mfd_cell lm3695_devices[] = { + { + .name = "ti-lmu-backlight", + .id = LM3695, + .of_compatible = "ti,lm3695-backlight", + }, +}; + +static struct mfd_cell lm3697_devices[] = { + { + .name = "ti-lmu-backlight", + .id = LM3697, + .of_compatible = "ti,lm3697-backlight", + }, + /* Monitoring driver for open/short circuit detection */ + { + .name = "ti-lmu-fault-monitor", + .id = LM3697, + .of_compatible = "ti,lm3697-fault-monitor", + }, +}; + +#define TI_LMU_DATA(chip, max_reg) \ +static const struct ti_lmu_data chip##_data = \ +{ \ + .cells = chip##_devices, \ + .num_cells = ARRAY_SIZE(chip##_devices),\ + .max_register = max_reg, \ +} \ + +TI_LMU_DATA(lm3532, LM3532_MAX_REG); +TI_LMU_DATA(lm3631, LM3631_MAX_REG); +TI_LMU_DATA(lm3632, LM3632_MAX_REG); +TI_LMU_DATA(lm3633, LM3633_MAX_REG); +TI_LMU_DATA(lm3695, LM3695_MAX_REG); +TI_LMU_DATA(lm3697, LM3697_MAX_REG); + +static const struct of_device_id ti_lmu_of_match[] = { + { .compatible = "ti,lm3532", .data = &lm3532_data }, + { .compatible = "ti,lm3631", .data = &lm3631_data }, + { .compatible = "ti,lm3632", .data = &lm3632_data }, + { .compatible = "ti,lm3633", .data = &lm3633_data }, + { .compatible = "ti,lm3695", .data = &lm3695_data }, + { .compatible = "ti,lm3697", .data = &lm3697_data }, + { } +}; +MODULE_DEVICE_TABLE(of, ti_lmu_of_match); + +static int ti_lmu_probe(struct i2c_client *cl, const struct i2c_device_id *id) +{ + struct device *dev = &cl->dev; + const struct of_device_id *match; + const struct ti_lmu_data *data; + struct regmap_config regmap_cfg; + struct ti_lmu *lmu; + int ret; + + match = of_match_device(ti_lmu_of_match, dev); + if (!match) + return -ENODEV; + /* + * Get device specific data from of_match table. + * This data is defined by using TI_LMU_DATA() macro. + */ + data = (struct ti_lmu_data *)match->data; + + lmu = devm_kzalloc(dev, sizeof(*lmu), GFP_KERNEL); + if (!lmu) + return -ENOMEM; + + lmu->dev = &cl->dev; + + /* Setup regmap */ + memset(®map_cfg, 0, sizeof(struct regmap_config)); + regmap_cfg.reg_bits = 8; + regmap_cfg.val_bits = 8; + regmap_cfg.name = id->name; + regmap_cfg.max_register = data->max_register; + + lmu->regmap = devm_regmap_init_i2c(cl, ®map_cfg); + if (IS_ERR(lmu->regmap)) + return PTR_ERR(lmu->regmap); + + /* HW enable pin control and additional power up sequence if required */ + lmu->en_gpio = of_get_named_gpio(dev->of_node, "enable-gpios", 0); + ret = ti_lmu_enable_hw(lmu, id->driver_data); + if (ret) + return ret; + + /* + * Fault circuit(open/short) can be detected by ti-lmu-fault-monitor. + * After fault detection is done, some devices should re-initialize + * configuration. The notifier enables such kind of handling. + */ + BLOCKING_INIT_NOTIFIER_HEAD(&lmu->notifier); + + i2c_set_clientdata(cl, lmu); + + return mfd_add_devices(lmu->dev, 0, data->cells, + data->num_cells, NULL, 0, NULL); +} + +static int ti_lmu_remove(struct i2c_client *cl) +{ + struct ti_lmu *lmu = i2c_get_clientdata(cl); + + ti_lmu_disable_hw(lmu); + mfd_remove_devices(lmu->dev); + return 0; +} + +static const struct i2c_device_id ti_lmu_ids[] = { + { "lm3532", LM3532 }, + { "lm3631", LM3631 }, + { "lm3632", LM3632 }, + { "lm3633", LM3633 }, + { "lm3695", LM3695 }, + { "lm3697", LM3697 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ti_lmu_ids); + +static struct i2c_driver ti_lmu_driver = { + .probe = ti_lmu_probe, + .remove = ti_lmu_remove, + .driver = { + .name = "ti-lmu", + .of_match_table = ti_lmu_of_match, + }, + .id_table = ti_lmu_ids, +}; + +module_i2c_driver(ti_lmu_driver); + +MODULE_DESCRIPTION("TI LMU MFD Core Driver"); +MODULE_AUTHOR("Milo Kim"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/ti-lmu-register.h b/include/linux/mfd/ti-lmu-register.h new file mode 100644 index 000000000000..2125c7c02818 --- /dev/null +++ b/include/linux/mfd/ti-lmu-register.h @@ -0,0 +1,280 @@ +/* + * TI LMU (Lighting Management Unit) Device Register Map + * + * Copyright 2017 Texas Instruments + * + * Author: Milo Kim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __MFD_TI_LMU_REGISTER_H__ +#define __MFD_TI_LMU_REGISTER_H__ + +#include + +/* LM3532 */ +#define LM3532_REG_OUTPUT_CFG 0x10 +#define LM3532_ILED1_CFG_MASK 0x03 +#define LM3532_ILED2_CFG_MASK 0x0C +#define LM3532_ILED3_CFG_MASK 0x30 +#define LM3532_ILED1_CFG_SHIFT 0 +#define LM3532_ILED2_CFG_SHIFT 2 +#define LM3532_ILED3_CFG_SHIFT 4 + +#define LM3532_REG_RAMPUP 0x12 +#define LM3532_REG_RAMPDN LM3532_REG_RAMPUP +#define LM3532_RAMPUP_MASK 0x07 +#define LM3532_RAMPUP_SHIFT 0 +#define LM3532_RAMPDN_MASK 0x38 +#define LM3532_RAMPDN_SHIFT 3 + +#define LM3532_REG_ENABLE 0x1D + +#define LM3532_REG_PWM_A_CFG 0x13 +#define LM3532_PWM_A_MASK 0x05 /* zone 0 */ +#define LM3532_PWM_ZONE_0 BIT(2) + +#define LM3532_REG_PWM_B_CFG 0x14 +#define LM3532_PWM_B_MASK 0x09 /* zone 1 */ +#define LM3532_PWM_ZONE_1 BIT(3) + +#define LM3532_REG_PWM_C_CFG 0x15 +#define LM3532_PWM_C_MASK 0x11 /* zone 2 */ +#define LM3532_PWM_ZONE_2 BIT(4) + +#define LM3532_REG_ZONE_CFG_A 0x16 +#define LM3532_REG_ZONE_CFG_B 0x18 +#define LM3532_REG_ZONE_CFG_C 0x1A +#define LM3532_ZONE_MASK (BIT(2) | BIT(3) | BIT(4)) +#define LM3532_ZONE_0 0 +#define LM3532_ZONE_1 BIT(2) +#define LM3532_ZONE_2 BIT(3) + +#define LM3532_REG_BRT_A 0x70 /* zone 0 */ +#define LM3532_REG_BRT_B 0x76 /* zone 1 */ +#define LM3532_REG_BRT_C 0x7C /* zone 2 */ + +#define LM3532_MAX_REG 0x7E + +/* LM3631 */ +#define LM3631_REG_DEVCTRL 0x00 +#define LM3631_LCD_EN_MASK BIT(1) +#define LM3631_BL_EN_MASK BIT(0) + +#define LM3631_REG_BRT_LSB 0x01 +#define LM3631_REG_BRT_MSB 0x02 + +#define LM3631_REG_BL_CFG 0x06 +#define LM3631_BL_CHANNEL_MASK BIT(3) +#define LM3631_BL_DUAL_CHANNEL 0 +#define LM3631_BL_SINGLE_CHANNEL BIT(3) +#define LM3631_MAP_MASK BIT(5) +#define LM3631_EXPONENTIAL_MAP 0 + +#define LM3631_REG_BRT_MODE 0x08 +#define LM3631_MODE_MASK (BIT(1) | BIT(2) | BIT(3)) +#define LM3631_DEFAULT_MODE (BIT(1) | BIT(3)) + +#define LM3631_REG_SLOPE 0x09 +#define LM3631_SLOPE_MASK 0xF0 +#define LM3631_SLOPE_SHIFT 4 + +#define LM3631_REG_LDO_CTRL1 0x0A +#define LM3631_EN_OREF_MASK BIT(0) +#define LM3631_EN_VNEG_MASK BIT(1) +#define LM3631_EN_VPOS_MASK BIT(2) + +#define LM3631_REG_LDO_CTRL2 0x0B +#define LM3631_EN_CONT_MASK BIT(0) + +#define LM3631_REG_VOUT_CONT 0x0C +#define LM3631_VOUT_CONT_MASK (BIT(6) | BIT(7)) + +#define LM3631_REG_VOUT_BOOST 0x0C +#define LM3631_REG_VOUT_POS 0x0D +#define LM3631_REG_VOUT_NEG 0x0E +#define LM3631_REG_VOUT_OREF 0x0F +#define LM3631_VOUT_MASK 0x3F + +#define LM3631_REG_ENTIME_VCONT 0x0B +#define LM3631_ENTIME_CONT_MASK 0x70 + +#define LM3631_REG_ENTIME_VOREF 0x0F +#define LM3631_REG_ENTIME_VPOS 0x10 +#define LM3631_REG_ENTIME_VNEG 0x11 +#define LM3631_ENTIME_MASK 0xF0 +#define LM3631_ENTIME_SHIFT 4 + +#define LM3631_MAX_REG 0x16 + +/* LM3632 */ +#define LM3632_REG_CONFIG1 0x02 +#define LM3632_OVP_MASK (BIT(5) | BIT(6) | BIT(7)) +#define LM3632_OVP_25V BIT(6) + +#define LM3632_REG_CONFIG2 0x03 +#define LM3632_SWFREQ_MASK BIT(7) +#define LM3632_SWFREQ_1MHZ BIT(7) + +#define LM3632_REG_BRT_LSB 0x04 +#define LM3632_REG_BRT_MSB 0x05 + +#define LM3632_REG_IO_CTRL 0x09 +#define LM3632_PWM_MASK BIT(6) +#define LM3632_I2C_MODE 0 +#define LM3632_PWM_MODE BIT(6) + +#define LM3632_REG_ENABLE 0x0A +#define LM3632_BL_EN_MASK BIT(0) +#define LM3632_BL_CHANNEL_MASK (BIT(3) | BIT(4)) +#define LM3632_BL_SINGLE_CHANNEL BIT(4) +#define LM3632_BL_DUAL_CHANNEL BIT(3) + +#define LM3632_REG_BIAS_CONFIG 0x0C +#define LM3632_EXT_EN_MASK BIT(0) +#define LM3632_EN_VNEG_MASK BIT(1) +#define LM3632_EN_VPOS_MASK BIT(2) + +#define LM3632_REG_VOUT_BOOST 0x0D +#define LM3632_REG_VOUT_POS 0x0E +#define LM3632_REG_VOUT_NEG 0x0F +#define LM3632_VOUT_MASK 0x3F + +#define LM3632_MAX_REG 0x10 + +/* LM3633 */ +#define LM3633_REG_HVLED_OUTPUT_CFG 0x10 +#define LM3633_HVLED1_CFG_MASK BIT(0) +#define LM3633_HVLED2_CFG_MASK BIT(1) +#define LM3633_HVLED3_CFG_MASK BIT(2) +#define LM3633_HVLED1_CFG_SHIFT 0 +#define LM3633_HVLED2_CFG_SHIFT 1 +#define LM3633_HVLED3_CFG_SHIFT 2 + +#define LM3633_REG_BANK_SEL 0x11 + +#define LM3633_REG_BL0_RAMP 0x12 +#define LM3633_REG_BL1_RAMP 0x13 +#define LM3633_BL_RAMPUP_MASK 0xF0 +#define LM3633_BL_RAMPUP_SHIFT 4 +#define LM3633_BL_RAMPDN_MASK 0x0F +#define LM3633_BL_RAMPDN_SHIFT 0 + +#define LM3633_REG_BL_RAMP_CONF 0x1B +#define LM3633_BL_RAMP_MASK 0x0F +#define LM3633_BL_RAMP_EACH 0x05 + +#define LM3633_REG_PTN0_RAMP 0x1C +#define LM3633_REG_PTN1_RAMP 0x1D +#define LM3633_PTN_RAMPUP_MASK 0x70 +#define LM3633_PTN_RAMPUP_SHIFT 4 +#define LM3633_PTN_RAMPDN_MASK 0x07 +#define LM3633_PTN_RAMPDN_SHIFT 0 + +#define LM3633_REG_LED_MAPPING_MODE 0x1F +#define LM3633_LED_EXPONENTIAL BIT(1) + +#define LM3633_REG_IMAX_HVLED_A 0x20 +#define LM3633_REG_IMAX_HVLED_B 0x21 +#define LM3633_REG_IMAX_LVLED_BASE 0x22 + +#define LM3633_REG_BL_FEEDBACK_ENABLE 0x28 + +#define LM3633_REG_ENABLE 0x2B +#define LM3633_LED_BANK_OFFSET 2 + +#define LM3633_REG_PATTERN 0x2C + +#define LM3633_REG_BOOST_CFG 0x2D +#define LM3633_OVP_MASK (BIT(1) | BIT(2)) +#define LM3633_OVP_40V 0x6 + +#define LM3633_REG_PWM_CFG 0x2F +#define LM3633_PWM_A_MASK BIT(0) +#define LM3633_PWM_B_MASK BIT(1) + +#define LM3633_REG_BRT_HVLED_A_LSB 0x40 +#define LM3633_REG_BRT_HVLED_A_MSB 0x41 +#define LM3633_REG_BRT_HVLED_B_LSB 0x42 +#define LM3633_REG_BRT_HVLED_B_MSB 0x43 + +#define LM3633_REG_BRT_LVLED_BASE 0x44 + +#define LM3633_REG_PTN_DELAY 0x50 + +#define LM3633_REG_PTN_LOWTIME 0x51 + +#define LM3633_REG_PTN_HIGHTIME 0x52 + +#define LM3633_REG_PTN_LOWBRT 0x53 + +#define LM3633_REG_PTN_HIGHBRT LM3633_REG_BRT_LVLED_BASE + +#define LM3633_REG_BL_OPEN_FAULT_STATUS 0xB0 + +#define LM3633_REG_BL_SHORT_FAULT_STATUS 0xB2 + +#define LM3633_REG_MONITOR_ENABLE 0xB4 + +#define LM3633_MAX_REG 0xB4 + +/* LM3695 */ +#define LM3695_REG_GP 0x10 +#define LM3695_BL_CHANNEL_MASK BIT(3) +#define LM3695_BL_DUAL_CHANNEL 0 +#define LM3695_BL_SINGLE_CHANNEL BIT(3) +#define LM3695_BRT_RW_MASK BIT(2) +#define LM3695_BL_EN_MASK BIT(0) + +#define LM3695_REG_BRT_LSB 0x13 +#define LM3695_REG_BRT_MSB 0x14 + +#define LM3695_MAX_REG 0x14 + +/* LM3697 */ +#define LM3697_REG_HVLED_OUTPUT_CFG 0x10 +#define LM3697_HVLED1_CFG_MASK BIT(0) +#define LM3697_HVLED2_CFG_MASK BIT(1) +#define LM3697_HVLED3_CFG_MASK BIT(2) +#define LM3697_HVLED1_CFG_SHIFT 0 +#define LM3697_HVLED2_CFG_SHIFT 1 +#define LM3697_HVLED3_CFG_SHIFT 2 + +#define LM3697_REG_BL0_RAMP 0x11 +#define LM3697_REG_BL1_RAMP 0x12 +#define LM3697_RAMPUP_MASK 0xF0 +#define LM3697_RAMPUP_SHIFT 4 +#define LM3697_RAMPDN_MASK 0x0F +#define LM3697_RAMPDN_SHIFT 0 + +#define LM3697_REG_RAMP_CONF 0x14 +#define LM3697_RAMP_MASK 0x0F +#define LM3697_RAMP_EACH 0x05 + +#define LM3697_REG_PWM_CFG 0x1C +#define LM3697_PWM_A_MASK BIT(0) +#define LM3697_PWM_B_MASK BIT(1) + +#define LM3697_REG_IMAX_A 0x17 +#define LM3697_REG_IMAX_B 0x18 + +#define LM3697_REG_FEEDBACK_ENABLE 0x19 + +#define LM3697_REG_BRT_A_LSB 0x20 +#define LM3697_REG_BRT_A_MSB 0x21 +#define LM3697_REG_BRT_B_LSB 0x22 +#define LM3697_REG_BRT_B_MSB 0x23 + +#define LM3697_REG_ENABLE 0x24 + +#define LM3697_REG_OPEN_FAULT_STATUS 0xB0 + +#define LM3697_REG_SHORT_FAULT_STATUS 0xB2 + +#define LM3697_REG_MONITOR_ENABLE 0xB4 + +#define LM3697_MAX_REG 0xB4 +#endif diff --git a/include/linux/mfd/ti-lmu.h b/include/linux/mfd/ti-lmu.h new file mode 100644 index 000000000000..09d5f30384e5 --- /dev/null +++ b/include/linux/mfd/ti-lmu.h @@ -0,0 +1,87 @@ +/* + * TI LMU (Lighting Management Unit) Devices + * + * Copyright 2017 Texas Instruments + * + * Author: Milo Kim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __MFD_TI_LMU_H__ +#define __MFD_TI_LMU_H__ + +#include +#include +#include + +/* Notifier event */ +#define LMU_EVENT_MONITOR_DONE 0x01 + +enum ti_lmu_id { + LM3532, + LM3631, + LM3632, + LM3633, + LM3695, + LM3697, + LMU_MAX_ID, +}; + +enum ti_lmu_max_current { + LMU_IMAX_5mA, + LMU_IMAX_6mA, + LMU_IMAX_7mA = 0x03, + LMU_IMAX_8mA, + LMU_IMAX_9mA, + LMU_IMAX_10mA = 0x07, + LMU_IMAX_11mA, + LMU_IMAX_12mA, + LMU_IMAX_13mA, + LMU_IMAX_14mA, + LMU_IMAX_15mA = 0x0D, + LMU_IMAX_16mA, + LMU_IMAX_17mA, + LMU_IMAX_18mA, + LMU_IMAX_19mA, + LMU_IMAX_20mA = 0x13, + LMU_IMAX_21mA, + LMU_IMAX_22mA, + LMU_IMAX_23mA = 0x17, + LMU_IMAX_24mA, + LMU_IMAX_25mA, + LMU_IMAX_26mA, + LMU_IMAX_27mA = 0x1C, + LMU_IMAX_28mA, + LMU_IMAX_29mA, + LMU_IMAX_30mA, +}; + +enum lm363x_regulator_id { + LM3631_BOOST, /* Boost output */ + LM3631_LDO_CONT, /* Display panel controller */ + LM3631_LDO_OREF, /* Gamma reference */ + LM3631_LDO_POS, /* Positive display bias output */ + LM3631_LDO_NEG, /* Negative display bias output */ + LM3632_BOOST, /* Boost output */ + LM3632_LDO_POS, /* Positive display bias output */ + LM3632_LDO_NEG, /* Negative display bias output */ +}; + +/** + * struct ti_lmu + * + * @dev: Parent device pointer + * @regmap: Used for i2c communcation on accessing registers + * @en_gpio: GPIO for HWEN pin [Optional] + * @notifier: Notifier for reporting hwmon event + */ +struct ti_lmu { + struct device *dev; + struct regmap *regmap; + int en_gpio; + struct blocking_notifier_head notifier; +}; +#endif -- cgit v1.2.3-59-g8ed1b From ca691f7118087a652e4d6c83a30f5ed6d5acbf14 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Wed, 1 Mar 2017 12:58:32 +0100 Subject: mfd: cros ec: spi: Increase wait time to 200ms This is a sucky change to bump up the time we'll wait for the EC. Why is it sucky? If 200ms for a transfer is a common thing it will have a massively bad impact on keyboard responsiveness. It still seems like a good idea to do this, though, because we have a gas gauge that claims that in an extreme case it could stretch the i2c clock for 144ms. It's not a common case so it shouldn't affect responsiveness, but it can happen. It's much better to have a single slow keyboard response than to start returning errors when we don't have to. In newer EC designs we should probably implement a virtual battery to respond to the kernel to insulate the kernel from these types of issues. Signed-off-by: Doug Anderson Signed-off-by: Enric Balletbo i Serra Signed-off-by: Lee Jones --- drivers/mfd/cros_ec_spi.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c index a518832ed5f5..c9714072e224 100644 --- a/drivers/mfd/cros_ec_spi.c +++ b/drivers/mfd/cros_ec_spi.c @@ -45,8 +45,11 @@ * on the other end and need to transfer ~256 bytes, then we need: * 10 us/bit * ~10 bits/byte * ~256 bytes = ~25ms * - * We'll wait 4 times that to handle clock stretching and other - * paranoia. + * We'll wait 8 times that to handle clock stretching and other + * paranoia. Note that some battery gas gauge ICs claim to have a + * clock stretch of 144ms in rare situations. That's incentive for + * not directly passing i2c through, but it's too late for that for + * existing hardware. * * It's pretty unlikely that we'll really see a 249 byte tunnel in * anything other than testing. If this was more common we might @@ -54,7 +57,7 @@ * wait loop. The 'flash write' command would be another candidate * for this, clocking in at 2-3ms. */ -#define EC_MSG_DEADLINE_MS 100 +#define EC_MSG_DEADLINE_MS 200 /* * Time between raising the SPI chip select (for the end of a -- cgit v1.2.3-59-g8ed1b From 8fafcd99bde2db25022a10f3d35759395a7e3d3c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 13 Mar 2017 19:53:01 +0200 Subject: mfd: intel-lpss: Remove left over variable After commit 028af5941dd8 ("mfd: intel-lpss: Pass SDA hold time to I2C host controller driver") the driver still has a non-used variable. Remove it here. Signed-off-by: Andy Shevchenko Acked-by: Mika Westerberg Signed-off-by: Lee Jones --- drivers/mfd/intel-lpss-acpi.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/mfd/intel-lpss-acpi.c b/drivers/mfd/intel-lpss-acpi.c index 6bf8d643d942..7911b0a14a6d 100644 --- a/drivers/mfd/intel-lpss-acpi.c +++ b/drivers/mfd/intel-lpss-acpi.c @@ -22,10 +22,6 @@ #include "intel-lpss.h" -static const struct intel_lpss_platform_info spt_info = { - .clk_rate = 120000000, -}; - static struct property_entry spt_i2c_properties[] = { PROPERTY_ENTRY_U32("i2c-sda-hold-time-ns", 230), { }, -- cgit v1.2.3-59-g8ed1b From dd47e97244709aab3edda281e0876c0b86bbe07c Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Wed, 22 Feb 2017 11:10:18 -0600 Subject: mfd: altr_a10sr: Add Arria10 DevKit Reset Controller Add Peripheral PHY Reset Controller to the Arria10 Development Kit System Resource Chip's MFD. Signed-off-by: Thor Thayer Signed-off-by: Lee Jones --- drivers/mfd/altera-a10sr.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mfd/altera-a10sr.c b/drivers/mfd/altera-a10sr.c index 06e1f7fc5605..96e7d2cb7b89 100644 --- a/drivers/mfd/altera-a10sr.c +++ b/drivers/mfd/altera-a10sr.c @@ -33,6 +33,10 @@ static const struct mfd_cell altr_a10sr_subdev_info[] = { .name = "altr_a10sr_gpio", .of_compatible = "altr,a10sr-gpio", }, + { + .name = "altr_a10sr_reset", + .of_compatible = "altr,a10sr-reset", + }, }; static bool altr_a10sr_reg_readable(struct device *dev, unsigned int reg) -- cgit v1.2.3-59-g8ed1b From 66da627e0cf4e06817e78a6641e39372e20a30f7 Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Wed, 22 Feb 2017 11:10:15 -0600 Subject: dt-bindings: mfd: Add Altera Arria10 SR Reset Controller bindings This patch adds documentation for the Altera A10-SR Reset Controller DT bindings. Signed-off-by: Thor Thayer Acked-by: Rob Herring Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/altera-a10sr.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/altera-a10sr.txt b/Documentation/devicetree/bindings/mfd/altera-a10sr.txt index ea151f295ad7..c8a736554b4b 100644 --- a/Documentation/devicetree/bindings/mfd/altera-a10sr.txt +++ b/Documentation/devicetree/bindings/mfd/altera-a10sr.txt @@ -18,6 +18,7 @@ The A10SR consists of these sub-devices: Device Description ------ ---------- a10sr_gpio GPIO Controller +a10sr_rst Reset Controller Arria10 GPIO Required Properties: @@ -27,6 +28,11 @@ Required Properties: the second cell is used to specify flags. See ../gpio/gpio.txt for more information. +Arria10 Peripheral PHY Reset +Required Properties: +- compatible : Should be "altr,a10sr-reset" +- #reset-cells : Should be one. + Example: resource-manager@0 { @@ -43,4 +49,9 @@ Example: gpio-controller; #gpio-cells = <2>; }; + + a10sr_rst: reset-controller { + compatible = "altr,a10sr-reset"; + #reset-cells = <1>; + }; }; -- cgit v1.2.3-59-g8ed1b From 0ec69542aa7362f7c939c9a33c6f50850d68f7eb Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Wed, 15 Mar 2017 12:05:34 -0500 Subject: mfd: tps65912: Fix variable name for SPI remove The SPI interface is mostly a copy/paste from the I2C interface with some minor renaming for consistency. "Client" is an I2C specific term that was left in the SPI remove path, rename this here. Signed-off-by: Andrew F. Davis Signed-off-by: Lee Jones --- drivers/mfd/tps65912-spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/tps65912-spi.c b/drivers/mfd/tps65912-spi.c index 4aeba9b6942a..3bd75061f777 100644 --- a/drivers/mfd/tps65912-spi.c +++ b/drivers/mfd/tps65912-spi.c @@ -49,9 +49,9 @@ static int tps65912_spi_probe(struct spi_device *spi) return tps65912_device_init(tps); } -static int tps65912_spi_remove(struct spi_device *client) +static int tps65912_spi_remove(struct spi_device *spi) { - struct tps65912 *tps = spi_get_drvdata(client); + struct tps65912 *tps = spi_get_drvdata(spi); return tps65912_device_exit(tps); } -- cgit v1.2.3-59-g8ed1b From ed7311f0d089553f39ff3e1e2d9f55f94324c42f Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Mon, 20 Mar 2017 09:16:45 +0100 Subject: mfd: axp20x: Correct name of temperature data ADC registers The registers 0x56 and 0x57 of AXP22X PMIC store the value of the internal temperature of the PMIC. This patch modifies the name of these registers from AXP22X_PMIC_ADC_H/L to AXP22X_PMIC_TEMP_H/L so their purpose is clearer. Signed-off-by: Quentin Schulz Acked-by: Chen-Yu Tsai Acked-by: Maxime Ripard Signed-off-by: Lee Jones --- drivers/mfd/axp20x.c | 2 +- include/linux/mfd/axp20x.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 05129004ed90..e02edf1a9ad3 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -101,7 +101,7 @@ static const struct regmap_range axp22x_volatile_ranges[] = { regmap_reg_range(AXP20X_PWR_INPUT_STATUS, AXP20X_PWR_OP_MODE), regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IRQ5_STATE), regmap_reg_range(AXP22X_GPIO_STATE, AXP22X_GPIO_STATE), - regmap_reg_range(AXP22X_PMIC_ADC_H, AXP20X_IPSOUT_V_HIGH_L), + regmap_reg_range(AXP22X_PMIC_TEMP_H, AXP20X_IPSOUT_V_HIGH_L), regmap_reg_range(AXP20X_FG_RES, AXP20X_FG_RES), }; diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h index 0d9a1ff38393..dc8798cf2a24 100644 --- a/include/linux/mfd/axp20x.h +++ b/include/linux/mfd/axp20x.h @@ -228,8 +228,8 @@ enum axp20x_variants { #define AXP20X_OCV_MAX 0xf /* AXP22X specific registers */ -#define AXP22X_PMIC_ADC_H 0x56 -#define AXP22X_PMIC_ADC_L 0x57 +#define AXP22X_PMIC_TEMP_H 0x56 +#define AXP22X_PMIC_TEMP_L 0x57 #define AXP22X_TS_ADC_H 0x58 #define AXP22X_TS_ADC_L 0x59 #define AXP22X_BATLOW_THRES1 0xe6 -- cgit v1.2.3-59-g8ed1b From 4d5e5c34a04d2e73f74fd7eae7018ddfba841fc1 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Mon, 20 Mar 2017 09:16:47 +0100 Subject: mfd: axp20x: Add ADC cells for AXP20X and AXP22X PMICs This adds the AXP20X/AXP22x ADCs driver to the mfd cells of the AXP209, AXP221 and AXP223 MFD. Signed-off-by: Quentin Schulz Acked-by: Maxime Ripard Acked-by: Chen-Yu Tsai Signed-off-by: Lee Jones --- drivers/mfd/axp20x.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index e02edf1a9ad3..46bdd239611a 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -589,6 +589,8 @@ static struct mfd_cell axp20x_cells[] = { .resources = axp20x_pek_resources, }, { .name = "axp20x-regulator", + }, { + .name = "axp20x-adc", }, { .name = "axp20x-ac-power-supply", .of_compatible = "x-powers,axp202-ac-power-supply", @@ -609,6 +611,8 @@ static struct mfd_cell axp221_cells[] = { .resources = axp22x_pek_resources, }, { .name = "axp20x-regulator", + }, { + .name = "axp22x-adc" }, { .name = "axp20x-usb-power-supply", .of_compatible = "x-powers,axp221-usb-power-supply", @@ -622,6 +626,8 @@ static struct mfd_cell axp223_cells[] = { .name = "axp20x-pek", .num_resources = ARRAY_SIZE(axp22x_pek_resources), .resources = axp22x_pek_resources, + }, { + .name = "axp22x-adc", }, { .name = "axp20x-regulator", }, { -- cgit v1.2.3-59-g8ed1b From 95c4f5319817a158c5d9bc8057a366c4758a0dc7 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Mon, 20 Mar 2017 09:16:48 +0100 Subject: mfd: axp20x: Add AC power supply cells for AXP22X PMICs The X-Powers AXP20X and AXP22X PMICs expose the status of AC power supply. This adds the AC power supply driver to the MFD cells of the AXP22X PMICs. Signed-off-by: Quentin Schulz Acked-by: Maxime Ripard Acked-By: Sebastian Reichel Acked-by: Chen-Yu Tsai Signed-off-by: Lee Jones --- drivers/mfd/axp20x.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 46bdd239611a..37643b1b7a43 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -613,6 +613,11 @@ static struct mfd_cell axp221_cells[] = { .name = "axp20x-regulator", }, { .name = "axp22x-adc" + }, { + .name = "axp20x-ac-power-supply", + .of_compatible = "x-powers,axp221-ac-power-supply", + .num_resources = ARRAY_SIZE(axp20x_ac_power_supply_resources), + .resources = axp20x_ac_power_supply_resources, }, { .name = "axp20x-usb-power-supply", .of_compatible = "x-powers,axp221-usb-power-supply", @@ -630,6 +635,11 @@ static struct mfd_cell axp223_cells[] = { .name = "axp22x-adc", }, { .name = "axp20x-regulator", + }, { + .name = "axp20x-ac-power-supply", + .of_compatible = "x-powers,axp221-ac-power-supply", + .num_resources = ARRAY_SIZE(axp20x_ac_power_supply_resources), + .resources = axp20x_ac_power_supply_resources, }, { .name = "axp20x-usb-power-supply", .of_compatible = "x-powers,axp223-usb-power-supply", -- cgit v1.2.3-59-g8ed1b From 976023701d1f1fda5e19983f38113bd67e881f64 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Mon, 20 Mar 2017 09:16:53 +0100 Subject: mfd: axp20x: Add CHRG_CTRL1/2/3 to writeable regs for AXP20X/AXP22X The CHRG_CTRL1 and CHRG_CTRL2 registers are made for controlling different battery charging settings such as the constant current charge value. The AXP22X also have a third register CHRG_CTRL3 which has settings for battery charging too. This adds the CHRG_CTRL1, CHRG_CTRL2 and CHRG_CTRL3 registers to the list of writeable registers for AXP20X and AXP22X PMICs. Signed-off-by: Quentin Schulz Acked-by: Maxime Ripard Acked-by: Chen-Yu Tsai Signed-off-by: Lee Jones --- drivers/mfd/axp20x.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 37643b1b7a43..5ba3b04cc9b1 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -68,6 +68,7 @@ static const struct regmap_access_table axp152_volatile_table = { static const struct regmap_range axp20x_writeable_ranges[] = { regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE), + regmap_reg_range(AXP20X_CHRG_CTRL1, AXP20X_CHRG_CTRL2), regmap_reg_range(AXP20X_DCDC_MODE, AXP20X_FG_RES), regmap_reg_range(AXP20X_RDC_H, AXP20X_OCV(AXP20X_OCV_MAX)), }; @@ -94,6 +95,7 @@ static const struct regmap_access_table axp20x_volatile_table = { /* AXP22x ranges are shared with the AXP809, as they cover the same range */ static const struct regmap_range axp22x_writeable_ranges[] = { regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE), + regmap_reg_range(AXP20X_CHRG_CTRL1, AXP22X_CHRG_CTRL3), regmap_reg_range(AXP20X_DCDC_MODE, AXP22X_BATLOW_THRES1), }; -- cgit v1.2.3-59-g8ed1b From 74772a300a2af09a1a94d5658e586f670bdca402 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Mon, 20 Mar 2017 14:47:25 +0800 Subject: dt-bindings: mfd: Add the description for LED as the sub module This patch adds description for LED as the sub-module on MT6397/MT6323 multifunction device. Signed-off-by: Sean Wang Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/mt6397.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mfd/mt6397.txt b/Documentation/devicetree/bindings/mfd/mt6397.txt index c568d52af5af..522a3bbf1bac 100644 --- a/Documentation/devicetree/bindings/mfd/mt6397.txt +++ b/Documentation/devicetree/bindings/mfd/mt6397.txt @@ -6,6 +6,7 @@ MT6397/MT6323 is a multifunction device with the following sub modules: - Audio codec - GPIO - Clock +- LED It is interfaced to host controller using SPI interface by a proprietary hardware called PMIC wrapper or pwrap. MT6397/MT6323 MFD is a child device of pwrap. -- cgit v1.2.3-59-g8ed1b From 040fc9b1df118016bf5410d21feb72e9371aeb11 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Mon, 20 Mar 2017 14:47:27 +0800 Subject: mfd: mt6397: Align the placement at which the mfd_cell of LED is defined Align the placement as which the mfd_cell of LED is defined as the other members done on the structure. Signed-off-by: Sean Wang Signed-off-by: Lee Jones --- drivers/mfd/mt6397-core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c index 8e601c846d08..04a601f6aebe 100644 --- a/drivers/mfd/mt6397-core.c +++ b/drivers/mfd/mt6397-core.c @@ -47,8 +47,7 @@ static const struct mfd_cell mt6323_devs[] = { { .name = "mt6323-regulator", .of_compatible = "mediatek,mt6323-regulator" - }, - { + }, { .name = "mt6323-led", .of_compatible = "mediatek,mt6323-led" }, -- cgit v1.2.3-59-g8ed1b From 81d30eda98fd8e29a6e002f6938cb746f75ca14f Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 16 Feb 2017 23:11:36 -0800 Subject: mfd: Convert remaining uses of pr_warning to pr_warn To enable eventual removal of pr_warning This makes pr_warn use consistent for drivers/mfd Prior to this patch, there were 4 uses of pr_warning and 9 uses of pr_warn in drivers/mfd Signed-off-by: Joe Perches Signed-off-by: Lee Jones --- drivers/mfd/db8500-prcmu.c | 2 +- drivers/mfd/sta2x11-mfd.c | 4 ++-- drivers/mfd/twl4030-power.c | 7 ++----- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index ca38a6a14110..5c739ac752e8 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2377,7 +2377,7 @@ static void ack_dbb_wakeup(void) static inline void print_unknown_header_warning(u8 n, u8 header) { - pr_warning("prcmu: Unknown message header (%d) in mailbox %d.\n", + pr_warn("prcmu: Unknown message header (%d) in mailbox %d\n", header, n); } diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c index 9292202039ee..3aeafa228baf 100644 --- a/drivers/mfd/sta2x11-mfd.c +++ b/drivers/mfd/sta2x11-mfd.c @@ -60,8 +60,8 @@ static struct sta2x11_mfd *sta2x11_mfd_find(struct pci_dev *pdev) struct sta2x11_mfd *mfd; if (!pdev && !list_empty(&sta2x11_mfd_list)) { - pr_warning("%s: Unspecified device, " - "using first instance\n", __func__); + pr_warn("%s: Unspecified device, using first instance\n", + __func__); return list_entry(sta2x11_mfd_list.next, struct sta2x11_mfd, list); } diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c index e1e69a480c56..f4b2c29d77e3 100644 --- a/drivers/mfd/twl4030-power.c +++ b/drivers/mfd/twl4030-power.c @@ -502,9 +502,7 @@ static int load_twl4030_script(const struct twl4030_power_data *pdata, } if (tscript->flags & TWL4030_SLEEP_SCRIPT) { if (!order) - pr_warning("TWL4030: Bad order of scripts (sleep "\ - "script before wakeup) Leads to boot"\ - "failure on some boards\n"); + pr_warn("TWL4030: Bad order of scripts (sleep script before wakeup) Leads to boot failure on some boards\n"); err = twl4030_config_sleep_sequence(address); } out: @@ -930,8 +928,7 @@ static int twl4030_power_probe(struct platform_device *pdev) err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &val, TWL4030_PM_MASTER_CFG_P123_TRANSITION); if (err) { - pr_warning("TWL4030 Unable to read registers\n"); - + pr_warn("TWL4030 Unable to read registers\n"); } else if (!(val & SEQ_OFFSYNC)) { val |= SEQ_OFFSYNC; err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, val, -- cgit v1.2.3-59-g8ed1b From de4ea10ad7bd86445875bab25596016544f827ca Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 15 Mar 2017 14:58:36 +0000 Subject: mfd: arizona: Remove duplicate set of ret variable arizona_poll_reg already returns ETIMEDOUT if we don't see the expected register changes before the time out, so remove pointless local setting of ETIMEDOUT. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/arizona-core.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index b6d4bc63c426..e00f577db830 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -342,10 +342,8 @@ static int arizona_enable_freerun_sysclk(struct arizona *arizona, ret = arizona_poll_reg(arizona, 25, ARIZONA_INTERRUPT_RAW_STATUS_5, ARIZONA_FLL1_CLOCK_OK_STS, ARIZONA_FLL1_CLOCK_OK_STS); - if (ret) { - ret = -ETIMEDOUT; + if (ret) goto err_fll; - } ret = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, 0x0144); if (ret) { @@ -407,11 +405,9 @@ static int wm5102_apply_hardware_patch(struct arizona *arizona) ret = arizona_poll_reg(arizona, 5, ARIZONA_WRITE_SEQUENCER_CTRL_1, ARIZONA_WSEQ_BUSY, 0); - if (ret) { + if (ret) regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0, ARIZONA_WSEQ_ABORT); - ret = -ETIMEDOUT; - } err: err = arizona_disable_freerun_sysclk(arizona, &state); -- cgit v1.2.3-59-g8ed1b From f9657b8f75473ce0488c750677bfef46751dd5e1 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 15 Mar 2017 14:58:37 +0000 Subject: mfd: arizona: Display register addresses in hex Register addresses are normally displayed in hex throughout the Arizona driver. Update the arizona_poll_reg function to follow this convention. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/arizona-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index e00f577db830..4cb34c36a0e5 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -245,7 +245,7 @@ static int arizona_poll_reg(struct arizona *arizona, for (i = 0; i < timeout; i++) { ret = regmap_read(arizona->regmap, reg, &val); if (ret != 0) { - dev_err(arizona->dev, "Failed to read reg %u: %d\n", + dev_err(arizona->dev, "Failed to read reg 0x%x: %d\n", reg, ret); continue; } @@ -256,7 +256,7 @@ static int arizona_poll_reg(struct arizona *arizona, usleep_range(1000, 5000); } - dev_err(arizona->dev, "Polling reg %u timed out: %x\n", reg, val); + dev_err(arizona->dev, "Polling reg 0x%x timed out: %x\n", reg, val); return -ETIMEDOUT; } -- cgit v1.2.3-59-g8ed1b From ef84f885e03763c963dbb5d36389d33ce50576f2 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 15 Mar 2017 14:58:38 +0000 Subject: mfd: arizona: Refactor arizona_poll_reg Currently, we specify the timeout in terms of the number of polls but it is more clear from a user of the functions perspective to specify the timeout directly in milliseconds, as such update the function to these new semantics. Additionally, arizona_poll_reg essentially hard-codes regmap_read_poll_timeout, update the implementation to use regmap_read_poll_timeout. We still keep arizona_poll_reg around as regmap_read_poll_timeout is a macro so rather than expand this for each caller keep it wrapped in arizona_poll_reg. Whilst we are doing this make the timeouts a little more generous as the previous system had a bit more slack as it was done as a delay per iteration of the loop whereas regmap_read_poll_timeout compares ktime's. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/arizona-core.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 4cb34c36a0e5..75488e65cd96 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -235,29 +235,25 @@ static irqreturn_t arizona_overclocked(int irq, void *data) return IRQ_HANDLED; } +#define ARIZONA_REG_POLL_DELAY_US 7500 + static int arizona_poll_reg(struct arizona *arizona, - int timeout, unsigned int reg, + int timeout_ms, unsigned int reg, unsigned int mask, unsigned int target) { unsigned int val = 0; - int ret, i; - - for (i = 0; i < timeout; i++) { - ret = regmap_read(arizona->regmap, reg, &val); - if (ret != 0) { - dev_err(arizona->dev, "Failed to read reg 0x%x: %d\n", - reg, ret); - continue; - } - - if ((val & mask) == target) - return 0; + int ret; - usleep_range(1000, 5000); - } + ret = regmap_read_poll_timeout(arizona->regmap, + ARIZONA_INTERRUPT_RAW_STATUS_5, val, + ((val & mask) == target), + ARIZONA_REG_POLL_DELAY_US, + timeout_ms * 1000); + if (ret) + dev_err(arizona->dev, "Polling reg 0x%x timed out: %x\n", + reg, val); - dev_err(arizona->dev, "Polling reg 0x%x timed out: %x\n", reg, val); - return -ETIMEDOUT; + return ret; } static int arizona_wait_for_boot(struct arizona *arizona) @@ -269,7 +265,7 @@ static int arizona_wait_for_boot(struct arizona *arizona) * we won't race with the interrupt handler as it'll be blocked on * runtime resume. */ - ret = arizona_poll_reg(arizona, 5, ARIZONA_INTERRUPT_RAW_STATUS_5, + ret = arizona_poll_reg(arizona, 30, ARIZONA_INTERRUPT_RAW_STATUS_5, ARIZONA_BOOT_DONE_STS, ARIZONA_BOOT_DONE_STS); if (!ret) @@ -339,7 +335,7 @@ static int arizona_enable_freerun_sysclk(struct arizona *arizona, ret); return ret; } - ret = arizona_poll_reg(arizona, 25, ARIZONA_INTERRUPT_RAW_STATUS_5, + ret = arizona_poll_reg(arizona, 180, ARIZONA_INTERRUPT_RAW_STATUS_5, ARIZONA_FLL1_CLOCK_OK_STS, ARIZONA_FLL1_CLOCK_OK_STS); if (ret) @@ -403,7 +399,7 @@ static int wm5102_apply_hardware_patch(struct arizona *arizona) goto err; } - ret = arizona_poll_reg(arizona, 5, ARIZONA_WRITE_SEQUENCER_CTRL_1, + ret = arizona_poll_reg(arizona, 30, ARIZONA_WRITE_SEQUENCER_CTRL_1, ARIZONA_WSEQ_BUSY, 0); if (ret) regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0, -- cgit v1.2.3-59-g8ed1b From f1e34ad849ad78770af067fd8e409e61b018f9d0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 17 Mar 2017 17:37:14 +0200 Subject: mfd: intel_soc_pmic_bxtwc: Move inclusion to c-file There is no need to include intel_soc_pmic.h into header which doesn't require it. Signed-off-by: Andy Shevchenko Signed-off-by: Lee Jones --- drivers/mfd/intel_soc_pmic_bxtwc.c | 1 + include/linux/mfd/intel_bxtwc.h | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mfd/intel_soc_pmic_bxtwc.c b/drivers/mfd/intel_soc_pmic_bxtwc.c index 699c8c7c9052..c71db687b7ca 100644 --- a/drivers/mfd/intel_soc_pmic_bxtwc.c +++ b/drivers/mfd/intel_soc_pmic_bxtwc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include /* PMIC device registers */ diff --git a/include/linux/mfd/intel_bxtwc.h b/include/linux/mfd/intel_bxtwc.h index 1a0ee9d6efe9..240d6752ec64 100644 --- a/include/linux/mfd/intel_bxtwc.h +++ b/include/linux/mfd/intel_bxtwc.h @@ -13,8 +13,6 @@ * more details. */ -#include - #ifndef __INTEL_BXTWC_H__ #define __INTEL_BXTWC_H__ -- cgit v1.2.3-59-g8ed1b From 0c227c51b98c03c6e7fb4f342f930cf576292064 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 17 Mar 2017 17:37:15 +0200 Subject: mfd: intel_soc_pmic_bxtwc: Rename header to follow c-file For better understanding of relationship between headers and modules rename: intel_bxtwc.h -> intel_soc_pmic_bxtwc.h While here, remove file name from the file itself. Signed-off-by: Andy Shevchenko Signed-off-by: Lee Jones --- drivers/mfd/intel_soc_pmic_bxtwc.c | 2 +- include/linux/mfd/intel_bxtwc.h | 67 -------------------------------- include/linux/mfd/intel_soc_pmic_bxtwc.h | 67 ++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 68 deletions(-) delete mode 100644 include/linux/mfd/intel_bxtwc.h create mode 100644 include/linux/mfd/intel_soc_pmic_bxtwc.h diff --git a/drivers/mfd/intel_soc_pmic_bxtwc.c b/drivers/mfd/intel_soc_pmic_bxtwc.c index c71db687b7ca..1496a862baa6 100644 --- a/drivers/mfd/intel_soc_pmic_bxtwc.c +++ b/drivers/mfd/intel_soc_pmic_bxtwc.c @@ -20,8 +20,8 @@ #include #include #include -#include #include +#include #include /* PMIC device registers */ diff --git a/include/linux/mfd/intel_bxtwc.h b/include/linux/mfd/intel_bxtwc.h deleted file mode 100644 index 240d6752ec64..000000000000 --- a/include/linux/mfd/intel_bxtwc.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * intel_bxtwc.h - Header file for Intel Broxton Whiskey Cove PMIC - * - * Copyright (C) 2015 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - */ - -#ifndef __INTEL_BXTWC_H__ -#define __INTEL_BXTWC_H__ - -/* BXT WC devices */ -#define BXTWC_DEVICE1_ADDR 0x4E -#define BXTWC_DEVICE2_ADDR 0x4F -#define BXTWC_DEVICE3_ADDR 0x5E - -/* device1 Registers */ -#define BXTWC_CHIPID 0x4E00 -#define BXTWC_CHIPVER 0x4E01 - -#define BXTWC_SCHGRIRQ0_ADDR 0x5E1A -#define BXTWC_CHGRCTRL0_ADDR 0x5E16 -#define BXTWC_CHGRCTRL1_ADDR 0x5E17 -#define BXTWC_CHGRCTRL2_ADDR 0x5E18 -#define BXTWC_CHGRSTATUS_ADDR 0x5E19 -#define BXTWC_THRMBATZONE_ADDR 0x4F22 - -#define BXTWC_USBPATH_ADDR 0x5E19 -#define BXTWC_USBPHYCTRL_ADDR 0x5E07 -#define BXTWC_USBIDCTRL_ADDR 0x5E05 -#define BXTWC_USBIDEN_MASK 0x01 -#define BXTWC_USBIDSTAT_ADDR 0x00FF -#define BXTWC_USBSRCDETSTATUS_ADDR 0x5E29 - -#define BXTWC_DBGUSBBC1_ADDR 0x5FE0 -#define BXTWC_DBGUSBBC2_ADDR 0x5FE1 -#define BXTWC_DBGUSBBCSTAT_ADDR 0x5FE2 - -#define BXTWC_WAKESRC_ADDR 0x4E22 -#define BXTWC_WAKESRC2_ADDR 0x4EE5 -#define BXTWC_CHRTTADDR_ADDR 0x5E22 -#define BXTWC_CHRTTDATA_ADDR 0x5E23 - -#define BXTWC_STHRMIRQ0_ADDR 0x4F19 -#define WC_MTHRMIRQ1_ADDR 0x4E12 -#define WC_STHRMIRQ1_ADDR 0x4F1A -#define WC_STHRMIRQ2_ADDR 0x4F1B - -#define BXTWC_THRMZN0H_ADDR 0x4F44 -#define BXTWC_THRMZN0L_ADDR 0x4F45 -#define BXTWC_THRMZN1H_ADDR 0x4F46 -#define BXTWC_THRMZN1L_ADDR 0x4F47 -#define BXTWC_THRMZN2H_ADDR 0x4F48 -#define BXTWC_THRMZN2L_ADDR 0x4F49 -#define BXTWC_THRMZN3H_ADDR 0x4F4A -#define BXTWC_THRMZN3L_ADDR 0x4F4B -#define BXTWC_THRMZN4H_ADDR 0x4F4C -#define BXTWC_THRMZN4L_ADDR 0x4F4D - -#endif diff --git a/include/linux/mfd/intel_soc_pmic_bxtwc.h b/include/linux/mfd/intel_soc_pmic_bxtwc.h new file mode 100644 index 000000000000..0c351bc85d2d --- /dev/null +++ b/include/linux/mfd/intel_soc_pmic_bxtwc.h @@ -0,0 +1,67 @@ +/* + * Header file for Intel Broxton Whiskey Cove PMIC + * + * Copyright (C) 2015 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef __INTEL_BXTWC_H__ +#define __INTEL_BXTWC_H__ + +/* BXT WC devices */ +#define BXTWC_DEVICE1_ADDR 0x4E +#define BXTWC_DEVICE2_ADDR 0x4F +#define BXTWC_DEVICE3_ADDR 0x5E + +/* device1 Registers */ +#define BXTWC_CHIPID 0x4E00 +#define BXTWC_CHIPVER 0x4E01 + +#define BXTWC_SCHGRIRQ0_ADDR 0x5E1A +#define BXTWC_CHGRCTRL0_ADDR 0x5E16 +#define BXTWC_CHGRCTRL1_ADDR 0x5E17 +#define BXTWC_CHGRCTRL2_ADDR 0x5E18 +#define BXTWC_CHGRSTATUS_ADDR 0x5E19 +#define BXTWC_THRMBATZONE_ADDR 0x4F22 + +#define BXTWC_USBPATH_ADDR 0x5E19 +#define BXTWC_USBPHYCTRL_ADDR 0x5E07 +#define BXTWC_USBIDCTRL_ADDR 0x5E05 +#define BXTWC_USBIDEN_MASK 0x01 +#define BXTWC_USBIDSTAT_ADDR 0x00FF +#define BXTWC_USBSRCDETSTATUS_ADDR 0x5E29 + +#define BXTWC_DBGUSBBC1_ADDR 0x5FE0 +#define BXTWC_DBGUSBBC2_ADDR 0x5FE1 +#define BXTWC_DBGUSBBCSTAT_ADDR 0x5FE2 + +#define BXTWC_WAKESRC_ADDR 0x4E22 +#define BXTWC_WAKESRC2_ADDR 0x4EE5 +#define BXTWC_CHRTTADDR_ADDR 0x5E22 +#define BXTWC_CHRTTDATA_ADDR 0x5E23 + +#define BXTWC_STHRMIRQ0_ADDR 0x4F19 +#define WC_MTHRMIRQ1_ADDR 0x4E12 +#define WC_STHRMIRQ1_ADDR 0x4F1A +#define WC_STHRMIRQ2_ADDR 0x4F1B + +#define BXTWC_THRMZN0H_ADDR 0x4F44 +#define BXTWC_THRMZN0L_ADDR 0x4F45 +#define BXTWC_THRMZN1H_ADDR 0x4F46 +#define BXTWC_THRMZN1L_ADDR 0x4F47 +#define BXTWC_THRMZN2H_ADDR 0x4F48 +#define BXTWC_THRMZN2L_ADDR 0x4F49 +#define BXTWC_THRMZN3H_ADDR 0x4F4A +#define BXTWC_THRMZN3L_ADDR 0x4F4B +#define BXTWC_THRMZN4H_ADDR 0x4F4C +#define BXTWC_THRMZN4L_ADDR 0x4F4D + +#endif -- cgit v1.2.3-59-g8ed1b From 93ad4471912029f7519c23da56538a5d54552124 Mon Sep 17 00:00:00 2001 From: Julia Cartwright Date: Tue, 21 Mar 2017 17:43:04 -0500 Subject: mfd: asic3: Make use of raw_spinlock variants The asic3 mfd driver currently implements an irq_chip for handling interrupts; due to how irq_chip handling is done, it's necessary for the irq_chip methods to be invoked from hardirq context, even on a a real-time kernel. Because the spinlock_t type becomes a "sleeping" spinlock w/ RT kernels, it is not suitable to be used with irq_chips. A quick audit of the operations under the lock reveal that they do only minimal, bounded work, and are therefore safe to do under a raw spinlock. Signed-off-by: Julia Cartwright Signed-off-by: Lee Jones --- drivers/mfd/asic3.c | 56 ++++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index 0413c8159551..cf2e25ab2940 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -78,7 +78,7 @@ struct asic3 { unsigned int bus_shift; unsigned int irq_nr; unsigned int irq_base; - spinlock_t lock; + raw_spinlock_t lock; u16 irq_bothedge[4]; struct gpio_chip gpio; struct device *dev; @@ -108,14 +108,14 @@ static void asic3_set_register(struct asic3 *asic, u32 reg, u32 bits, bool set) unsigned long flags; u32 val; - spin_lock_irqsave(&asic->lock, flags); + raw_spin_lock_irqsave(&asic->lock, flags); val = asic3_read_register(asic, reg); if (set) val |= bits; else val &= ~bits; asic3_write_register(asic, reg, val); - spin_unlock_irqrestore(&asic->lock, flags); + raw_spin_unlock_irqrestore(&asic->lock, flags); } /* IRQs */ @@ -129,13 +129,13 @@ static void asic3_irq_flip_edge(struct asic3 *asic, u16 edge; unsigned long flags; - spin_lock_irqsave(&asic->lock, flags); + raw_spin_lock_irqsave(&asic->lock, flags); edge = asic3_read_register(asic, base + ASIC3_GPIO_EDGE_TRIGGER); edge ^= bit; asic3_write_register(asic, base + ASIC3_GPIO_EDGE_TRIGGER, edge); - spin_unlock_irqrestore(&asic->lock, flags); + raw_spin_unlock_irqrestore(&asic->lock, flags); } static void asic3_irq_demux(struct irq_desc *desc) @@ -151,10 +151,10 @@ static void asic3_irq_demux(struct irq_desc *desc) u32 status; int bank; - spin_lock_irqsave(&asic->lock, flags); + raw_spin_lock_irqsave(&asic->lock, flags); status = asic3_read_register(asic, ASIC3_OFFSET(INTR, P_INT_STAT)); - spin_unlock_irqrestore(&asic->lock, flags); + raw_spin_unlock_irqrestore(&asic->lock, flags); /* Check all ten register bits */ if ((status & 0x3ff) == 0) @@ -167,7 +167,7 @@ static void asic3_irq_demux(struct irq_desc *desc) base = ASIC3_GPIO_A_BASE + bank * ASIC3_GPIO_BASE_INCR; - spin_lock_irqsave(&asic->lock, flags); + raw_spin_lock_irqsave(&asic->lock, flags); istat = asic3_read_register(asic, base + ASIC3_GPIO_INT_STATUS); @@ -175,7 +175,7 @@ static void asic3_irq_demux(struct irq_desc *desc) asic3_write_register(asic, base + ASIC3_GPIO_INT_STATUS, 0); - spin_unlock_irqrestore(&asic->lock, flags); + raw_spin_unlock_irqrestore(&asic->lock, flags); for (i = 0; i < ASIC3_GPIOS_PER_BANK; i++) { int bit = (1 << i); @@ -230,11 +230,11 @@ static void asic3_mask_gpio_irq(struct irq_data *data) bank = asic3_irq_to_bank(asic, data->irq); index = asic3_irq_to_index(asic, data->irq); - spin_lock_irqsave(&asic->lock, flags); + raw_spin_lock_irqsave(&asic->lock, flags); val = asic3_read_register(asic, bank + ASIC3_GPIO_MASK); val |= 1 << index; asic3_write_register(asic, bank + ASIC3_GPIO_MASK, val); - spin_unlock_irqrestore(&asic->lock, flags); + raw_spin_unlock_irqrestore(&asic->lock, flags); } static void asic3_mask_irq(struct irq_data *data) @@ -243,7 +243,7 @@ static void asic3_mask_irq(struct irq_data *data) int regval; unsigned long flags; - spin_lock_irqsave(&asic->lock, flags); + raw_spin_lock_irqsave(&asic->lock, flags); regval = asic3_read_register(asic, ASIC3_INTR_BASE + ASIC3_INTR_INT_MASK); @@ -255,7 +255,7 @@ static void asic3_mask_irq(struct irq_data *data) ASIC3_INTR_BASE + ASIC3_INTR_INT_MASK, regval); - spin_unlock_irqrestore(&asic->lock, flags); + raw_spin_unlock_irqrestore(&asic->lock, flags); } static void asic3_unmask_gpio_irq(struct irq_data *data) @@ -267,11 +267,11 @@ static void asic3_unmask_gpio_irq(struct irq_data *data) bank = asic3_irq_to_bank(asic, data->irq); index = asic3_irq_to_index(asic, data->irq); - spin_lock_irqsave(&asic->lock, flags); + raw_spin_lock_irqsave(&asic->lock, flags); val = asic3_read_register(asic, bank + ASIC3_GPIO_MASK); val &= ~(1 << index); asic3_write_register(asic, bank + ASIC3_GPIO_MASK, val); - spin_unlock_irqrestore(&asic->lock, flags); + raw_spin_unlock_irqrestore(&asic->lock, flags); } static void asic3_unmask_irq(struct irq_data *data) @@ -280,7 +280,7 @@ static void asic3_unmask_irq(struct irq_data *data) int regval; unsigned long flags; - spin_lock_irqsave(&asic->lock, flags); + raw_spin_lock_irqsave(&asic->lock, flags); regval = asic3_read_register(asic, ASIC3_INTR_BASE + ASIC3_INTR_INT_MASK); @@ -292,7 +292,7 @@ static void asic3_unmask_irq(struct irq_data *data) ASIC3_INTR_BASE + ASIC3_INTR_INT_MASK, regval); - spin_unlock_irqrestore(&asic->lock, flags); + raw_spin_unlock_irqrestore(&asic->lock, flags); } static int asic3_gpio_irq_type(struct irq_data *data, unsigned int type) @@ -306,7 +306,7 @@ static int asic3_gpio_irq_type(struct irq_data *data, unsigned int type) index = asic3_irq_to_index(asic, data->irq); bit = 1<lock, flags); + raw_spin_lock_irqsave(&asic->lock, flags); level = asic3_read_register(asic, bank + ASIC3_GPIO_LEVEL_TRIGGER); edge = asic3_read_register(asic, @@ -348,7 +348,7 @@ static int asic3_gpio_irq_type(struct irq_data *data, unsigned int type) edge); asic3_write_register(asic, bank + ASIC3_GPIO_TRIGGER_TYPE, trigger); - spin_unlock_irqrestore(&asic->lock, flags); + raw_spin_unlock_irqrestore(&asic->lock, flags); return 0; } @@ -455,7 +455,7 @@ static int asic3_gpio_direction(struct gpio_chip *chip, return -EINVAL; } - spin_lock_irqsave(&asic->lock, flags); + raw_spin_lock_irqsave(&asic->lock, flags); out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_DIRECTION); @@ -467,7 +467,7 @@ static int asic3_gpio_direction(struct gpio_chip *chip, asic3_write_register(asic, gpio_base + ASIC3_GPIO_DIRECTION, out_reg); - spin_unlock_irqrestore(&asic->lock, flags); + raw_spin_unlock_irqrestore(&asic->lock, flags); return 0; @@ -524,7 +524,7 @@ static void asic3_gpio_set(struct gpio_chip *chip, mask = ASIC3_GPIO_TO_MASK(offset); - spin_lock_irqsave(&asic->lock, flags); + raw_spin_lock_irqsave(&asic->lock, flags); out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_OUT); @@ -535,7 +535,7 @@ static void asic3_gpio_set(struct gpio_chip *chip, asic3_write_register(asic, gpio_base + ASIC3_GPIO_OUT, out_reg); - spin_unlock_irqrestore(&asic->lock, flags); + raw_spin_unlock_irqrestore(&asic->lock, flags); } static int asic3_gpio_to_irq(struct gpio_chip *chip, unsigned offset) @@ -611,13 +611,13 @@ static void asic3_clk_enable(struct asic3 *asic, struct asic3_clk *clk) unsigned long flags; u32 cdex; - spin_lock_irqsave(&asic->lock, flags); + raw_spin_lock_irqsave(&asic->lock, flags); if (clk->enabled++ == 0) { cdex = asic3_read_register(asic, ASIC3_OFFSET(CLOCK, CDEX)); cdex |= clk->cdex; asic3_write_register(asic, ASIC3_OFFSET(CLOCK, CDEX), cdex); } - spin_unlock_irqrestore(&asic->lock, flags); + raw_spin_unlock_irqrestore(&asic->lock, flags); } static void asic3_clk_disable(struct asic3 *asic, struct asic3_clk *clk) @@ -627,13 +627,13 @@ static void asic3_clk_disable(struct asic3 *asic, struct asic3_clk *clk) WARN_ON(clk->enabled == 0); - spin_lock_irqsave(&asic->lock, flags); + raw_spin_lock_irqsave(&asic->lock, flags); if (--clk->enabled == 0) { cdex = asic3_read_register(asic, ASIC3_OFFSET(CLOCK, CDEX)); cdex &= ~clk->cdex; asic3_write_register(asic, ASIC3_OFFSET(CLOCK, CDEX), cdex); } - spin_unlock_irqrestore(&asic->lock, flags); + raw_spin_unlock_irqrestore(&asic->lock, flags); } /* MFD cells (SPI, PWM, LED, DS1WM, MMC) */ @@ -963,7 +963,7 @@ static int __init asic3_probe(struct platform_device *pdev) if (!asic) return -ENOMEM; - spin_lock_init(&asic->lock); + raw_spin_lock_init(&asic->lock); platform_set_drvdata(pdev, asic); asic->dev = &pdev->dev; -- cgit v1.2.3-59-g8ed1b From 9fe8c2dfe165f18873042d25ef430e18c97b1dae Mon Sep 17 00:00:00 2001 From: Julia Cartwright Date: Tue, 21 Mar 2017 17:43:05 -0500 Subject: mfd: t7l66xb: Make use of raw_spinlock variants The t7l66xb mfd driver currently implements an irq_chip for handling interrupts; due to how irq_chip handling is done, it's necessary for the irq_chip methods to be invoked from hardirq context, even on a a real-time kernel. Because the spinlock_t type becomes a "sleeping" spinlock w/ RT kernels, it is not suitable to be used with irq_chips. A quick audit of the operations under the lock reveal that they do only minimal, bounded work, and are therefore safe to do under a raw spinlock. Signed-off-by: Julia Cartwright Signed-off-by: Lee Jones --- drivers/mfd/t7l66xb.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/mfd/t7l66xb.c b/drivers/mfd/t7l66xb.c index 94bd89cb1f06..22c811396edc 100644 --- a/drivers/mfd/t7l66xb.c +++ b/drivers/mfd/t7l66xb.c @@ -69,7 +69,7 @@ static const struct resource t7l66xb_mmc_resources[] = { struct t7l66xb { void __iomem *scr; /* Lock to protect registers requiring read/modify/write ops. */ - spinlock_t lock; + raw_spinlock_t lock; struct resource rscr; struct clk *clk48m; @@ -89,13 +89,13 @@ static int t7l66xb_mmc_enable(struct platform_device *mmc) clk_prepare_enable(t7l66xb->clk32k); - spin_lock_irqsave(&t7l66xb->lock, flags); + raw_spin_lock_irqsave(&t7l66xb->lock, flags); dev_ctl = tmio_ioread8(t7l66xb->scr + SCR_DEV_CTL); dev_ctl |= SCR_DEV_CTL_MMC; tmio_iowrite8(dev_ctl, t7l66xb->scr + SCR_DEV_CTL); - spin_unlock_irqrestore(&t7l66xb->lock, flags); + raw_spin_unlock_irqrestore(&t7l66xb->lock, flags); tmio_core_mmc_enable(t7l66xb->scr + 0x200, 0, t7l66xb_mmc_resources[0].start & 0xfffe); @@ -110,13 +110,13 @@ static int t7l66xb_mmc_disable(struct platform_device *mmc) unsigned long flags; u8 dev_ctl; - spin_lock_irqsave(&t7l66xb->lock, flags); + raw_spin_lock_irqsave(&t7l66xb->lock, flags); dev_ctl = tmio_ioread8(t7l66xb->scr + SCR_DEV_CTL); dev_ctl &= ~SCR_DEV_CTL_MMC; tmio_iowrite8(dev_ctl, t7l66xb->scr + SCR_DEV_CTL); - spin_unlock_irqrestore(&t7l66xb->lock, flags); + raw_spin_unlock_irqrestore(&t7l66xb->lock, flags); clk_disable_unprepare(t7l66xb->clk32k); @@ -206,11 +206,11 @@ static void t7l66xb_irq_mask(struct irq_data *data) unsigned long flags; u8 imr; - spin_lock_irqsave(&t7l66xb->lock, flags); + raw_spin_lock_irqsave(&t7l66xb->lock, flags); imr = tmio_ioread8(t7l66xb->scr + SCR_IMR); imr |= 1 << (data->irq - t7l66xb->irq_base); tmio_iowrite8(imr, t7l66xb->scr + SCR_IMR); - spin_unlock_irqrestore(&t7l66xb->lock, flags); + raw_spin_unlock_irqrestore(&t7l66xb->lock, flags); } static void t7l66xb_irq_unmask(struct irq_data *data) @@ -219,11 +219,11 @@ static void t7l66xb_irq_unmask(struct irq_data *data) unsigned long flags; u8 imr; - spin_lock_irqsave(&t7l66xb->lock, flags); + raw_spin_lock_irqsave(&t7l66xb->lock, flags); imr = tmio_ioread8(t7l66xb->scr + SCR_IMR); imr &= ~(1 << (data->irq - t7l66xb->irq_base)); tmio_iowrite8(imr, t7l66xb->scr + SCR_IMR); - spin_unlock_irqrestore(&t7l66xb->lock, flags); + raw_spin_unlock_irqrestore(&t7l66xb->lock, flags); } static struct irq_chip t7l66xb_chip = { @@ -321,7 +321,7 @@ static int t7l66xb_probe(struct platform_device *dev) if (!t7l66xb) return -ENOMEM; - spin_lock_init(&t7l66xb->lock); + raw_spin_lock_init(&t7l66xb->lock); platform_set_drvdata(dev, t7l66xb); -- cgit v1.2.3-59-g8ed1b From f5139c27e734bf8458c0afc60ef86e45517e1126 Mon Sep 17 00:00:00 2001 From: Julia Cartwright Date: Tue, 21 Mar 2017 17:43:06 -0500 Subject: mfd: tc6393xb: Make use of raw_spinlock variants The tc6393xb mfd driver currently implements an irq_chip for handling interrupts; due to how irq_chip handling is done, it's necessary for the irq_chip methods to be invoked from hardirq context, even on a a real-time kernel. Because the spinlock_t type becomes a "sleeping" spinlock w/ RT kernels, it is not suitable to be used with irq_chips. A quick audit of the operations under the lock reveal that they do only minimal, bounded work, and are therefore safe to do under a raw spinlock. Signed-off-by: Julia Cartwright Signed-off-by: Lee Jones --- drivers/mfd/tc6393xb.c | 52 +++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c index d42d322ac7ca..d16e71bd9482 100644 --- a/drivers/mfd/tc6393xb.c +++ b/drivers/mfd/tc6393xb.c @@ -95,7 +95,7 @@ struct tc6393xb { struct clk *clk; /* 3,6 Mhz */ - spinlock_t lock; /* protects RMW cycles */ + raw_spinlock_t lock; /* protects RMW cycles */ struct { u8 fer; @@ -126,13 +126,13 @@ static int tc6393xb_nand_enable(struct platform_device *nand) struct tc6393xb *tc6393xb = platform_get_drvdata(dev); unsigned long flags; - spin_lock_irqsave(&tc6393xb->lock, flags); + raw_spin_lock_irqsave(&tc6393xb->lock, flags); /* SMD buffer on */ dev_dbg(&dev->dev, "SMD buffer on\n"); tmio_iowrite8(0xff, tc6393xb->scr + SCR_GPI_BCR(1)); - spin_unlock_irqrestore(&tc6393xb->lock, flags); + raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); return 0; } @@ -226,7 +226,7 @@ static int tc6393xb_ohci_enable(struct platform_device *dev) u16 ccr; u8 fer; - spin_lock_irqsave(&tc6393xb->lock, flags); + raw_spin_lock_irqsave(&tc6393xb->lock, flags); ccr = tmio_ioread16(tc6393xb->scr + SCR_CCR); ccr |= SCR_CCR_USBCK; @@ -236,7 +236,7 @@ static int tc6393xb_ohci_enable(struct platform_device *dev) fer |= SCR_FER_USBEN; tmio_iowrite8(fer, tc6393xb->scr + SCR_FER); - spin_unlock_irqrestore(&tc6393xb->lock, flags); + raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); return 0; } @@ -248,7 +248,7 @@ static int tc6393xb_ohci_disable(struct platform_device *dev) u16 ccr; u8 fer; - spin_lock_irqsave(&tc6393xb->lock, flags); + raw_spin_lock_irqsave(&tc6393xb->lock, flags); fer = tmio_ioread8(tc6393xb->scr + SCR_FER); fer &= ~SCR_FER_USBEN; @@ -258,7 +258,7 @@ static int tc6393xb_ohci_disable(struct platform_device *dev) ccr &= ~SCR_CCR_USBCK; tmio_iowrite16(ccr, tc6393xb->scr + SCR_CCR); - spin_unlock_irqrestore(&tc6393xb->lock, flags); + raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); return 0; } @@ -280,14 +280,14 @@ static int tc6393xb_fb_enable(struct platform_device *dev) unsigned long flags; u16 ccr; - spin_lock_irqsave(&tc6393xb->lock, flags); + raw_spin_lock_irqsave(&tc6393xb->lock, flags); ccr = tmio_ioread16(tc6393xb->scr + SCR_CCR); ccr &= ~SCR_CCR_MCLK_MASK; ccr |= SCR_CCR_MCLK_48; tmio_iowrite16(ccr, tc6393xb->scr + SCR_CCR); - spin_unlock_irqrestore(&tc6393xb->lock, flags); + raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); return 0; } @@ -298,14 +298,14 @@ static int tc6393xb_fb_disable(struct platform_device *dev) unsigned long flags; u16 ccr; - spin_lock_irqsave(&tc6393xb->lock, flags); + raw_spin_lock_irqsave(&tc6393xb->lock, flags); ccr = tmio_ioread16(tc6393xb->scr + SCR_CCR); ccr &= ~SCR_CCR_MCLK_MASK; ccr |= SCR_CCR_MCLK_OFF; tmio_iowrite16(ccr, tc6393xb->scr + SCR_CCR); - spin_unlock_irqrestore(&tc6393xb->lock, flags); + raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); return 0; } @@ -317,7 +317,7 @@ int tc6393xb_lcd_set_power(struct platform_device *fb, bool on) u8 fer; unsigned long flags; - spin_lock_irqsave(&tc6393xb->lock, flags); + raw_spin_lock_irqsave(&tc6393xb->lock, flags); fer = ioread8(tc6393xb->scr + SCR_FER); if (on) @@ -326,7 +326,7 @@ int tc6393xb_lcd_set_power(struct platform_device *fb, bool on) fer &= ~SCR_FER_SLCDEN; iowrite8(fer, tc6393xb->scr + SCR_FER); - spin_unlock_irqrestore(&tc6393xb->lock, flags); + raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); return 0; } @@ -338,12 +338,12 @@ int tc6393xb_lcd_mode(struct platform_device *fb, struct tc6393xb *tc6393xb = platform_get_drvdata(dev); unsigned long flags; - spin_lock_irqsave(&tc6393xb->lock, flags); + raw_spin_lock_irqsave(&tc6393xb->lock, flags); iowrite16(mode->pixclock, tc6393xb->scr + SCR_PLL1CR + 0); iowrite16(mode->pixclock >> 16, tc6393xb->scr + SCR_PLL1CR + 2); - spin_unlock_irqrestore(&tc6393xb->lock, flags); + raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); return 0; } @@ -462,11 +462,11 @@ static void tc6393xb_gpio_set(struct gpio_chip *chip, struct tc6393xb *tc6393xb = gpiochip_get_data(chip); unsigned long flags; - spin_lock_irqsave(&tc6393xb->lock, flags); + raw_spin_lock_irqsave(&tc6393xb->lock, flags); __tc6393xb_gpio_set(chip, offset, value); - spin_unlock_irqrestore(&tc6393xb->lock, flags); + raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); } static int tc6393xb_gpio_direction_input(struct gpio_chip *chip, @@ -476,13 +476,13 @@ static int tc6393xb_gpio_direction_input(struct gpio_chip *chip, unsigned long flags; u8 doecr; - spin_lock_irqsave(&tc6393xb->lock, flags); + raw_spin_lock_irqsave(&tc6393xb->lock, flags); doecr = tmio_ioread8(tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); doecr &= ~TC_GPIO_BIT(offset); tmio_iowrite8(doecr, tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); - spin_unlock_irqrestore(&tc6393xb->lock, flags); + raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); return 0; } @@ -494,7 +494,7 @@ static int tc6393xb_gpio_direction_output(struct gpio_chip *chip, unsigned long flags; u8 doecr; - spin_lock_irqsave(&tc6393xb->lock, flags); + raw_spin_lock_irqsave(&tc6393xb->lock, flags); __tc6393xb_gpio_set(chip, offset, value); @@ -502,7 +502,7 @@ static int tc6393xb_gpio_direction_output(struct gpio_chip *chip, doecr |= TC_GPIO_BIT(offset); tmio_iowrite8(doecr, tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); - spin_unlock_irqrestore(&tc6393xb->lock, flags); + raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); return 0; } @@ -548,11 +548,11 @@ static void tc6393xb_irq_mask(struct irq_data *data) unsigned long flags; u8 imr; - spin_lock_irqsave(&tc6393xb->lock, flags); + raw_spin_lock_irqsave(&tc6393xb->lock, flags); imr = tmio_ioread8(tc6393xb->scr + SCR_IMR); imr |= 1 << (data->irq - tc6393xb->irq_base); tmio_iowrite8(imr, tc6393xb->scr + SCR_IMR); - spin_unlock_irqrestore(&tc6393xb->lock, flags); + raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); } static void tc6393xb_irq_unmask(struct irq_data *data) @@ -561,11 +561,11 @@ static void tc6393xb_irq_unmask(struct irq_data *data) unsigned long flags; u8 imr; - spin_lock_irqsave(&tc6393xb->lock, flags); + raw_spin_lock_irqsave(&tc6393xb->lock, flags); imr = tmio_ioread8(tc6393xb->scr + SCR_IMR); imr &= ~(1 << (data->irq - tc6393xb->irq_base)); tmio_iowrite8(imr, tc6393xb->scr + SCR_IMR); - spin_unlock_irqrestore(&tc6393xb->lock, flags); + raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); } static struct irq_chip tc6393xb_chip = { @@ -628,7 +628,7 @@ static int tc6393xb_probe(struct platform_device *dev) goto err_kzalloc; } - spin_lock_init(&tc6393xb->lock); + raw_spin_lock_init(&tc6393xb->lock); platform_set_drvdata(dev, tc6393xb); -- cgit v1.2.3-59-g8ed1b From 85fdaf8eb9bbec1f0f8a52fd5d85659d60738816 Mon Sep 17 00:00:00 2001 From: Keerthy Date: Thu, 10 Nov 2016 10:39:18 +0530 Subject: mfd: palmas: Reset the POWERHOLD mux during power off POWERHOLD signal has higher priority over the DEV_ON bit. So power off will not happen if the POWERHOLD is held high. Hence reset the MUX to GPIO_7 mode to release the POWERHOLD and the DEV_ON bit to take effect to power off the PMIC. PMIC Power off happens in dire situations like thermal shutdown so irrespective of the POWERHOLD setting go ahead and turn off the powerhold. Currently poweroff is broken on boards that have powerhold enabled. This fixes poweroff on those boards. Signed-off-by: Keerthy Signed-off-by: Lee Jones --- drivers/mfd/palmas.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c index ee9e9ea10444..f0c559d9fa43 100644 --- a/drivers/mfd/palmas.c +++ b/drivers/mfd/palmas.c @@ -430,6 +430,20 @@ static void palmas_power_off(void) { unsigned int addr; int ret, slave; + struct device_node *np = palmas_dev->dev->of_node; + + if (of_property_read_bool(np, "ti,palmas-override-powerhold")) { + addr = PALMAS_BASE_TO_REG(PALMAS_PU_PD_OD_BASE, + PALMAS_PRIMARY_SECONDARY_PAD2); + slave = PALMAS_BASE_TO_SLAVE(PALMAS_PU_PD_OD_BASE); + + ret = regmap_update_bits(palmas_dev->regmap[slave], addr, + PALMAS_PRIMARY_SECONDARY_PAD2_GPIO_7_MASK, 0); + if (ret) + dev_err(palmas_dev->dev, + "Unable to write PRIMARY_SECONDARY_PAD2 %d\n", + ret); + } slave = PALMAS_BASE_TO_SLAVE(PALMAS_PMU_CONTROL_BASE); addr = PALMAS_BASE_TO_REG(PALMAS_PMU_CONTROL_BASE, PALMAS_DEV_CTRL); -- cgit v1.2.3-59-g8ed1b From 119d53d44cb0da2e3140106a9e359fc53bd6d837 Mon Sep 17 00:00:00 2001 From: Belen Sarabia Date: Sat, 25 Mar 2017 19:26:47 +0100 Subject: mfd: ipaq-micro: Delete redundant return value check of platform_get_resource() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit devm_ioremap_resource does checks on the resource. No need to duplicate this in the driver. Signed-off-by: Belén Sarabia Signed-off-by: Lee Jones --- drivers/mfd/ipaq-micro.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/mfd/ipaq-micro.c b/drivers/mfd/ipaq-micro.c index df16fd1df68b..124aad2b1d02 100644 --- a/drivers/mfd/ipaq-micro.c +++ b/drivers/mfd/ipaq-micro.c @@ -400,9 +400,6 @@ static int __init micro_probe(struct platform_device *pdev) micro->dev = &pdev->dev; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -EINVAL; - micro->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(micro->base)) return PTR_ERR(micro->base); -- cgit v1.2.3-59-g8ed1b From fe9d7cb22ef3a26a74e49730c0efdbdae4b17d4b Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 16 Mar 2017 09:30:28 +0100 Subject: mfd: syscon: atmel-smc: Add new helpers to ease SMC regs manipulation These new helpers + macro definitions are meant to replace the old ones which are unpractical to use. Note that the macros and function prefixes have been intentionally changed to ATMEL_[H]SMC_XX and atmel_[h]smc_ to reflect the fact that this IP is also embedded in avr32 SoCs (and not only in at91 ones). Signed-off-by: Boris Brezillon Acked-by: Nicolas Ferre Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 4 + drivers/mfd/Makefile | 1 + drivers/mfd/atmel-smc.c | 314 +++++++++++++++++++++++++++++++++++ include/linux/mfd/syscon/atmel-smc.h | 87 ++++++++++ 4 files changed, 406 insertions(+) create mode 100644 drivers/mfd/atmel-smc.c diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index add0c35c38c5..ce3a9180a765 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -121,6 +121,10 @@ config MFD_ATMEL_HLCDC additional drivers must be enabled in order to use the functionality of the device. +config MFD_ATMEL_SMC + bool + select MFD_SYSCON + config MFD_BCM590XX tristate "Broadcom BCM590xx PMUs" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 294a0525aeb1..fa86dbe65e52 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -185,6 +185,7 @@ obj-$(CONFIG_MFD_TPS65090) += tps65090.o obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o obj-$(CONFIG_MFD_ATMEL_FLEXCOM) += atmel-flexcom.o obj-$(CONFIG_MFD_ATMEL_HLCDC) += atmel-hlcdc.o +obj-$(CONFIG_MFD_ATMEL_SMC) += atmel-smc.o obj-$(CONFIG_MFD_INTEL_LPSS) += intel-lpss.o obj-$(CONFIG_MFD_INTEL_LPSS_PCI) += intel-lpss-pci.o obj-$(CONFIG_MFD_INTEL_LPSS_ACPI) += intel-lpss-acpi.o diff --git a/drivers/mfd/atmel-smc.c b/drivers/mfd/atmel-smc.c new file mode 100644 index 000000000000..954cf0f66a31 --- /dev/null +++ b/drivers/mfd/atmel-smc.c @@ -0,0 +1,314 @@ +/* + * Atmel SMC (Static Memory Controller) helper functions. + * + * Copyright (C) 2017 Atmel + * Copyright (C) 2017 Free Electrons + * + * Author: Boris Brezillon + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +/** + * atmel_smc_cs_conf_init - initialize a SMC CS conf + * @conf: the SMC CS conf to initialize + * + * Set all fields to 0 so that one can start defining a new config. + */ +void atmel_smc_cs_conf_init(struct atmel_smc_cs_conf *conf) +{ + memset(conf, 0, sizeof(*conf)); +} +EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_init); + +/** + * atmel_smc_cs_encode_ncycles - encode a number of MCK clk cycles in the + * format expected by the SMC engine + * @ncycles: number of MCK clk cycles + * @msbpos: position of the MSB part of the timing field + * @msbwidth: width of the MSB part of the timing field + * @msbfactor: factor applied to the MSB + * @encodedval: param used to store the encoding result + * + * This function encodes the @ncycles value as described in the datasheet + * (section "SMC Setup/Pulse/Cycle/Timings Register"). This is a generic + * helper which called with different parameter depending on the encoding + * scheme. + * + * If the @ncycles value is too big to be encoded, -ERANGE is returned and + * the encodedval is contains the maximum val. Otherwise, 0 is returned. + */ +static int atmel_smc_cs_encode_ncycles(unsigned int ncycles, + unsigned int msbpos, + unsigned int msbwidth, + unsigned int msbfactor, + unsigned int *encodedval) +{ + unsigned int lsbmask = GENMASK(msbpos - 1, 0); + unsigned int msbmask = GENMASK(msbwidth - 1, 0); + unsigned int msb, lsb; + int ret = 0; + + msb = ncycles / msbfactor; + lsb = ncycles % msbfactor; + + if (lsb > lsbmask) { + lsb = 0; + msb++; + } + + /* + * Let's just put the maximum we can if the requested setting does + * not fit in the register field. + * We still return -ERANGE in case the caller cares. + */ + if (msb > msbmask) { + msb = msbmask; + lsb = lsbmask; + ret = -ERANGE; + } + + *encodedval = (msb << msbpos) | lsb; + + return ret; +} + +/** + * atmel_smc_cs_conf_set_timing - set the SMC CS conf Txx parameter to a + * specific value + * @conf: SMC CS conf descriptor + * @shift: the position of the Txx field in the TIMINGS register + * @ncycles: value (expressed in MCK clk cycles) to assign to this Txx + * parameter + * + * This function encodes the @ncycles value as described in the datasheet + * (section "SMC Timings Register"), and then stores the result in the + * @conf->timings field at @shift position. + * + * Returns -EINVAL if shift is invalid, -ERANGE if ncycles does not fit in + * the field, and 0 otherwise. + */ +int atmel_smc_cs_conf_set_timing(struct atmel_smc_cs_conf *conf, + unsigned int shift, unsigned int ncycles) +{ + unsigned int val; + int ret; + + if (shift != ATMEL_HSMC_TIMINGS_TCLR_SHIFT && + shift != ATMEL_HSMC_TIMINGS_TADL_SHIFT && + shift != ATMEL_HSMC_TIMINGS_TAR_SHIFT && + shift != ATMEL_HSMC_TIMINGS_TRR_SHIFT && + shift != ATMEL_HSMC_TIMINGS_TWB_SHIFT) + return -EINVAL; + + /* + * The formula described in atmel datasheets (section "HSMC Timings + * Register"): + * + * ncycles = (Txx[3] * 64) + Txx[2:0] + */ + ret = atmel_smc_cs_encode_ncycles(ncycles, 3, 1, 64, &val); + conf->timings &= ~GENMASK(shift + 3, shift); + conf->timings |= val << shift; + + return ret; +} +EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_timing); + +/** + * atmel_smc_cs_conf_set_setup - set the SMC CS conf xx_SETUP parameter to a + * specific value + * @conf: SMC CS conf descriptor + * @shift: the position of the xx_SETUP field in the SETUP register + * @ncycles: value (expressed in MCK clk cycles) to assign to this xx_SETUP + * parameter + * + * This function encodes the @ncycles value as described in the datasheet + * (section "SMC Setup Register"), and then stores the result in the + * @conf->setup field at @shift position. + * + * Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in + * the field, and 0 otherwise. + */ +int atmel_smc_cs_conf_set_setup(struct atmel_smc_cs_conf *conf, + unsigned int shift, unsigned int ncycles) +{ + unsigned int val; + int ret; + + if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NCS_WR_SHIFT && + shift != ATMEL_SMC_NRD_SHIFT && shift != ATMEL_SMC_NCS_RD_SHIFT) + return -EINVAL; + + /* + * The formula described in atmel datasheets (section "SMC Setup + * Register"): + * + * ncycles = (128 * xx_SETUP[5]) + xx_SETUP[4:0] + */ + ret = atmel_smc_cs_encode_ncycles(ncycles, 5, 1, 128, &val); + conf->setup &= ~GENMASK(shift + 7, shift); + conf->setup |= val << shift; + + return ret; +} +EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_setup); + +/** + * atmel_smc_cs_conf_set_pulse - set the SMC CS conf xx_PULSE parameter to a + * specific value + * @conf: SMC CS conf descriptor + * @shift: the position of the xx_PULSE field in the PULSE register + * @ncycles: value (expressed in MCK clk cycles) to assign to this xx_PULSE + * parameter + * + * This function encodes the @ncycles value as described in the datasheet + * (section "SMC Pulse Register"), and then stores the result in the + * @conf->setup field at @shift position. + * + * Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in + * the field, and 0 otherwise. + */ +int atmel_smc_cs_conf_set_pulse(struct atmel_smc_cs_conf *conf, + unsigned int shift, unsigned int ncycles) +{ + unsigned int val; + int ret; + + if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NCS_WR_SHIFT && + shift != ATMEL_SMC_NRD_SHIFT && shift != ATMEL_SMC_NCS_RD_SHIFT) + return -EINVAL; + + /* + * The formula described in atmel datasheets (section "SMC Pulse + * Register"): + * + * ncycles = (256 * xx_PULSE[6]) + xx_PULSE[5:0] + */ + ret = atmel_smc_cs_encode_ncycles(ncycles, 6, 1, 256, &val); + conf->pulse &= ~GENMASK(shift + 7, shift); + conf->pulse |= val << shift; + + return ret; +} +EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_pulse); + +/** + * atmel_smc_cs_conf_set_cycle - set the SMC CS conf xx_CYCLE parameter to a + * specific value + * @conf: SMC CS conf descriptor + * @shift: the position of the xx_CYCLE field in the CYCLE register + * @ncycles: value (expressed in MCK clk cycles) to assign to this xx_CYCLE + * parameter + * + * This function encodes the @ncycles value as described in the datasheet + * (section "SMC Pulse Register"), and then stores the result in the + * @conf->setup field at @shift position. + * + * Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in + * the field, and 0 otherwise. + */ +int atmel_smc_cs_conf_set_cycle(struct atmel_smc_cs_conf *conf, + unsigned int shift, unsigned int ncycles) +{ + unsigned int val; + int ret; + + if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NRD_SHIFT) + return -EINVAL; + + /* + * The formula described in atmel datasheets (section "SMC Cycle + * Register"): + * + * ncycles = (xx_CYCLE[8:7] * 256) + xx_CYCLE[6:0] + */ + ret = atmel_smc_cs_encode_ncycles(ncycles, 7, 2, 256, &val); + conf->cycle &= ~GENMASK(shift + 15, shift); + conf->cycle |= val << shift; + + return ret; +} +EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_cycle); + +/** + * atmel_smc_cs_conf_apply - apply an SMC CS conf + * @regmap: the SMC regmap + * @cs: the CS id + * @conf the SMC CS conf to apply + * + * Applies an SMC CS configuration. + * Only valid on at91sam9/avr32 SoCs. + */ +void atmel_smc_cs_conf_apply(struct regmap *regmap, int cs, + const struct atmel_smc_cs_conf *conf) +{ + regmap_write(regmap, ATMEL_SMC_SETUP(cs), conf->setup); + regmap_write(regmap, ATMEL_SMC_PULSE(cs), conf->pulse); + regmap_write(regmap, ATMEL_SMC_CYCLE(cs), conf->cycle); + regmap_write(regmap, ATMEL_SMC_MODE(cs), conf->mode); +} +EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_apply); + +/** + * atmel_hsmc_cs_conf_apply - apply an SMC CS conf + * @regmap: the HSMC regmap + * @cs: the CS id + * @conf the SMC CS conf to apply + * + * Applies an SMC CS configuration. + * Only valid on post-sama5 SoCs. + */ +void atmel_hsmc_cs_conf_apply(struct regmap *regmap, int cs, + const struct atmel_smc_cs_conf *conf) +{ + regmap_write(regmap, ATMEL_HSMC_SETUP(cs), conf->setup); + regmap_write(regmap, ATMEL_HSMC_PULSE(cs), conf->pulse); + regmap_write(regmap, ATMEL_HSMC_CYCLE(cs), conf->cycle); + regmap_write(regmap, ATMEL_HSMC_TIMINGS(cs), conf->timings); + regmap_write(regmap, ATMEL_HSMC_MODE(cs), conf->mode); +} +EXPORT_SYMBOL_GPL(atmel_hsmc_cs_conf_apply); + +/** + * atmel_smc_cs_conf_get - retrieve the current SMC CS conf + * @regmap: the SMC regmap + * @cs: the CS id + * @conf: the SMC CS conf object to store the current conf + * + * Retrieve the SMC CS configuration. + * Only valid on at91sam9/avr32 SoCs. + */ +void atmel_smc_cs_conf_get(struct regmap *regmap, int cs, + struct atmel_smc_cs_conf *conf) +{ + regmap_read(regmap, ATMEL_SMC_SETUP(cs), &conf->setup); + regmap_read(regmap, ATMEL_SMC_PULSE(cs), &conf->pulse); + regmap_read(regmap, ATMEL_SMC_CYCLE(cs), &conf->cycle); + regmap_read(regmap, ATMEL_SMC_MODE(cs), &conf->mode); +} +EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_get); + +/** + * atmel_hsmc_cs_conf_get - retrieve the current SMC CS conf + * @regmap: the HSMC regmap + * @cs: the CS id + * @conf: the SMC CS conf object to store the current conf + * + * Retrieve the SMC CS configuration. + * Only valid on post-sama5 SoCs. + */ +void atmel_hsmc_cs_conf_get(struct regmap *regmap, int cs, + struct atmel_smc_cs_conf *conf) +{ + regmap_read(regmap, ATMEL_HSMC_SETUP(cs), &conf->setup); + regmap_read(regmap, ATMEL_HSMC_PULSE(cs), &conf->pulse); + regmap_read(regmap, ATMEL_HSMC_CYCLE(cs), &conf->cycle); + regmap_read(regmap, ATMEL_HSMC_TIMINGS(cs), &conf->timings); + regmap_read(regmap, ATMEL_HSMC_MODE(cs), &conf->mode); +} +EXPORT_SYMBOL_GPL(atmel_hsmc_cs_conf_get); diff --git a/include/linux/mfd/syscon/atmel-smc.h b/include/linux/mfd/syscon/atmel-smc.h index be6ebe64eebe..00e6e3c8ee6f 100644 --- a/include/linux/mfd/syscon/atmel-smc.h +++ b/include/linux/mfd/syscon/atmel-smc.h @@ -69,6 +69,93 @@ #define AT91_SMC_PS_16 (2 << 28) #define AT91_SMC_PS_32 (3 << 28) +#define ATMEL_SMC_SETUP(cs) (((cs) * 0x10)) +#define ATMEL_HSMC_SETUP(cs) (0x600 + ((cs) * 0x14)) +#define ATMEL_SMC_PULSE(cs) (((cs) * 0x10) + 0x4) +#define ATMEL_HSMC_PULSE(cs) (0x600 + ((cs) * 0x14) + 0x4) +#define ATMEL_SMC_CYCLE(cs) (((cs) * 0x10) + 0x8) +#define ATMEL_HSMC_CYCLE(cs) (0x600 + ((cs) * 0x14) + 0x8) +#define ATMEL_SMC_NWE_SHIFT 0 +#define ATMEL_SMC_NCS_WR_SHIFT 8 +#define ATMEL_SMC_NRD_SHIFT 16 +#define ATMEL_SMC_NCS_RD_SHIFT 24 + +#define ATMEL_SMC_MODE(cs) (((cs) * 0x10) + 0xc) +#define ATMEL_HSMC_MODE(cs) (0x600 + ((cs) * 0x14) + 0x10) +#define ATMEL_SMC_MODE_READMODE_MASK BIT(0) +#define ATMEL_SMC_MODE_READMODE_NCS (0 << 0) +#define ATMEL_SMC_MODE_READMODE_NRD (1 << 0) +#define ATMEL_SMC_MODE_WRITEMODE_MASK BIT(1) +#define ATMEL_SMC_MODE_WRITEMODE_NCS (0 << 1) +#define ATMEL_SMC_MODE_WRITEMODE_NWE (1 << 1) +#define ATMEL_SMC_MODE_EXNWMODE_MASK GENMASK(5, 4) +#define ATMEL_SMC_MODE_EXNWMODE_DISABLE (0 << 4) +#define ATMEL_SMC_MODE_EXNWMODE_FROZEN (2 << 4) +#define ATMEL_SMC_MODE_EXNWMODE_READY (3 << 4) +#define ATMEL_SMC_MODE_BAT_MASK BIT(8) +#define ATMEL_SMC_MODE_BAT_SELECT (0 << 8) +#define ATMEL_SMC_MODE_BAT_WRITE (1 << 8) +#define ATMEL_SMC_MODE_DBW_MASK GENMASK(13, 12) +#define ATMEL_SMC_MODE_DBW_8 (0 << 12) +#define ATMEL_SMC_MODE_DBW_16 (1 << 12) +#define ATMEL_SMC_MODE_DBW_32 (2 << 12) +#define ATMEL_SMC_MODE_TDF_MASK GENMASK(19, 16) +#define ATMEL_SMC_MODE_TDF(x) (((x) - 1) << 16) +#define ATMEL_SMC_MODE_TDF_MAX 16 +#define ATMEL_SMC_MODE_TDF_MIN 1 +#define ATMEL_SMC_MODE_TDFMODE_OPTIMIZED BIT(20) +#define ATMEL_SMC_MODE_PMEN BIT(24) +#define ATMEL_SMC_MODE_PS_MASK GENMASK(29, 28) +#define ATMEL_SMC_MODE_PS_4 (0 << 28) +#define ATMEL_SMC_MODE_PS_8 (1 << 28) +#define ATMEL_SMC_MODE_PS_16 (2 << 28) +#define ATMEL_SMC_MODE_PS_32 (3 << 28) + +#define ATMEL_HSMC_TIMINGS(cs) (0x600 + ((cs) * 0x14) + 0xc) +#define ATMEL_HSMC_TIMINGS_OCMS BIT(12) +#define ATMEL_HSMC_TIMINGS_RBNSEL(x) ((x) << 28) +#define ATMEL_HSMC_TIMINGS_NFSEL BIT(31) +#define ATMEL_HSMC_TIMINGS_TCLR_SHIFT 0 +#define ATMEL_HSMC_TIMINGS_TADL_SHIFT 4 +#define ATMEL_HSMC_TIMINGS_TAR_SHIFT 8 +#define ATMEL_HSMC_TIMINGS_TRR_SHIFT 16 +#define ATMEL_HSMC_TIMINGS_TWB_SHIFT 24 + +/** + * struct atmel_smc_cs_conf - SMC CS config as described in the datasheet. + * @setup: NCS/NWE/NRD setup timings (not applicable to at91rm9200) + * @pulse: NCS/NWE/NRD pulse timings (not applicable to at91rm9200) + * @cycle: NWE/NRD cycle timings (not applicable to at91rm9200) + * @timings: advanced NAND related timings (only applicable to HSMC) + * @mode: all kind of config parameters (see the fields definition above). + * The mode fields are different on at91rm9200 + */ +struct atmel_smc_cs_conf { + u32 setup; + u32 pulse; + u32 cycle; + u32 timings; + u32 mode; +}; + +void atmel_smc_cs_conf_init(struct atmel_smc_cs_conf *conf); +int atmel_smc_cs_conf_set_timing(struct atmel_smc_cs_conf *conf, + unsigned int shift, + unsigned int ncycles); +int atmel_smc_cs_conf_set_setup(struct atmel_smc_cs_conf *conf, + unsigned int shift, unsigned int ncycles); +int atmel_smc_cs_conf_set_pulse(struct atmel_smc_cs_conf *conf, + unsigned int shift, unsigned int ncycles); +int atmel_smc_cs_conf_set_cycle(struct atmel_smc_cs_conf *conf, + unsigned int shift, unsigned int ncycles); +void atmel_smc_cs_conf_apply(struct regmap *regmap, int cs, + const struct atmel_smc_cs_conf *conf); +void atmel_hsmc_cs_conf_apply(struct regmap *regmap, int cs, + const struct atmel_smc_cs_conf *conf); +void atmel_smc_cs_conf_get(struct regmap *regmap, int cs, + struct atmel_smc_cs_conf *conf); +void atmel_hsmc_cs_conf_get(struct regmap *regmap, int cs, + struct atmel_smc_cs_conf *conf); /* * This function converts a setup timing expressed in nanoseconds into an -- cgit v1.2.3-59-g8ed1b From 8eb8c7d844b9da6301542627eaaae28f4d20cb75 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 16 Mar 2017 09:30:29 +0100 Subject: memory: atmel-ebi: Simplify SMC config code New helpers/macros have been to atmel-smc.h introduced to simplify SMC regs manipulation. Rework the code to use those helpers, and simplify the ->xlate_config(), ->get_config() and ->apply_config() implementations. SMC configs are now stored in a struct atmel_smc_cs_conf object that directly contains registers values, which should help implementing ->suspend()/->resume() hooks. We can also get rid of those regmap fields (and the associated ->init() hook) which are not longer needed thanks to the atmel_[h]smc_cs_conf_{apply,get}() helpers. Signed-off-by: Boris Brezillon Acked-by: Nicolas Ferre Signed-off-by: Lee Jones --- drivers/memory/Kconfig | 1 + drivers/memory/atmel-ebi.c | 430 +++++++++++++-------------------------------- 2 files changed, 128 insertions(+), 303 deletions(-) diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index ec80e35c8dfe..3ecc429297a0 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -30,6 +30,7 @@ config ATMEL_EBI default y depends on ARCH_AT91 && OF select MFD_SYSCON + select MFD_ATMEL_SMC help Driver for Atmel EBI controller. Used to configure the EBI (external bus interface) when the device- diff --git a/drivers/memory/atmel-ebi.c b/drivers/memory/atmel-ebi.c index 4e83a8b92665..e1b8590e7d23 100644 --- a/drivers/memory/atmel-ebi.c +++ b/drivers/memory/atmel-ebi.c @@ -18,37 +18,9 @@ #include #include -struct at91sam9_smc_timings { - u32 ncs_rd_setup_ns; - u32 nrd_setup_ns; - u32 ncs_wr_setup_ns; - u32 nwe_setup_ns; - u32 ncs_rd_pulse_ns; - u32 nrd_pulse_ns; - u32 ncs_wr_pulse_ns; - u32 nwe_pulse_ns; - u32 nrd_cycle_ns; - u32 nwe_cycle_ns; - u32 tdf_ns; -}; - -struct at91sam9_smc_generic_fields { - struct regmap_field *setup; - struct regmap_field *pulse; - struct regmap_field *cycle; - struct regmap_field *mode; -}; - -struct at91sam9_ebi_dev_config { - struct at91sam9_smc_timings timings; - u32 mode; -}; - struct at91_ebi_dev_config { int cs; - union { - struct at91sam9_ebi_dev_config sam9; - }; + struct atmel_smc_cs_conf smcconf; }; struct at91_ebi; @@ -69,9 +41,8 @@ struct at91_ebi_caps { int (*xlate_config)(struct at91_ebi_dev *ebid, struct device_node *configs_np, struct at91_ebi_dev_config *conf); - int (*apply_config)(struct at91_ebi_dev *ebid, - struct at91_ebi_dev_config *conf); - int (*init)(struct at91_ebi *ebi); + void (*apply_config)(struct at91_ebi_dev *ebid, + struct at91_ebi_dev_config *conf); }; struct at91_ebi { @@ -86,151 +57,119 @@ struct at91_ebi { struct device *dev; const struct at91_ebi_caps *caps; struct list_head devs; - union { - struct at91sam9_smc_generic_fields sam9; - }; }; +struct atmel_smc_timing_xlate { + const char *name; + int (*converter)(struct atmel_smc_cs_conf *conf, + unsigned int shift, unsigned int nycles); + unsigned int shift; +}; + +#define ATMEL_SMC_SETUP_XLATE(nm, pos) \ + { .name = nm, .converter = atmel_smc_cs_conf_set_setup, .shift = pos} + +#define ATMEL_SMC_PULSE_XLATE(nm, pos) \ + { .name = nm, .converter = atmel_smc_cs_conf_set_pulse, .shift = pos} + +#define ATMEL_SMC_CYCLE_XLATE(nm, pos) \ + { .name = nm, .converter = atmel_smc_cs_conf_set_setup, .shift = pos} + static void at91sam9_ebi_get_config(struct at91_ebi_dev *ebid, struct at91_ebi_dev_config *conf) { - struct at91sam9_smc_generic_fields *fields = &ebid->ebi->sam9; - unsigned int clk_period = NSEC_PER_SEC / clk_get_rate(ebid->ebi->clk); - struct at91sam9_ebi_dev_config *config = &conf->sam9; - struct at91sam9_smc_timings *timings = &config->timings; - unsigned int val; - - regmap_fields_read(fields->mode, conf->cs, &val); - config->mode = val & ~AT91_SMC_TDF; - - val = (val & AT91_SMC_TDF) >> 16; - timings->tdf_ns = clk_period * val; - - regmap_fields_read(fields->setup, conf->cs, &val); - timings->ncs_rd_setup_ns = (val >> 24) & 0x1f; - timings->ncs_rd_setup_ns += ((val >> 29) & 0x1) * 128; - timings->ncs_rd_setup_ns *= clk_period; - timings->nrd_setup_ns = (val >> 16) & 0x1f; - timings->nrd_setup_ns += ((val >> 21) & 0x1) * 128; - timings->nrd_setup_ns *= clk_period; - timings->ncs_wr_setup_ns = (val >> 8) & 0x1f; - timings->ncs_wr_setup_ns += ((val >> 13) & 0x1) * 128; - timings->ncs_wr_setup_ns *= clk_period; - timings->nwe_setup_ns = val & 0x1f; - timings->nwe_setup_ns += ((val >> 5) & 0x1) * 128; - timings->nwe_setup_ns *= clk_period; - - regmap_fields_read(fields->pulse, conf->cs, &val); - timings->ncs_rd_pulse_ns = (val >> 24) & 0x3f; - timings->ncs_rd_pulse_ns += ((val >> 30) & 0x1) * 256; - timings->ncs_rd_pulse_ns *= clk_period; - timings->nrd_pulse_ns = (val >> 16) & 0x3f; - timings->nrd_pulse_ns += ((val >> 22) & 0x1) * 256; - timings->nrd_pulse_ns *= clk_period; - timings->ncs_wr_pulse_ns = (val >> 8) & 0x3f; - timings->ncs_wr_pulse_ns += ((val >> 14) & 0x1) * 256; - timings->ncs_wr_pulse_ns *= clk_period; - timings->nwe_pulse_ns = val & 0x3f; - timings->nwe_pulse_ns += ((val >> 6) & 0x1) * 256; - timings->nwe_pulse_ns *= clk_period; - - regmap_fields_read(fields->cycle, conf->cs, &val); - timings->nrd_cycle_ns = (val >> 16) & 0x7f; - timings->nrd_cycle_ns += ((val >> 23) & 0x3) * 256; - timings->nrd_cycle_ns *= clk_period; - timings->nwe_cycle_ns = val & 0x7f; - timings->nwe_cycle_ns += ((val >> 7) & 0x3) * 256; - timings->nwe_cycle_ns *= clk_period; + atmel_smc_cs_conf_get(ebid->ebi->smc.regmap, conf->cs, + &conf->smcconf); } -static int at91_xlate_timing(struct device_node *np, const char *prop, - u32 *val, bool *required) +static void sama5_ebi_get_config(struct at91_ebi_dev *ebid, + struct at91_ebi_dev_config *conf) { - if (!of_property_read_u32(np, prop, val)) { - *required = true; - return 0; - } - - if (*required) - return -EINVAL; - - return 0; + atmel_hsmc_cs_conf_get(ebid->ebi->smc.regmap, conf->cs, + &conf->smcconf); } -static int at91sam9_smc_xslate_timings(struct at91_ebi_dev *ebid, +static const struct atmel_smc_timing_xlate timings_xlate_table[] = { + ATMEL_SMC_SETUP_XLATE("atmel,smc-ncs-rd-setup-ns", + ATMEL_SMC_NCS_RD_SHIFT), + ATMEL_SMC_SETUP_XLATE("atmel,smc-ncs-wr-setup-ns", + ATMEL_SMC_NCS_WR_SHIFT), + ATMEL_SMC_SETUP_XLATE("atmel,smc-nrd-setup-ns", ATMEL_SMC_NRD_SHIFT), + ATMEL_SMC_SETUP_XLATE("atmel,smc-nwe-setup-ns", ATMEL_SMC_NWE_SHIFT), + ATMEL_SMC_PULSE_XLATE("atmel,smc-ncs-rd-pulse-ns", + ATMEL_SMC_NCS_RD_SHIFT), + ATMEL_SMC_PULSE_XLATE("atmel,smc-ncs-wr-pulse-ns", + ATMEL_SMC_NCS_WR_SHIFT), + ATMEL_SMC_PULSE_XLATE("atmel,smc-nrd-pulse-ns", ATMEL_SMC_NRD_SHIFT), + ATMEL_SMC_PULSE_XLATE("atmel,smc-nwe-pulse-ns", ATMEL_SMC_NWE_SHIFT), + ATMEL_SMC_CYCLE_XLATE("atmel,smc-nrd-cycle-ns", ATMEL_SMC_NRD_SHIFT), + ATMEL_SMC_CYCLE_XLATE("atmel,smc-nwe-cycle-ns", ATMEL_SMC_NWE_SHIFT), +}; + +static int at91_ebi_xslate_smc_timings(struct at91_ebi_dev *ebid, struct device_node *np, - struct at91sam9_smc_timings *timings, - bool *required) + struct atmel_smc_cs_conf *smcconf) { - int ret; - - ret = at91_xlate_timing(np, "atmel,smc-ncs-rd-setup-ns", - &timings->ncs_rd_setup_ns, required); - if (ret) - goto out; - - ret = at91_xlate_timing(np, "atmel,smc-nrd-setup-ns", - &timings->nrd_setup_ns, required); - if (ret) - goto out; - - ret = at91_xlate_timing(np, "atmel,smc-ncs-wr-setup-ns", - &timings->ncs_wr_setup_ns, required); - if (ret) - goto out; + unsigned int clk_rate = clk_get_rate(ebid->ebi->clk); + unsigned int clk_period_ns = NSEC_PER_SEC / clk_rate; + bool required = false; + unsigned int ncycles; + int ret, i; + u32 val; - ret = at91_xlate_timing(np, "atmel,smc-nwe-setup-ns", - &timings->nwe_setup_ns, required); - if (ret) - goto out; + ret = of_property_read_u32(np, "atmel,smc-tdf-ns", &val); + if (!ret) { + required = true; + ncycles = DIV_ROUND_UP(val, clk_period_ns); + if (ncycles > ATMEL_SMC_MODE_TDF_MAX || + ncycles < ATMEL_SMC_MODE_TDF_MIN) { + ret = -EINVAL; + goto out; + } - ret = at91_xlate_timing(np, "atmel,smc-ncs-rd-pulse-ns", - &timings->ncs_rd_pulse_ns, required); - if (ret) - goto out; + smcconf->mode |= ATMEL_SMC_MODE_TDF(ncycles); + } - ret = at91_xlate_timing(np, "atmel,smc-nrd-pulse-ns", - &timings->nrd_pulse_ns, required); - if (ret) - goto out; + for (i = 0; i < ARRAY_SIZE(timings_xlate_table); i++) { + const struct atmel_smc_timing_xlate *xlate; - ret = at91_xlate_timing(np, "atmel,smc-ncs-wr-pulse-ns", - &timings->ncs_wr_pulse_ns, required); - if (ret) - goto out; + xlate = &timings_xlate_table[i]; - ret = at91_xlate_timing(np, "atmel,smc-nwe-pulse-ns", - &timings->nwe_pulse_ns, required); - if (ret) - goto out; - - ret = at91_xlate_timing(np, "atmel,smc-nwe-cycle-ns", - &timings->nwe_cycle_ns, required); - if (ret) - goto out; + ret = of_property_read_u32(np, xlate->name, &val); + if (ret) { + if (!required) + continue; + else + break; + } - ret = at91_xlate_timing(np, "atmel,smc-nrd-cycle-ns", - &timings->nrd_cycle_ns, required); - if (ret) - goto out; + if (!required) { + ret = -EINVAL; + break; + } - ret = at91_xlate_timing(np, "atmel,smc-tdf-ns", - &timings->tdf_ns, required); + ncycles = DIV_ROUND_UP(val, clk_period_ns); + ret = xlate->converter(smcconf, xlate->shift, ncycles); + if (ret) + goto out; + } out: - if (ret) + if (ret) { dev_err(ebid->ebi->dev, "missing or invalid timings definition in %s", np->full_name); + return ret; + } - return ret; + return required; } -static int at91sam9_ebi_xslate_config(struct at91_ebi_dev *ebid, +static int at91_ebi_xslate_smc_config(struct at91_ebi_dev *ebid, struct device_node *np, struct at91_ebi_dev_config *conf) { - struct at91sam9_ebi_dev_config *config = &conf->sam9; + struct atmel_smc_cs_conf *smcconf = &conf->smcconf; bool required = false; const char *tmp_str; u32 tmp; @@ -240,15 +179,15 @@ static int at91sam9_ebi_xslate_config(struct at91_ebi_dev *ebid, if (!ret) { switch (tmp) { case 8: - config->mode |= AT91_SMC_DBW_8; + smcconf->mode |= ATMEL_SMC_MODE_DBW_8; break; case 16: - config->mode |= AT91_SMC_DBW_16; + smcconf->mode |= ATMEL_SMC_MODE_DBW_16; break; case 32: - config->mode |= AT91_SMC_DBW_32; + smcconf->mode |= ATMEL_SMC_MODE_DBW_32; break; default: @@ -259,28 +198,28 @@ static int at91sam9_ebi_xslate_config(struct at91_ebi_dev *ebid, } if (of_property_read_bool(np, "atmel,smc-tdf-optimized")) { - config->mode |= AT91_SMC_TDFMODE_OPTIMIZED; + smcconf->mode |= ATMEL_SMC_MODE_TDFMODE_OPTIMIZED; required = true; } tmp_str = NULL; of_property_read_string(np, "atmel,smc-byte-access-type", &tmp_str); if (tmp_str && !strcmp(tmp_str, "write")) { - config->mode |= AT91_SMC_BAT_WRITE; + smcconf->mode |= ATMEL_SMC_MODE_BAT_WRITE; required = true; } tmp_str = NULL; of_property_read_string(np, "atmel,smc-read-mode", &tmp_str); if (tmp_str && !strcmp(tmp_str, "nrd")) { - config->mode |= AT91_SMC_READMODE_NRD; + smcconf->mode |= ATMEL_SMC_MODE_READMODE_NRD; required = true; } tmp_str = NULL; of_property_read_string(np, "atmel,smc-write-mode", &tmp_str); if (tmp_str && !strcmp(tmp_str, "nwe")) { - config->mode |= AT91_SMC_WRITEMODE_NWE; + smcconf->mode |= ATMEL_SMC_MODE_WRITEMODE_NWE; required = true; } @@ -288,9 +227,9 @@ static int at91sam9_ebi_xslate_config(struct at91_ebi_dev *ebid, of_property_read_string(np, "atmel,smc-exnw-mode", &tmp_str); if (tmp_str) { if (!strcmp(tmp_str, "frozen")) - config->mode |= AT91_SMC_EXNWMODE_FROZEN; + smcconf->mode |= ATMEL_SMC_MODE_EXNWMODE_FROZEN; else if (!strcmp(tmp_str, "ready")) - config->mode |= AT91_SMC_EXNWMODE_READY; + smcconf->mode |= ATMEL_SMC_MODE_EXNWMODE_READY; else if (strcmp(tmp_str, "disabled")) return -EINVAL; @@ -301,155 +240,54 @@ static int at91sam9_ebi_xslate_config(struct at91_ebi_dev *ebid, if (!ret) { switch (tmp) { case 4: - config->mode |= AT91_SMC_PS_4; + smcconf->mode |= ATMEL_SMC_MODE_PS_4; break; case 8: - config->mode |= AT91_SMC_PS_8; + smcconf->mode |= ATMEL_SMC_MODE_PS_8; break; case 16: - config->mode |= AT91_SMC_PS_16; + smcconf->mode |= ATMEL_SMC_MODE_PS_16; break; case 32: - config->mode |= AT91_SMC_PS_32; + smcconf->mode |= ATMEL_SMC_MODE_PS_32; break; default: return -EINVAL; } - config->mode |= AT91_SMC_PMEN; + smcconf->mode |= ATMEL_SMC_MODE_PMEN; required = true; } - ret = at91sam9_smc_xslate_timings(ebid, np, &config->timings, - &required); + ret = at91_ebi_xslate_smc_timings(ebid, np, &conf->smcconf); if (ret) - return ret; - - return required; -} - -static int at91sam9_ebi_apply_config(struct at91_ebi_dev *ebid, - struct at91_ebi_dev_config *conf) -{ - unsigned int clk_rate = clk_get_rate(ebid->ebi->clk); - unsigned int clk_period = NSEC_PER_SEC / clk_rate; - struct at91sam9_ebi_dev_config *config = &conf->sam9; - struct at91sam9_smc_timings *timings = &config->timings; - struct at91sam9_smc_generic_fields *fields = &ebid->ebi->sam9; - u32 coded_val; - u32 val; + return -EINVAL; - coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate, - timings->ncs_rd_setup_ns); - val = AT91SAM9_SMC_NCS_NRDSETUP(coded_val); - coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate, - timings->nrd_setup_ns); - val |= AT91SAM9_SMC_NRDSETUP(coded_val); - coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate, - timings->ncs_wr_setup_ns); - val |= AT91SAM9_SMC_NCS_WRSETUP(coded_val); - coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate, - timings->nwe_setup_ns); - val |= AT91SAM9_SMC_NWESETUP(coded_val); - regmap_fields_write(fields->setup, conf->cs, val); - - coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate, - timings->ncs_rd_pulse_ns); - val = AT91SAM9_SMC_NCS_NRDPULSE(coded_val); - coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate, - timings->nrd_pulse_ns); - val |= AT91SAM9_SMC_NRDPULSE(coded_val); - coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate, - timings->ncs_wr_pulse_ns); - val |= AT91SAM9_SMC_NCS_WRPULSE(coded_val); - coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate, - timings->nwe_pulse_ns); - val |= AT91SAM9_SMC_NWEPULSE(coded_val); - regmap_fields_write(fields->pulse, conf->cs, val); - - coded_val = at91sam9_smc_cycle_ns_to_cycles(clk_rate, - timings->nrd_cycle_ns); - val = AT91SAM9_SMC_NRDCYCLE(coded_val); - coded_val = at91sam9_smc_cycle_ns_to_cycles(clk_rate, - timings->nwe_cycle_ns); - val |= AT91SAM9_SMC_NWECYCLE(coded_val); - regmap_fields_write(fields->cycle, conf->cs, val); - - val = DIV_ROUND_UP(timings->tdf_ns, clk_period); - if (val > AT91_SMC_TDF_MAX) - val = AT91_SMC_TDF_MAX; - regmap_fields_write(fields->mode, conf->cs, - config->mode | AT91_SMC_TDF_(val)); + if ((ret > 0 && !required) || (!ret && required)) { + dev_err(ebid->ebi->dev, "missing atmel,smc- properties in %s", + np->full_name); + return -EINVAL; + } - return 0; + return required; } -static int at91sam9_ebi_init(struct at91_ebi *ebi) +static void at91sam9_ebi_apply_config(struct at91_ebi_dev *ebid, + struct at91_ebi_dev_config *conf) { - struct at91sam9_smc_generic_fields *fields = &ebi->sam9; - struct reg_field field = REG_FIELD(0, 0, 31); - - field.id_size = fls(ebi->caps->available_cs); - field.id_offset = AT91SAM9_SMC_GENERIC_BLK_SZ; - - field.reg = AT91SAM9_SMC_SETUP(AT91SAM9_SMC_GENERIC); - fields->setup = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap, - field); - if (IS_ERR(fields->setup)) - return PTR_ERR(fields->setup); - - field.reg = AT91SAM9_SMC_PULSE(AT91SAM9_SMC_GENERIC); - fields->pulse = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap, - field); - if (IS_ERR(fields->pulse)) - return PTR_ERR(fields->pulse); - - field.reg = AT91SAM9_SMC_CYCLE(AT91SAM9_SMC_GENERIC); - fields->cycle = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap, - field); - if (IS_ERR(fields->cycle)) - return PTR_ERR(fields->cycle); - - field.reg = AT91SAM9_SMC_MODE(AT91SAM9_SMC_GENERIC); - fields->mode = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap, - field); - return PTR_ERR_OR_ZERO(fields->mode); + atmel_smc_cs_conf_apply(ebid->ebi->smc.regmap, conf->cs, + &conf->smcconf); } -static int sama5d3_ebi_init(struct at91_ebi *ebi) +static void sama5_ebi_apply_config(struct at91_ebi_dev *ebid, + struct at91_ebi_dev_config *conf) { - struct at91sam9_smc_generic_fields *fields = &ebi->sam9; - struct reg_field field = REG_FIELD(0, 0, 31); - - field.id_size = fls(ebi->caps->available_cs); - field.id_offset = SAMA5_SMC_GENERIC_BLK_SZ; - - field.reg = AT91SAM9_SMC_SETUP(SAMA5_SMC_GENERIC); - fields->setup = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap, - field); - if (IS_ERR(fields->setup)) - return PTR_ERR(fields->setup); - - field.reg = AT91SAM9_SMC_PULSE(SAMA5_SMC_GENERIC); - fields->pulse = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap, - field); - if (IS_ERR(fields->pulse)) - return PTR_ERR(fields->pulse); - - field.reg = AT91SAM9_SMC_CYCLE(SAMA5_SMC_GENERIC); - fields->cycle = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap, - field); - if (IS_ERR(fields->cycle)) - return PTR_ERR(fields->cycle); - - field.reg = SAMA5_SMC_MODE(SAMA5_SMC_GENERIC); - fields->mode = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap, - field); - return PTR_ERR_OR_ZERO(fields->mode); + atmel_hsmc_cs_conf_apply(ebid->ebi->smc.regmap, conf->cs, + &conf->smcconf); } static int at91_ebi_dev_setup(struct at91_ebi *ebi, struct device_node *np, @@ -508,9 +346,7 @@ static int at91_ebi_dev_setup(struct at91_ebi *ebi, struct device_node *np, if (apply) { conf.cs = cs; - ret = caps->apply_config(ebid, &conf); - if (ret) - return ret; + caps->apply_config(ebid, &conf); } caps->get_config(ebid, &ebid->configs[i]); @@ -539,9 +375,8 @@ static const struct at91_ebi_caps at91sam9260_ebi_caps = { .available_cs = 0xff, .ebi_csa = &at91sam9260_ebi_csa, .get_config = at91sam9_ebi_get_config, - .xlate_config = at91sam9_ebi_xslate_config, + .xlate_config = at91_ebi_xslate_smc_config, .apply_config = at91sam9_ebi_apply_config, - .init = at91sam9_ebi_init, }; static const struct reg_field at91sam9261_ebi_csa = @@ -552,9 +387,8 @@ static const struct at91_ebi_caps at91sam9261_ebi_caps = { .available_cs = 0xff, .ebi_csa = &at91sam9261_ebi_csa, .get_config = at91sam9_ebi_get_config, - .xlate_config = at91sam9_ebi_xslate_config, + .xlate_config = at91_ebi_xslate_smc_config, .apply_config = at91sam9_ebi_apply_config, - .init = at91sam9_ebi_init, }; static const struct reg_field at91sam9263_ebi0_csa = @@ -565,9 +399,8 @@ static const struct at91_ebi_caps at91sam9263_ebi0_caps = { .available_cs = 0x3f, .ebi_csa = &at91sam9263_ebi0_csa, .get_config = at91sam9_ebi_get_config, - .xlate_config = at91sam9_ebi_xslate_config, + .xlate_config = at91_ebi_xslate_smc_config, .apply_config = at91sam9_ebi_apply_config, - .init = at91sam9_ebi_init, }; static const struct reg_field at91sam9263_ebi1_csa = @@ -578,9 +411,8 @@ static const struct at91_ebi_caps at91sam9263_ebi1_caps = { .available_cs = 0x7, .ebi_csa = &at91sam9263_ebi1_csa, .get_config = at91sam9_ebi_get_config, - .xlate_config = at91sam9_ebi_xslate_config, + .xlate_config = at91_ebi_xslate_smc_config, .apply_config = at91sam9_ebi_apply_config, - .init = at91sam9_ebi_init, }; static const struct reg_field at91sam9rl_ebi_csa = @@ -591,9 +423,8 @@ static const struct at91_ebi_caps at91sam9rl_ebi_caps = { .available_cs = 0x3f, .ebi_csa = &at91sam9rl_ebi_csa, .get_config = at91sam9_ebi_get_config, - .xlate_config = at91sam9_ebi_xslate_config, + .xlate_config = at91_ebi_xslate_smc_config, .apply_config = at91sam9_ebi_apply_config, - .init = at91sam9_ebi_init, }; static const struct reg_field at91sam9g45_ebi_csa = @@ -604,26 +435,23 @@ static const struct at91_ebi_caps at91sam9g45_ebi_caps = { .available_cs = 0x3f, .ebi_csa = &at91sam9g45_ebi_csa, .get_config = at91sam9_ebi_get_config, - .xlate_config = at91sam9_ebi_xslate_config, + .xlate_config = at91_ebi_xslate_smc_config, .apply_config = at91sam9_ebi_apply_config, - .init = at91sam9_ebi_init, }; static const struct at91_ebi_caps at91sam9x5_ebi_caps = { .available_cs = 0x3f, .ebi_csa = &at91sam9263_ebi0_csa, .get_config = at91sam9_ebi_get_config, - .xlate_config = at91sam9_ebi_xslate_config, + .xlate_config = at91_ebi_xslate_smc_config, .apply_config = at91sam9_ebi_apply_config, - .init = at91sam9_ebi_init, }; static const struct at91_ebi_caps sama5d3_ebi_caps = { .available_cs = 0xf, - .get_config = at91sam9_ebi_get_config, - .xlate_config = at91sam9_ebi_xslate_config, - .apply_config = at91sam9_ebi_apply_config, - .init = sama5d3_ebi_init, + .get_config = sama5_ebi_get_config, + .xlate_config = at91_ebi_xslate_smc_config, + .apply_config = sama5_ebi_apply_config, }; static const struct of_device_id at91_ebi_id_table[] = { @@ -745,10 +573,6 @@ static int at91_ebi_probe(struct platform_device *pdev) return PTR_ERR(ebi->ebi_csa); } - ret = ebi->caps->init(ebi); - if (ret) - return ret; - ret = of_property_read_u32(np, "#address-cells", &val); if (ret) { dev_err(dev, "missing #address-cells property\n"); -- cgit v1.2.3-59-g8ed1b From d9f81dad161f3f0b339a65e0e6ac5db898119027 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 16 Mar 2017 09:30:30 +0100 Subject: memory: atmel-ebi: Stop using reg_field objects for simple things Turn the ->ebi_csa reg field into a simple offset that can be used with with the matrix regmap. Using reg fields was overkill for this use case. Signed-off-by: Boris Brezillon Acked-by: Nicolas Ferre Signed-off-by: Lee Jones --- drivers/memory/atmel-ebi.c | 55 +++++++++++----------------------------------- 1 file changed, 13 insertions(+), 42 deletions(-) diff --git a/drivers/memory/atmel-ebi.c b/drivers/memory/atmel-ebi.c index e1b8590e7d23..8640da386d32 100644 --- a/drivers/memory/atmel-ebi.c +++ b/drivers/memory/atmel-ebi.c @@ -35,7 +35,7 @@ struct at91_ebi_dev { struct at91_ebi_caps { unsigned int available_cs; - const struct reg_field *ebi_csa; + unsigned int ebi_csa_offs; void (*get_config)(struct at91_ebi_dev *ebid, struct at91_ebi_dev_config *conf); int (*xlate_config)(struct at91_ebi_dev *ebid, @@ -52,7 +52,6 @@ struct at91_ebi { struct regmap *regmap; struct clk *clk; } smc; - struct regmap_field *ebi_csa; struct device *dev; const struct at91_ebi_caps *caps; @@ -355,9 +354,10 @@ static int at91_ebi_dev_setup(struct at91_ebi *ebi, struct device_node *np, * Attach the EBI device to the generic SMC logic if at least * one "atmel,smc-" property is present. */ - if (ebi->ebi_csa && apply) - regmap_field_update_bits(ebi->ebi_csa, - BIT(cs), 0); + if (ebi->caps->ebi_csa_offs && apply) + regmap_update_bits(ebi->matrix, + ebi->caps->ebi_csa_offs, + BIT(cs), 0); i++; } @@ -367,73 +367,49 @@ static int at91_ebi_dev_setup(struct at91_ebi *ebi, struct device_node *np, return 0; } -static const struct reg_field at91sam9260_ebi_csa = - REG_FIELD(AT91SAM9260_MATRIX_EBICSA, 0, - AT91_MATRIX_EBI_NUM_CS - 1); - static const struct at91_ebi_caps at91sam9260_ebi_caps = { .available_cs = 0xff, - .ebi_csa = &at91sam9260_ebi_csa, + .ebi_csa_offs = AT91SAM9260_MATRIX_EBICSA, .get_config = at91sam9_ebi_get_config, .xlate_config = at91_ebi_xslate_smc_config, .apply_config = at91sam9_ebi_apply_config, }; -static const struct reg_field at91sam9261_ebi_csa = - REG_FIELD(AT91SAM9261_MATRIX_EBICSA, 0, - AT91_MATRIX_EBI_NUM_CS - 1); - static const struct at91_ebi_caps at91sam9261_ebi_caps = { .available_cs = 0xff, - .ebi_csa = &at91sam9261_ebi_csa, + .ebi_csa_offs = AT91SAM9261_MATRIX_EBICSA, .get_config = at91sam9_ebi_get_config, .xlate_config = at91_ebi_xslate_smc_config, .apply_config = at91sam9_ebi_apply_config, }; -static const struct reg_field at91sam9263_ebi0_csa = - REG_FIELD(AT91SAM9263_MATRIX_EBI0CSA, 0, - AT91_MATRIX_EBI_NUM_CS - 1); - static const struct at91_ebi_caps at91sam9263_ebi0_caps = { .available_cs = 0x3f, - .ebi_csa = &at91sam9263_ebi0_csa, + .ebi_csa_offs = AT91SAM9263_MATRIX_EBI0CSA, .get_config = at91sam9_ebi_get_config, .xlate_config = at91_ebi_xslate_smc_config, .apply_config = at91sam9_ebi_apply_config, }; -static const struct reg_field at91sam9263_ebi1_csa = - REG_FIELD(AT91SAM9263_MATRIX_EBI1CSA, 0, - AT91_MATRIX_EBI_NUM_CS - 1); - static const struct at91_ebi_caps at91sam9263_ebi1_caps = { .available_cs = 0x7, - .ebi_csa = &at91sam9263_ebi1_csa, + .ebi_csa_offs = AT91SAM9263_MATRIX_EBI1CSA, .get_config = at91sam9_ebi_get_config, .xlate_config = at91_ebi_xslate_smc_config, .apply_config = at91sam9_ebi_apply_config, }; -static const struct reg_field at91sam9rl_ebi_csa = - REG_FIELD(AT91SAM9RL_MATRIX_EBICSA, 0, - AT91_MATRIX_EBI_NUM_CS - 1); - static const struct at91_ebi_caps at91sam9rl_ebi_caps = { .available_cs = 0x3f, - .ebi_csa = &at91sam9rl_ebi_csa, + .ebi_csa_offs = AT91SAM9RL_MATRIX_EBICSA, .get_config = at91sam9_ebi_get_config, .xlate_config = at91_ebi_xslate_smc_config, .apply_config = at91sam9_ebi_apply_config, }; -static const struct reg_field at91sam9g45_ebi_csa = - REG_FIELD(AT91SAM9G45_MATRIX_EBICSA, 0, - AT91_MATRIX_EBI_NUM_CS - 1); - static const struct at91_ebi_caps at91sam9g45_ebi_caps = { .available_cs = 0x3f, - .ebi_csa = &at91sam9g45_ebi_csa, + .ebi_csa_offs = AT91SAM9G45_MATRIX_EBICSA, .get_config = at91sam9_ebi_get_config, .xlate_config = at91_ebi_xslate_smc_config, .apply_config = at91sam9_ebi_apply_config, @@ -441,7 +417,7 @@ static const struct at91_ebi_caps at91sam9g45_ebi_caps = { static const struct at91_ebi_caps at91sam9x5_ebi_caps = { .available_cs = 0x3f, - .ebi_csa = &at91sam9263_ebi0_csa, + .ebi_csa_offs = AT91SAM9X5_MATRIX_EBICSA, .get_config = at91sam9_ebi_get_config, .xlate_config = at91_ebi_xslate_smc_config, .apply_config = at91sam9_ebi_apply_config, @@ -561,16 +537,11 @@ static int at91_ebi_probe(struct platform_device *pdev) * The sama5d3 does not provide an EBICSA register and thus does need * to access the matrix registers. */ - if (ebi->caps->ebi_csa) { + if (ebi->caps->ebi_csa_offs) { ebi->matrix = syscon_regmap_lookup_by_phandle(np, "atmel,matrix"); if (IS_ERR(ebi->matrix)) return PTR_ERR(ebi->matrix); - - ebi->ebi_csa = regmap_field_alloc(ebi->matrix, - *ebi->caps->ebi_csa); - if (IS_ERR(ebi->ebi_csa)) - return PTR_ERR(ebi->ebi_csa); } ret = of_property_read_u32(np, "#address-cells", &val); -- cgit v1.2.3-59-g8ed1b From 0d69080d9e01d5d60f1887def2080ce3f66f5856 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 16 Mar 2017 09:30:31 +0100 Subject: mfd: syscon: atmel-smc: Remove unused helpers/macros All macros prefixed with AT91[SAM9]_SMC have been replaced by equivalent definitions prefixed with ATMEL_SMC, and the at91sam9_smc_xxxx() helpers are no longer used. Drop these definitions before someone starts using them again. Signed-off-by: Boris Brezillon Acked-by: Nicolas Ferre Signed-off-by: Lee Jones --- include/linux/mfd/syscon/atmel-smc.h | 152 ----------------------------------- 1 file changed, 152 deletions(-) diff --git a/include/linux/mfd/syscon/atmel-smc.h b/include/linux/mfd/syscon/atmel-smc.h index 00e6e3c8ee6f..afa266169800 100644 --- a/include/linux/mfd/syscon/atmel-smc.h +++ b/include/linux/mfd/syscon/atmel-smc.h @@ -17,58 +17,6 @@ #include #include -#define AT91SAM9_SMC_GENERIC 0x00 -#define AT91SAM9_SMC_GENERIC_BLK_SZ 0x10 - -#define SAMA5_SMC_GENERIC 0x600 -#define SAMA5_SMC_GENERIC_BLK_SZ 0x14 - -#define AT91SAM9_SMC_SETUP(o) ((o) + 0x00) -#define AT91SAM9_SMC_NWESETUP(x) (x) -#define AT91SAM9_SMC_NCS_WRSETUP(x) ((x) << 8) -#define AT91SAM9_SMC_NRDSETUP(x) ((x) << 16) -#define AT91SAM9_SMC_NCS_NRDSETUP(x) ((x) << 24) - -#define AT91SAM9_SMC_PULSE(o) ((o) + 0x04) -#define AT91SAM9_SMC_NWEPULSE(x) (x) -#define AT91SAM9_SMC_NCS_WRPULSE(x) ((x) << 8) -#define AT91SAM9_SMC_NRDPULSE(x) ((x) << 16) -#define AT91SAM9_SMC_NCS_NRDPULSE(x) ((x) << 24) - -#define AT91SAM9_SMC_CYCLE(o) ((o) + 0x08) -#define AT91SAM9_SMC_NWECYCLE(x) (x) -#define AT91SAM9_SMC_NRDCYCLE(x) ((x) << 16) - -#define AT91SAM9_SMC_MODE(o) ((o) + 0x0c) -#define SAMA5_SMC_MODE(o) ((o) + 0x10) -#define AT91_SMC_READMODE BIT(0) -#define AT91_SMC_READMODE_NCS (0 << 0) -#define AT91_SMC_READMODE_NRD (1 << 0) -#define AT91_SMC_WRITEMODE BIT(1) -#define AT91_SMC_WRITEMODE_NCS (0 << 1) -#define AT91_SMC_WRITEMODE_NWE (1 << 1) -#define AT91_SMC_EXNWMODE GENMASK(5, 4) -#define AT91_SMC_EXNWMODE_DISABLE (0 << 4) -#define AT91_SMC_EXNWMODE_FROZEN (2 << 4) -#define AT91_SMC_EXNWMODE_READY (3 << 4) -#define AT91_SMC_BAT BIT(8) -#define AT91_SMC_BAT_SELECT (0 << 8) -#define AT91_SMC_BAT_WRITE (1 << 8) -#define AT91_SMC_DBW GENMASK(13, 12) -#define AT91_SMC_DBW_8 (0 << 12) -#define AT91_SMC_DBW_16 (1 << 12) -#define AT91_SMC_DBW_32 (2 << 12) -#define AT91_SMC_TDF GENMASK(19, 16) -#define AT91_SMC_TDF_(x) ((((x) - 1) << 16) & AT91_SMC_TDF) -#define AT91_SMC_TDF_MAX 16 -#define AT91_SMC_TDFMODE_OPTIMIZED BIT(20) -#define AT91_SMC_PMEN BIT(24) -#define AT91_SMC_PS GENMASK(29, 28) -#define AT91_SMC_PS_4 (0 << 28) -#define AT91_SMC_PS_8 (1 << 28) -#define AT91_SMC_PS_16 (2 << 28) -#define AT91_SMC_PS_32 (3 << 28) - #define ATMEL_SMC_SETUP(cs) (((cs) * 0x10)) #define ATMEL_HSMC_SETUP(cs) (0x600 + ((cs) * 0x14)) #define ATMEL_SMC_PULSE(cs) (((cs) * 0x10) + 0x4) @@ -157,104 +105,4 @@ void atmel_smc_cs_conf_get(struct regmap *regmap, int cs, void atmel_hsmc_cs_conf_get(struct regmap *regmap, int cs, struct atmel_smc_cs_conf *conf); -/* - * This function converts a setup timing expressed in nanoseconds into an - * encoded value that can be written in the SMC_SETUP register. - * - * The following formula is described in atmel datasheets (section - * "SMC Setup Register"): - * - * setup length = (128* SETUP[5] + SETUP[4:0]) - * - * where setup length is the timing expressed in cycles. - */ -static inline u32 at91sam9_smc_setup_ns_to_cycles(unsigned int clk_rate, - u32 timing_ns) -{ - u32 clk_period = DIV_ROUND_UP(NSEC_PER_SEC, clk_rate); - u32 coded_cycles = 0; - u32 cycles; - - cycles = DIV_ROUND_UP(timing_ns, clk_period); - if (cycles / 32) { - coded_cycles |= 1 << 5; - if (cycles < 128) - cycles = 0; - } - - coded_cycles |= cycles % 32; - - return coded_cycles; -} - -/* - * This function converts a pulse timing expressed in nanoseconds into an - * encoded value that can be written in the SMC_PULSE register. - * - * The following formula is described in atmel datasheets (section - * "SMC Pulse Register"): - * - * pulse length = (256* PULSE[6] + PULSE[5:0]) - * - * where pulse length is the timing expressed in cycles. - */ -static inline u32 at91sam9_smc_pulse_ns_to_cycles(unsigned int clk_rate, - u32 timing_ns) -{ - u32 clk_period = DIV_ROUND_UP(NSEC_PER_SEC, clk_rate); - u32 coded_cycles = 0; - u32 cycles; - - cycles = DIV_ROUND_UP(timing_ns, clk_period); - if (cycles / 64) { - coded_cycles |= 1 << 6; - if (cycles < 256) - cycles = 0; - } - - coded_cycles |= cycles % 64; - - return coded_cycles; -} - -/* - * This function converts a cycle timing expressed in nanoseconds into an - * encoded value that can be written in the SMC_CYCLE register. - * - * The following formula is described in atmel datasheets (section - * "SMC Cycle Register"): - * - * cycle length = (CYCLE[8:7]*256 + CYCLE[6:0]) - * - * where cycle length is the timing expressed in cycles. - */ -static inline u32 at91sam9_smc_cycle_ns_to_cycles(unsigned int clk_rate, - u32 timing_ns) -{ - u32 clk_period = DIV_ROUND_UP(NSEC_PER_SEC, clk_rate); - u32 coded_cycles = 0; - u32 cycles; - - cycles = DIV_ROUND_UP(timing_ns, clk_period); - if (cycles / 128) { - coded_cycles = cycles / 256; - cycles %= 256; - if (cycles >= 128) { - coded_cycles++; - cycles = 0; - } - - if (coded_cycles > 0x3) { - coded_cycles = 0x3; - cycles = 0x7f; - } - - coded_cycles <<= 7; - } - - coded_cycles |= cycles % 128; - - return coded_cycles; -} - #endif /* _LINUX_MFD_SYSCON_ATMEL_SMC_H_ */ -- cgit v1.2.3-59-g8ed1b From 9453fa4694156108d37bb2d03658286ce85ca4f0 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 16 Mar 2017 09:30:32 +0100 Subject: memory: atmel-ebi: Change naming scheme The EBI block is not only available on at91 SoCs, but also on avr32 ones. Change the structure and function prefixes from at91_ebi to atmel_ebi to match this fact and make the prefix and driver name consistent. Signed-off-by: Boris Brezillon Acked-by: Nicolas Ferre Signed-off-by: Lee Jones --- drivers/memory/atmel-ebi.c | 120 ++++++++++++++++++++++----------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/drivers/memory/atmel-ebi.c b/drivers/memory/atmel-ebi.c index 8640da386d32..8beba9a60d1c 100644 --- a/drivers/memory/atmel-ebi.c +++ b/drivers/memory/atmel-ebi.c @@ -18,34 +18,34 @@ #include #include -struct at91_ebi_dev_config { +struct atmel_ebi_dev_config { int cs; struct atmel_smc_cs_conf smcconf; }; -struct at91_ebi; +struct atmel_ebi; -struct at91_ebi_dev { +struct atmel_ebi_dev { struct list_head node; - struct at91_ebi *ebi; + struct atmel_ebi *ebi; u32 mode; int numcs; - struct at91_ebi_dev_config configs[]; + struct atmel_ebi_dev_config configs[]; }; -struct at91_ebi_caps { +struct atmel_ebi_caps { unsigned int available_cs; unsigned int ebi_csa_offs; - void (*get_config)(struct at91_ebi_dev *ebid, - struct at91_ebi_dev_config *conf); - int (*xlate_config)(struct at91_ebi_dev *ebid, + void (*get_config)(struct atmel_ebi_dev *ebid, + struct atmel_ebi_dev_config *conf); + int (*xlate_config)(struct atmel_ebi_dev *ebid, struct device_node *configs_np, - struct at91_ebi_dev_config *conf); - void (*apply_config)(struct at91_ebi_dev *ebid, - struct at91_ebi_dev_config *conf); + struct atmel_ebi_dev_config *conf); + void (*apply_config)(struct atmel_ebi_dev *ebid, + struct atmel_ebi_dev_config *conf); }; -struct at91_ebi { +struct atmel_ebi { struct clk *clk; struct regmap *matrix; struct { @@ -54,7 +54,7 @@ struct at91_ebi { } smc; struct device *dev; - const struct at91_ebi_caps *caps; + const struct atmel_ebi_caps *caps; struct list_head devs; }; @@ -74,15 +74,15 @@ struct atmel_smc_timing_xlate { #define ATMEL_SMC_CYCLE_XLATE(nm, pos) \ { .name = nm, .converter = atmel_smc_cs_conf_set_setup, .shift = pos} -static void at91sam9_ebi_get_config(struct at91_ebi_dev *ebid, - struct at91_ebi_dev_config *conf) +static void at91sam9_ebi_get_config(struct atmel_ebi_dev *ebid, + struct atmel_ebi_dev_config *conf) { atmel_smc_cs_conf_get(ebid->ebi->smc.regmap, conf->cs, &conf->smcconf); } -static void sama5_ebi_get_config(struct at91_ebi_dev *ebid, - struct at91_ebi_dev_config *conf) +static void sama5_ebi_get_config(struct atmel_ebi_dev *ebid, + struct atmel_ebi_dev_config *conf) { atmel_hsmc_cs_conf_get(ebid->ebi->smc.regmap, conf->cs, &conf->smcconf); @@ -105,9 +105,9 @@ static const struct atmel_smc_timing_xlate timings_xlate_table[] = { ATMEL_SMC_CYCLE_XLATE("atmel,smc-nwe-cycle-ns", ATMEL_SMC_NWE_SHIFT), }; -static int at91_ebi_xslate_smc_timings(struct at91_ebi_dev *ebid, - struct device_node *np, - struct atmel_smc_cs_conf *smcconf) +static int atmel_ebi_xslate_smc_timings(struct atmel_ebi_dev *ebid, + struct device_node *np, + struct atmel_smc_cs_conf *smcconf) { unsigned int clk_rate = clk_get_rate(ebid->ebi->clk); unsigned int clk_period_ns = NSEC_PER_SEC / clk_rate; @@ -164,9 +164,9 @@ out: return required; } -static int at91_ebi_xslate_smc_config(struct at91_ebi_dev *ebid, - struct device_node *np, - struct at91_ebi_dev_config *conf) +static int atmel_ebi_xslate_smc_config(struct atmel_ebi_dev *ebid, + struct device_node *np, + struct atmel_ebi_dev_config *conf) { struct atmel_smc_cs_conf *smcconf = &conf->smcconf; bool required = false; @@ -262,7 +262,7 @@ static int at91_ebi_xslate_smc_config(struct at91_ebi_dev *ebid, required = true; } - ret = at91_ebi_xslate_smc_timings(ebid, np, &conf->smcconf); + ret = atmel_ebi_xslate_smc_timings(ebid, np, &conf->smcconf); if (ret) return -EINVAL; @@ -275,27 +275,27 @@ static int at91_ebi_xslate_smc_config(struct at91_ebi_dev *ebid, return required; } -static void at91sam9_ebi_apply_config(struct at91_ebi_dev *ebid, - struct at91_ebi_dev_config *conf) +static void at91sam9_ebi_apply_config(struct atmel_ebi_dev *ebid, + struct atmel_ebi_dev_config *conf) { atmel_smc_cs_conf_apply(ebid->ebi->smc.regmap, conf->cs, &conf->smcconf); } -static void sama5_ebi_apply_config(struct at91_ebi_dev *ebid, - struct at91_ebi_dev_config *conf) +static void sama5_ebi_apply_config(struct atmel_ebi_dev *ebid, + struct atmel_ebi_dev_config *conf) { atmel_hsmc_cs_conf_apply(ebid->ebi->smc.regmap, conf->cs, &conf->smcconf); } -static int at91_ebi_dev_setup(struct at91_ebi *ebi, struct device_node *np, - int reg_cells) +static int atmel_ebi_dev_setup(struct atmel_ebi *ebi, struct device_node *np, + int reg_cells) { - const struct at91_ebi_caps *caps = ebi->caps; - struct at91_ebi_dev_config conf = { }; + const struct atmel_ebi_caps *caps = ebi->caps; + struct atmel_ebi_dev_config conf = { }; struct device *dev = ebi->dev; - struct at91_ebi_dev *ebid; + struct atmel_ebi_dev *ebid; unsigned long cslines = 0; int ret, numcs = 0, nentries, i; bool apply = false; @@ -367,70 +367,70 @@ static int at91_ebi_dev_setup(struct at91_ebi *ebi, struct device_node *np, return 0; } -static const struct at91_ebi_caps at91sam9260_ebi_caps = { +static const struct atmel_ebi_caps at91sam9260_ebi_caps = { .available_cs = 0xff, .ebi_csa_offs = AT91SAM9260_MATRIX_EBICSA, .get_config = at91sam9_ebi_get_config, - .xlate_config = at91_ebi_xslate_smc_config, + .xlate_config = atmel_ebi_xslate_smc_config, .apply_config = at91sam9_ebi_apply_config, }; -static const struct at91_ebi_caps at91sam9261_ebi_caps = { +static const struct atmel_ebi_caps at91sam9261_ebi_caps = { .available_cs = 0xff, .ebi_csa_offs = AT91SAM9261_MATRIX_EBICSA, .get_config = at91sam9_ebi_get_config, - .xlate_config = at91_ebi_xslate_smc_config, + .xlate_config = atmel_ebi_xslate_smc_config, .apply_config = at91sam9_ebi_apply_config, }; -static const struct at91_ebi_caps at91sam9263_ebi0_caps = { +static const struct atmel_ebi_caps at91sam9263_ebi0_caps = { .available_cs = 0x3f, .ebi_csa_offs = AT91SAM9263_MATRIX_EBI0CSA, .get_config = at91sam9_ebi_get_config, - .xlate_config = at91_ebi_xslate_smc_config, + .xlate_config = atmel_ebi_xslate_smc_config, .apply_config = at91sam9_ebi_apply_config, }; -static const struct at91_ebi_caps at91sam9263_ebi1_caps = { +static const struct atmel_ebi_caps at91sam9263_ebi1_caps = { .available_cs = 0x7, .ebi_csa_offs = AT91SAM9263_MATRIX_EBI1CSA, .get_config = at91sam9_ebi_get_config, - .xlate_config = at91_ebi_xslate_smc_config, + .xlate_config = atmel_ebi_xslate_smc_config, .apply_config = at91sam9_ebi_apply_config, }; -static const struct at91_ebi_caps at91sam9rl_ebi_caps = { +static const struct atmel_ebi_caps at91sam9rl_ebi_caps = { .available_cs = 0x3f, .ebi_csa_offs = AT91SAM9RL_MATRIX_EBICSA, .get_config = at91sam9_ebi_get_config, - .xlate_config = at91_ebi_xslate_smc_config, + .xlate_config = atmel_ebi_xslate_smc_config, .apply_config = at91sam9_ebi_apply_config, }; -static const struct at91_ebi_caps at91sam9g45_ebi_caps = { +static const struct atmel_ebi_caps at91sam9g45_ebi_caps = { .available_cs = 0x3f, .ebi_csa_offs = AT91SAM9G45_MATRIX_EBICSA, .get_config = at91sam9_ebi_get_config, - .xlate_config = at91_ebi_xslate_smc_config, + .xlate_config = atmel_ebi_xslate_smc_config, .apply_config = at91sam9_ebi_apply_config, }; -static const struct at91_ebi_caps at91sam9x5_ebi_caps = { +static const struct atmel_ebi_caps at91sam9x5_ebi_caps = { .available_cs = 0x3f, .ebi_csa_offs = AT91SAM9X5_MATRIX_EBICSA, .get_config = at91sam9_ebi_get_config, - .xlate_config = at91_ebi_xslate_smc_config, + .xlate_config = atmel_ebi_xslate_smc_config, .apply_config = at91sam9_ebi_apply_config, }; -static const struct at91_ebi_caps sama5d3_ebi_caps = { +static const struct atmel_ebi_caps sama5d3_ebi_caps = { .available_cs = 0xf, .get_config = sama5_ebi_get_config, - .xlate_config = at91_ebi_xslate_smc_config, + .xlate_config = atmel_ebi_xslate_smc_config, .apply_config = sama5_ebi_apply_config, }; -static const struct of_device_id at91_ebi_id_table[] = { +static const struct of_device_id atmel_ebi_id_table[] = { { .compatible = "atmel,at91sam9260-ebi", .data = &at91sam9260_ebi_caps, @@ -466,7 +466,7 @@ static const struct of_device_id at91_ebi_id_table[] = { { /* sentinel */ } }; -static int at91_ebi_dev_disable(struct at91_ebi *ebi, struct device_node *np) +static int atmel_ebi_dev_disable(struct atmel_ebi *ebi, struct device_node *np) { struct device *dev = ebi->dev; struct property *newprop; @@ -488,17 +488,17 @@ static int at91_ebi_dev_disable(struct at91_ebi *ebi, struct device_node *np) return of_update_property(np, newprop); } -static int at91_ebi_probe(struct platform_device *pdev) +static int atmel_ebi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *child, *np = dev->of_node, *smc_np; const struct of_device_id *match; - struct at91_ebi *ebi; + struct atmel_ebi *ebi; int ret, reg_cells; struct clk *clk; u32 val; - match = of_match_device(at91_ebi_id_table, dev); + match = of_match_device(atmel_ebi_id_table, dev); if (!match || !match->data) return -EINVAL; @@ -564,12 +564,12 @@ static int at91_ebi_probe(struct platform_device *pdev) if (!of_find_property(child, "reg", NULL)) continue; - ret = at91_ebi_dev_setup(ebi, child, reg_cells); + ret = atmel_ebi_dev_setup(ebi, child, reg_cells); if (ret) { dev_err(dev, "failed to configure EBI bus for %s, disabling the device", child->full_name); - ret = at91_ebi_dev_disable(ebi, child); + ret = atmel_ebi_dev_disable(ebi, child); if (ret) return ret; } @@ -578,10 +578,10 @@ static int at91_ebi_probe(struct platform_device *pdev) return of_platform_populate(np, NULL, NULL, dev); } -static struct platform_driver at91_ebi_driver = { +static struct platform_driver atmel_ebi_driver = { .driver = { .name = "atmel-ebi", - .of_match_table = at91_ebi_id_table, + .of_match_table = atmel_ebi_id_table, }, }; -builtin_platform_driver_probe(at91_ebi_driver, at91_ebi_probe); +builtin_platform_driver_probe(atmel_ebi_driver, atmel_ebi_probe); -- cgit v1.2.3-59-g8ed1b From aaa572b945876619ebd114d5a456d1250f267b9a Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 16 Mar 2017 09:30:33 +0100 Subject: memory: atmel-ebi: Add missing ->numcs assignment ebid->numcs is never assigned, set it to numcs after allocating the EBI dev object. Signed-off-by: Boris Brezillon Acked-by: Nicolas Ferre Signed-off-by: Lee Jones --- drivers/memory/atmel-ebi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/memory/atmel-ebi.c b/drivers/memory/atmel-ebi.c index 8beba9a60d1c..7d24a957e98c 100644 --- a/drivers/memory/atmel-ebi.c +++ b/drivers/memory/atmel-ebi.c @@ -332,6 +332,7 @@ static int atmel_ebi_dev_setup(struct atmel_ebi *ebi, struct device_node *np, return -ENOMEM; ebid->ebi = ebi; + ebid->numcs = numcs; ret = caps->xlate_config(ebid, np, &conf); if (ret < 0) -- cgit v1.2.3-59-g8ed1b From 4407319d8fff93308c2afe40e7c2a7dc564ec122 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 16 Mar 2017 09:30:34 +0100 Subject: memory: atmel-ebi: Add PM ops Add a ->resume() hook to make sure the EBI dev configs are correctly restored when resuming the platform. Signed-off-by: Boris Brezillon Acked-by: Nicolas Ferre Signed-off-by: Lee Jones --- drivers/memory/atmel-ebi.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/memory/atmel-ebi.c b/drivers/memory/atmel-ebi.c index 7d24a957e98c..35910f945bfa 100644 --- a/drivers/memory/atmel-ebi.c +++ b/drivers/memory/atmel-ebi.c @@ -507,6 +507,8 @@ static int atmel_ebi_probe(struct platform_device *pdev) if (!ebi) return -ENOMEM; + platform_set_drvdata(pdev, ebi); + INIT_LIST_HEAD(&ebi->devs); ebi->caps = match->data; ebi->dev = dev; @@ -579,10 +581,28 @@ static int atmel_ebi_probe(struct platform_device *pdev) return of_platform_populate(np, NULL, NULL, dev); } +static int atmel_ebi_resume(struct device *dev) +{ + struct atmel_ebi *ebi = dev_get_drvdata(dev); + struct atmel_ebi_dev *ebid; + + list_for_each_entry(ebid, &ebi->devs, node) { + int i; + + for (i = 0; i < ebid->numcs; i++) + ebid->ebi->caps->apply_config(ebid, &ebid->configs[i]); + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(atmel_ebi_pm_ops, NULL, atmel_ebi_resume); + static struct platform_driver atmel_ebi_driver = { .driver = { .name = "atmel-ebi", .of_match_table = atmel_ebi_id_table, + .pm = &atmel_ebi_pm_ops, }, }; builtin_platform_driver_probe(atmel_ebi_driver, atmel_ebi_probe); -- cgit v1.2.3-59-g8ed1b From b4ccc4d2e82f7c7f8304f44544bdefcd16234582 Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Thu, 30 Mar 2017 16:35:40 -0700 Subject: mfd: bxtwc: Remove unnecessary i2c_addr checks in ipc calls In the following code block, BXTWC_DEVICE1_ADDR value is already fixed and hence there no need to check for if (!i2c_addr) in every ipc read/write calls. Even if this check is required it can be moved to probe function. i2c_addr = BXTWC_DEVICE1_ADDR; if (!i2c_addr) { dev_err(pmic->dev, "I2C address not set\n"); return -EINVAL; } This patch remove this extra check and adds some NULL parameter checks. Signed-off-by: Kuppuswamy Sathyanarayanan Signed-off-by: Lee Jones --- drivers/mfd/intel_soc_pmic_bxtwc.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/mfd/intel_soc_pmic_bxtwc.c b/drivers/mfd/intel_soc_pmic_bxtwc.c index 1496a862baa6..8c3cbf63c6ad 100644 --- a/drivers/mfd/intel_soc_pmic_bxtwc.c +++ b/drivers/mfd/intel_soc_pmic_bxtwc.c @@ -238,15 +238,14 @@ static int regmap_ipc_byte_reg_read(void *context, unsigned int reg, u8 ipc_out[4]; struct intel_soc_pmic *pmic = context; + if (!pmic) + return -EINVAL; + if (reg & REG_ADDR_MASK) i2c_addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT; - else { + else i2c_addr = BXTWC_DEVICE1_ADDR; - if (!i2c_addr) { - dev_err(pmic->dev, "I2C address not set\n"); - return -EINVAL; - } - } + reg &= REG_OFFSET_MASK; ipc_in[0] = reg; @@ -271,15 +270,14 @@ static int regmap_ipc_byte_reg_write(void *context, unsigned int reg, u8 ipc_in[3]; struct intel_soc_pmic *pmic = context; + if (!pmic) + return -EINVAL; + if (reg & REG_ADDR_MASK) i2c_addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT; - else { + else i2c_addr = BXTWC_DEVICE1_ADDR; - if (!i2c_addr) { - dev_err(pmic->dev, "I2C address not set\n"); - return -EINVAL; - } - } + reg &= REG_OFFSET_MASK; ipc_in[0] = reg; -- cgit v1.2.3-59-g8ed1b From b97eef5d77b53662b06603c1b18e21968029c925 Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Fri, 31 Mar 2017 10:12:39 +0200 Subject: mfd: stmpe: Fix bit clearing on STMPE1600 GPIO bits clearing on pins assigned to STMPE1600 had no effects due to missing "clear registers" settings within stmpe1600_regs[]. STMPE1600 does not have dedicated "clear registers", but single "set/clear registers", hence stmpe1600_regs[] "clear registers" (STMPE_IDX_GPCR_XXX) must be set to same value as "set registers" (STMPE_IDX_GPSR_XXX), ie STMPE1600_REG_GPSR_XXX. Signed-off-by: Hugues Fruchet Reviewed-by: Patrice Chotard Signed-off-by: Lee Jones --- drivers/mfd/stmpe.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index b0c7bcdaf5df..566caca4efd8 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -568,6 +568,8 @@ static const u8 stmpe1600_regs[] = { [STMPE_IDX_GPMR_CSB] = STMPE1600_REG_GPMR_MSB, [STMPE_IDX_GPSR_LSB] = STMPE1600_REG_GPSR_LSB, [STMPE_IDX_GPSR_CSB] = STMPE1600_REG_GPSR_MSB, + [STMPE_IDX_GPCR_LSB] = STMPE1600_REG_GPSR_LSB, + [STMPE_IDX_GPCR_CSB] = STMPE1600_REG_GPSR_MSB, [STMPE_IDX_GPDR_LSB] = STMPE1600_REG_GPDR_LSB, [STMPE_IDX_GPDR_CSB] = STMPE1600_REG_GPDR_MSB, [STMPE_IDX_IEGPIOR_LSB] = STMPE1600_REG_IEGPIOR_LSB, -- cgit v1.2.3-59-g8ed1b From e1569fb64e5efb31b835ac8e76bf1c18313da8e5 Mon Sep 17 00:00:00 2001 From: Steve Twiss Date: Tue, 28 Mar 2017 15:43:31 +0100 Subject: dt-bindings: mfd: DA9062/61 MFD binding Extend existing DA9062 binding information to include the DA9061 PMIC for MFD core and regulators. Add a da9062-onkey link to the existing onkey binding file. Add a da9062-thermal link to the new temperature monitoring binding file. Delete the da9062-watchdog section and replace it with a link to the new DA9061/62 binding information file. Acked-by: Rob Herring Signed-off-by: Steve Twiss Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/da9062.txt | 49 +++++++++++++++++------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/Documentation/devicetree/bindings/mfd/da9062.txt b/Documentation/devicetree/bindings/mfd/da9062.txt index 38802b54d48a..c0a418c27e9d 100644 --- a/Documentation/devicetree/bindings/mfd/da9062.txt +++ b/Documentation/devicetree/bindings/mfd/da9062.txt @@ -1,22 +1,39 @@ * Dialog DA9062 Power Management Integrated Circuit (PMIC) -DA9062 consists of a large and varied group of sub-devices: +Product information for the DA9062 and DA9061 devices can be found here: +- http://www.dialog-semiconductor.com/products/da9062 +- http://www.dialog-semiconductor.com/products/da9061 + +The DA9062 PMIC consists of: Device Supply Names Description ------ ------------ ----------- da9062-regulator : : LDOs & BUCKs da9062-rtc : : Real-Time Clock +da9062-onkey : : On Key +da9062-watchdog : : Watchdog Timer +da9062-thermal : : Thermal + +The DA9061 PMIC consists of: + +Device Supply Names Description +------ ------------ ----------- +da9062-regulator : : LDOs & BUCKs +da9062-onkey : : On Key da9062-watchdog : : Watchdog Timer +da9062-thermal : : Thermal ====== Required properties: -- compatible : Should be "dlg,da9062". +- compatible : Should be + "dlg,da9062" for DA9062 + "dlg,da9061" for DA9061 - reg : Specifies the I2C slave address (this defaults to 0x58 but it can be modified to match the chip's OTP settings). - interrupt-parent : Specifies the reference to the interrupt controller for - the DA9062. + the DA9062 or DA9061. - interrupts : IRQ line information. - interrupt-controller @@ -25,8 +42,8 @@ further information on IRQ bindings. Sub-nodes: -- regulators : This node defines the settings for the LDOs and BUCKs. The - DA9062 regulators are bound using their names listed below: +- regulators : This node defines the settings for the LDOs and BUCKs. + The DA9062 regulators are bound using their names listed below: buck1 : BUCK_1 buck2 : BUCK_2 @@ -37,19 +54,29 @@ Sub-nodes: ldo3 : LDO_3 ldo4 : LDO_4 + The DA9061 regulators are bound using their names listed below: + + buck1 : BUCK_1 + buck2 : BUCK_2 + buck3 : BUCK_3 + ldo1 : LDO_1 + ldo2 : LDO_2 + ldo3 : LDO_3 + ldo4 : LDO_4 + The component follows the standard regulator framework and the bindings details of individual regulator device can be found in: Documentation/devicetree/bindings/regulator/regulator.txt - - rtc : This node defines settings required for the Real-Time Clock associated with the DA9062. There are currently no entries in this binding, however compatible = "dlg,da9062-rtc" should be added if a node is created. -- watchdog: This node defines the settings for the watchdog driver associated - with the DA9062 PMIC. The compatible = "dlg,da9062-watchdog" should be added - if a node is created. +- onkey : See ../input/da9062-onkey.txt + +- watchdog: See ../watchdog/da9062-watchdog.txt +- thermal : See ../thermal/da9062-thermal.txt Example: @@ -64,10 +91,6 @@ Example: compatible = "dlg,da9062-rtc"; }; - watchdog { - compatible = "dlg,da9062-watchdog"; - }; - regulators { DA9062_BUCK1: buck1 { regulator-name = "BUCK1"; -- cgit v1.2.3-59-g8ed1b From 656211b1dfb9e0b68d4e634931432e29a6facf46 Mon Sep 17 00:00:00 2001 From: Steve Twiss Date: Mon, 3 Apr 2017 15:46:40 +0100 Subject: mfd: Add support for DA9061 MFD support for DA9061 is provided as part of the DA9062 device driver. The registers header file adds two new chip variant IDs defined in DA9061 and DA9062 hardware. The core header file adds new software enumerations for listing the valid DA9061 IRQs and a da9062_compatible_types enumeration for distinguishing between DA9061/62 devices in software. The core source code adds a new .compatible of_device_id entry. This is extended from DA9062 to support both "dlg,da9061" and "dlg,da9062". The .data entry now holds a reference to the enumerated device type. A new regmap_irq_chip model is added for DA9061 and this supports the new list of regmap_irq entries. A new mfd_cell da9061_devs[] array lists the new sub system components for DA9061. Support is added for a new DA9061 regmap_config which lists the correct readable, writable and volatile ranges for this chip. The probe function uses the device tree compatible string to switch on the da9062_compatible_types and configure the correct mfd cells, irq chip and regmap config. Kconfig is updated to reflect support for DA9061 and DA9062 PMICs. Signed-off-by: Steve Twiss Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 5 +- drivers/mfd/da9062-core.c | 427 +++++++++++++++++++++++++++++++++-- include/linux/mfd/da9062/core.h | 29 ++- include/linux/mfd/da9062/registers.h | 5 +- 4 files changed, 443 insertions(+), 23 deletions(-) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index ce3a9180a765..de68b5ba8741 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -267,13 +267,14 @@ config MFD_DA9055 called "da9055" config MFD_DA9062 - tristate "Dialog Semiconductor DA9062 PMIC Support" + tristate "Dialog Semiconductor DA9062/61 PMIC Support" select MFD_CORE select REGMAP_I2C select REGMAP_IRQ depends on I2C help - Say yes here for support for the Dialog Semiconductor DA9062 PMIC. + Say yes here for support for the Dialog Semiconductor DA9061 and + DA9062 PMICs. This includes the I2C driver and core APIs. Additional drivers must be enabled in order to use the functionality of the device. diff --git a/drivers/mfd/da9062-core.c b/drivers/mfd/da9062-core.c index 8f873866ea60..7f5e8be0a9ea 100644 --- a/drivers/mfd/da9062-core.c +++ b/drivers/mfd/da9062-core.c @@ -1,6 +1,6 @@ /* - * Core, IRQ and I2C device driver for DA9062 PMIC - * Copyright (C) 2015 Dialog Semiconductor Ltd. + * Core, IRQ and I2C device driver for DA9061 and DA9062 PMICs + * Copyright (C) 2015-2017 Dialog Semiconductor * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,6 +30,70 @@ #define DA9062_REG_EVENT_B_OFFSET 1 #define DA9062_REG_EVENT_C_OFFSET 2 +static struct regmap_irq da9061_irqs[] = { + /* EVENT A */ + [DA9061_IRQ_ONKEY] = { + .reg_offset = DA9062_REG_EVENT_A_OFFSET, + .mask = DA9062AA_M_NONKEY_MASK, + }, + [DA9061_IRQ_WDG_WARN] = { + .reg_offset = DA9062_REG_EVENT_A_OFFSET, + .mask = DA9062AA_M_WDG_WARN_MASK, + }, + [DA9061_IRQ_SEQ_RDY] = { + .reg_offset = DA9062_REG_EVENT_A_OFFSET, + .mask = DA9062AA_M_SEQ_RDY_MASK, + }, + /* EVENT B */ + [DA9061_IRQ_TEMP] = { + .reg_offset = DA9062_REG_EVENT_B_OFFSET, + .mask = DA9062AA_M_TEMP_MASK, + }, + [DA9061_IRQ_LDO_LIM] = { + .reg_offset = DA9062_REG_EVENT_B_OFFSET, + .mask = DA9062AA_M_LDO_LIM_MASK, + }, + [DA9061_IRQ_DVC_RDY] = { + .reg_offset = DA9062_REG_EVENT_B_OFFSET, + .mask = DA9062AA_M_DVC_RDY_MASK, + }, + [DA9061_IRQ_VDD_WARN] = { + .reg_offset = DA9062_REG_EVENT_B_OFFSET, + .mask = DA9062AA_M_VDD_WARN_MASK, + }, + /* EVENT C */ + [DA9061_IRQ_GPI0] = { + .reg_offset = DA9062_REG_EVENT_C_OFFSET, + .mask = DA9062AA_M_GPI0_MASK, + }, + [DA9061_IRQ_GPI1] = { + .reg_offset = DA9062_REG_EVENT_C_OFFSET, + .mask = DA9062AA_M_GPI1_MASK, + }, + [DA9061_IRQ_GPI2] = { + .reg_offset = DA9062_REG_EVENT_C_OFFSET, + .mask = DA9062AA_M_GPI2_MASK, + }, + [DA9061_IRQ_GPI3] = { + .reg_offset = DA9062_REG_EVENT_C_OFFSET, + .mask = DA9062AA_M_GPI3_MASK, + }, + [DA9061_IRQ_GPI4] = { + .reg_offset = DA9062_REG_EVENT_C_OFFSET, + .mask = DA9062AA_M_GPI4_MASK, + }, +}; + +static struct regmap_irq_chip da9061_irq_chip = { + .name = "da9061-irq", + .irqs = da9061_irqs, + .num_irqs = DA9061_NUM_IRQ, + .num_regs = 3, + .status_base = DA9062AA_EVENT_A, + .mask_base = DA9062AA_IRQ_MASK_A, + .ack_base = DA9062AA_EVENT_A, +}; + static struct regmap_irq da9062_irqs[] = { /* EVENT A */ [DA9062_IRQ_ONKEY] = { @@ -102,6 +166,57 @@ static struct regmap_irq_chip da9062_irq_chip = { .ack_base = DA9062AA_EVENT_A, }; +static struct resource da9061_core_resources[] = { + DEFINE_RES_IRQ_NAMED(DA9061_IRQ_VDD_WARN, "VDD_WARN"), +}; + +static struct resource da9061_regulators_resources[] = { + DEFINE_RES_IRQ_NAMED(DA9061_IRQ_LDO_LIM, "LDO_LIM"), +}; + +static struct resource da9061_thermal_resources[] = { + DEFINE_RES_IRQ_NAMED(DA9061_IRQ_TEMP, "THERMAL"), +}; + +static struct resource da9061_wdt_resources[] = { + DEFINE_RES_IRQ_NAMED(DA9061_IRQ_WDG_WARN, "WD_WARN"), +}; + +static struct resource da9061_onkey_resources[] = { + DEFINE_RES_IRQ_NAMED(DA9061_IRQ_ONKEY, "ONKEY"), +}; + +static const struct mfd_cell da9061_devs[] = { + { + .name = "da9061-core", + .num_resources = ARRAY_SIZE(da9061_core_resources), + .resources = da9061_core_resources, + }, + { + .name = "da9062-regulators", + .num_resources = ARRAY_SIZE(da9061_regulators_resources), + .resources = da9061_regulators_resources, + }, + { + .name = "da9061-watchdog", + .num_resources = ARRAY_SIZE(da9061_wdt_resources), + .resources = da9061_wdt_resources, + .of_compatible = "dlg,da9061-watchdog", + }, + { + .name = "da9061-thermal", + .num_resources = ARRAY_SIZE(da9061_thermal_resources), + .resources = da9061_thermal_resources, + .of_compatible = "dlg,da9061-thermal", + }, + { + .name = "da9061-onkey", + .num_resources = ARRAY_SIZE(da9061_onkey_resources), + .resources = da9061_onkey_resources, + .of_compatible = "dlg,da9061-onkey", + }, +}; + static struct resource da9062_core_resources[] = { DEFINE_RES_NAMED(DA9062_IRQ_VDD_WARN, 1, "VDD_WARN", IORESOURCE_IRQ), }; @@ -200,7 +315,8 @@ static int da9062_clear_fault_log(struct da9062 *chip) static int da9062_get_device_type(struct da9062 *chip) { - int device_id, variant_id, variant_mrc; + int device_id, variant_id, variant_mrc, variant_vrc; + char *type; int ret; ret = regmap_read(chip->regmap, DA9062AA_DEVICE_ID, &device_id); @@ -219,9 +335,23 @@ static int da9062_get_device_type(struct da9062 *chip) return -EIO; } + variant_vrc = (variant_id & DA9062AA_VRC_MASK) >> DA9062AA_VRC_SHIFT; + + switch (variant_vrc) { + case DA9062_PMIC_VARIANT_VRC_DA9061: + type = "DA9061"; + break; + case DA9062_PMIC_VARIANT_VRC_DA9062: + type = "DA9062"; + break; + default: + type = "Unknown"; + break; + } + dev_info(chip->dev, - "Device detected (device-ID: 0x%02X, var-ID: 0x%02X)\n", - device_id, variant_id); + "Device detected (device-ID: 0x%02X, var-ID: 0x%02X, %s)\n", + device_id, variant_id, type); variant_mrc = (variant_id & DA9062AA_MRC_MASK) >> DA9062AA_MRC_SHIFT; @@ -234,6 +364,234 @@ static int da9062_get_device_type(struct da9062 *chip) return ret; } +static const struct regmap_range da9061_aa_readable_ranges[] = { + { + .range_min = DA9062AA_PAGE_CON, + .range_max = DA9062AA_STATUS_B, + }, { + .range_min = DA9062AA_STATUS_D, + .range_max = DA9062AA_EVENT_C, + }, { + .range_min = DA9062AA_IRQ_MASK_A, + .range_max = DA9062AA_IRQ_MASK_C, + }, { + .range_min = DA9062AA_CONTROL_A, + .range_max = DA9062AA_GPIO_4, + }, { + .range_min = DA9062AA_GPIO_WKUP_MODE, + .range_max = DA9062AA_GPIO_OUT3_4, + }, { + .range_min = DA9062AA_BUCK1_CONT, + .range_max = DA9062AA_BUCK4_CONT, + }, { + .range_min = DA9062AA_BUCK3_CONT, + .range_max = DA9062AA_BUCK3_CONT, + }, { + .range_min = DA9062AA_LDO1_CONT, + .range_max = DA9062AA_LDO4_CONT, + }, { + .range_min = DA9062AA_DVC_1, + .range_max = DA9062AA_DVC_1, + }, { + .range_min = DA9062AA_SEQ, + .range_max = DA9062AA_ID_4_3, + }, { + .range_min = DA9062AA_ID_12_11, + .range_max = DA9062AA_ID_16_15, + }, { + .range_min = DA9062AA_ID_22_21, + .range_max = DA9062AA_ID_32_31, + }, { + .range_min = DA9062AA_SEQ_A, + .range_max = DA9062AA_WAIT, + }, { + .range_min = DA9062AA_RESET, + .range_max = DA9062AA_BUCK_ILIM_C, + }, { + .range_min = DA9062AA_BUCK1_CFG, + .range_max = DA9062AA_BUCK3_CFG, + }, { + .range_min = DA9062AA_VBUCK1_A, + .range_max = DA9062AA_VBUCK4_A, + }, { + .range_min = DA9062AA_VBUCK3_A, + .range_max = DA9062AA_VBUCK3_A, + }, { + .range_min = DA9062AA_VLDO1_A, + .range_max = DA9062AA_VLDO4_A, + }, { + .range_min = DA9062AA_VBUCK1_B, + .range_max = DA9062AA_VBUCK4_B, + }, { + .range_min = DA9062AA_VBUCK3_B, + .range_max = DA9062AA_VBUCK3_B, + }, { + .range_min = DA9062AA_VLDO1_B, + .range_max = DA9062AA_VLDO4_B, + }, { + .range_min = DA9062AA_BBAT_CONT, + .range_max = DA9062AA_BBAT_CONT, + }, { + .range_min = DA9062AA_INTERFACE, + .range_max = DA9062AA_CONFIG_E, + }, { + .range_min = DA9062AA_CONFIG_G, + .range_max = DA9062AA_CONFIG_K, + }, { + .range_min = DA9062AA_CONFIG_M, + .range_max = DA9062AA_CONFIG_M, + }, { + .range_min = DA9062AA_GP_ID_0, + .range_max = DA9062AA_GP_ID_19, + }, { + .range_min = DA9062AA_DEVICE_ID, + .range_max = DA9062AA_CONFIG_ID, + }, +}; + +static const struct regmap_range da9061_aa_writeable_ranges[] = { + { + .range_min = DA9062AA_PAGE_CON, + .range_max = DA9062AA_PAGE_CON, + }, { + .range_min = DA9062AA_FAULT_LOG, + .range_max = DA9062AA_EVENT_C, + }, { + .range_min = DA9062AA_IRQ_MASK_A, + .range_max = DA9062AA_IRQ_MASK_C, + }, { + .range_min = DA9062AA_CONTROL_A, + .range_max = DA9062AA_GPIO_4, + }, { + .range_min = DA9062AA_GPIO_WKUP_MODE, + .range_max = DA9062AA_GPIO_OUT3_4, + }, { + .range_min = DA9062AA_BUCK1_CONT, + .range_max = DA9062AA_BUCK4_CONT, + }, { + .range_min = DA9062AA_BUCK3_CONT, + .range_max = DA9062AA_BUCK3_CONT, + }, { + .range_min = DA9062AA_LDO1_CONT, + .range_max = DA9062AA_LDO4_CONT, + }, { + .range_min = DA9062AA_DVC_1, + .range_max = DA9062AA_DVC_1, + }, { + .range_min = DA9062AA_SEQ, + .range_max = DA9062AA_ID_4_3, + }, { + .range_min = DA9062AA_ID_12_11, + .range_max = DA9062AA_ID_16_15, + }, { + .range_min = DA9062AA_ID_22_21, + .range_max = DA9062AA_ID_32_31, + }, { + .range_min = DA9062AA_SEQ_A, + .range_max = DA9062AA_WAIT, + }, { + .range_min = DA9062AA_RESET, + .range_max = DA9062AA_BUCK_ILIM_C, + }, { + .range_min = DA9062AA_BUCK1_CFG, + .range_max = DA9062AA_BUCK3_CFG, + }, { + .range_min = DA9062AA_VBUCK1_A, + .range_max = DA9062AA_VBUCK4_A, + }, { + .range_min = DA9062AA_VBUCK3_A, + .range_max = DA9062AA_VBUCK3_A, + }, { + .range_min = DA9062AA_VLDO1_A, + .range_max = DA9062AA_VLDO4_A, + }, { + .range_min = DA9062AA_VBUCK1_B, + .range_max = DA9062AA_VBUCK4_B, + }, { + .range_min = DA9062AA_VBUCK3_B, + .range_max = DA9062AA_VBUCK3_B, + }, { + .range_min = DA9062AA_VLDO1_B, + .range_max = DA9062AA_VLDO4_B, + }, { + .range_min = DA9062AA_BBAT_CONT, + .range_max = DA9062AA_BBAT_CONT, + }, { + .range_min = DA9062AA_GP_ID_0, + .range_max = DA9062AA_GP_ID_19, + }, +}; + +static const struct regmap_range da9061_aa_volatile_ranges[] = { + { + .range_min = DA9062AA_PAGE_CON, + .range_max = DA9062AA_STATUS_B, + }, { + .range_min = DA9062AA_STATUS_D, + .range_max = DA9062AA_EVENT_C, + }, { + .range_min = DA9062AA_CONTROL_A, + .range_max = DA9062AA_CONTROL_B, + }, { + .range_min = DA9062AA_CONTROL_E, + .range_max = DA9062AA_CONTROL_F, + }, { + .range_min = DA9062AA_BUCK1_CONT, + .range_max = DA9062AA_BUCK4_CONT, + }, { + .range_min = DA9062AA_BUCK3_CONT, + .range_max = DA9062AA_BUCK3_CONT, + }, { + .range_min = DA9062AA_LDO1_CONT, + .range_max = DA9062AA_LDO4_CONT, + }, { + .range_min = DA9062AA_DVC_1, + .range_max = DA9062AA_DVC_1, + }, { + .range_min = DA9062AA_SEQ, + .range_max = DA9062AA_SEQ, + }, +}; + +static const struct regmap_access_table da9061_aa_readable_table = { + .yes_ranges = da9061_aa_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9061_aa_readable_ranges), +}; + +static const struct regmap_access_table da9061_aa_writeable_table = { + .yes_ranges = da9061_aa_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9061_aa_writeable_ranges), +}; + +static const struct regmap_access_table da9061_aa_volatile_table = { + .yes_ranges = da9061_aa_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(da9061_aa_volatile_ranges), +}; + +static const struct regmap_range_cfg da9061_range_cfg[] = { + { + .range_min = DA9062AA_PAGE_CON, + .range_max = DA9062AA_CONFIG_ID, + .selector_reg = DA9062AA_PAGE_CON, + .selector_mask = 1 << DA9062_I2C_PAGE_SEL_SHIFT, + .selector_shift = DA9062_I2C_PAGE_SEL_SHIFT, + .window_start = 0, + .window_len = 256, + } +}; + +static struct regmap_config da9061_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .ranges = da9061_range_cfg, + .num_ranges = ARRAY_SIZE(da9061_range_cfg), + .max_register = DA9062AA_CONFIG_ID, + .cache_type = REGCACHE_RBTREE, + .rd_table = &da9061_aa_readable_table, + .wr_table = &da9061_aa_writeable_table, + .volatile_table = &da9061_aa_volatile_table, +}; + static const struct regmap_range da9062_aa_readable_ranges[] = { { .range_min = DA9062AA_PAGE_CON, @@ -456,17 +814,39 @@ static struct regmap_config da9062_regmap_config = { .volatile_table = &da9062_aa_volatile_table, }; +static const struct of_device_id da9062_dt_ids[] = { + { .compatible = "dlg,da9061", .data = (void *)COMPAT_TYPE_DA9061, }, + { .compatible = "dlg,da9062", .data = (void *)COMPAT_TYPE_DA9062, }, + { } +}; +MODULE_DEVICE_TABLE(of, da9062_dt_ids); + static int da9062_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct da9062 *chip; + const struct of_device_id *match; unsigned int irq_base; + const struct mfd_cell *cell; + const struct regmap_irq_chip *irq_chip; + const struct regmap_config *config; + int cell_num; int ret; chip = devm_kzalloc(&i2c->dev, sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; + if (i2c->dev.of_node) { + match = of_match_node(da9062_dt_ids, i2c->dev.of_node); + if (!match) + return -EINVAL; + + chip->chip_type = (uintptr_t)match->data; + } else { + chip->chip_type = id->driver_data; + } + i2c_set_clientdata(i2c, chip); chip->dev = &i2c->dev; @@ -475,7 +855,25 @@ static int da9062_i2c_probe(struct i2c_client *i2c, return -EINVAL; } - chip->regmap = devm_regmap_init_i2c(i2c, &da9062_regmap_config); + switch (chip->chip_type) { + case COMPAT_TYPE_DA9061: + cell = da9061_devs; + cell_num = ARRAY_SIZE(da9061_devs); + irq_chip = &da9061_irq_chip; + config = &da9061_regmap_config; + break; + case COMPAT_TYPE_DA9062: + cell = da9062_devs; + cell_num = ARRAY_SIZE(da9062_devs); + irq_chip = &da9062_irq_chip; + config = &da9062_regmap_config; + break; + default: + dev_err(chip->dev, "Unrecognised chip type\n"); + return -ENODEV; + } + + chip->regmap = devm_regmap_init_i2c(i2c, config); if (IS_ERR(chip->regmap)) { ret = PTR_ERR(chip->regmap); dev_err(chip->dev, "Failed to allocate register map: %d\n", @@ -493,7 +891,7 @@ static int da9062_i2c_probe(struct i2c_client *i2c, ret = regmap_add_irq_chip(chip->regmap, i2c->irq, IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED, - -1, &da9062_irq_chip, + -1, irq_chip, &chip->regmap_irq); if (ret) { dev_err(chip->dev, "Failed to request IRQ %d: %d\n", @@ -503,8 +901,8 @@ static int da9062_i2c_probe(struct i2c_client *i2c, irq_base = regmap_irq_chip_get_base(chip->regmap_irq); - ret = mfd_add_devices(chip->dev, PLATFORM_DEVID_NONE, da9062_devs, - ARRAY_SIZE(da9062_devs), NULL, irq_base, + ret = mfd_add_devices(chip->dev, PLATFORM_DEVID_NONE, cell, + cell_num, NULL, irq_base, NULL); if (ret) { dev_err(chip->dev, "Cannot register child devices\n"); @@ -526,17 +924,12 @@ static int da9062_i2c_remove(struct i2c_client *i2c) } static const struct i2c_device_id da9062_i2c_id[] = { - { "da9062", 0 }, + { "da9061", COMPAT_TYPE_DA9061 }, + { "da9062", COMPAT_TYPE_DA9062 }, { }, }; MODULE_DEVICE_TABLE(i2c, da9062_i2c_id); -static const struct of_device_id da9062_dt_ids[] = { - { .compatible = "dlg,da9062", }, - { } -}; -MODULE_DEVICE_TABLE(of, da9062_dt_ids); - static struct i2c_driver da9062_i2c_driver = { .driver = { .name = "da9062", @@ -549,6 +942,6 @@ static struct i2c_driver da9062_i2c_driver = { module_i2c_driver(da9062_i2c_driver); -MODULE_DESCRIPTION("Core device driver for Dialog DA9062"); +MODULE_DESCRIPTION("Core device driver for Dialog DA9061 and DA9062"); MODULE_AUTHOR("Steve Twiss "); MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/da9062/core.h b/include/linux/mfd/da9062/core.h index 376ba84366a0..74d33a01ddae 100644 --- a/include/linux/mfd/da9062/core.h +++ b/include/linux/mfd/da9062/core.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 Dialog Semiconductor Ltd. + * Copyright (C) 2015-2017 Dialog Semiconductor * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -18,7 +18,31 @@ #include #include -/* Interrupts */ +enum da9062_compatible_types { + COMPAT_TYPE_DA9061 = 1, + COMPAT_TYPE_DA9062, +}; + +enum da9061_irqs { + /* IRQ A */ + DA9061_IRQ_ONKEY, + DA9061_IRQ_WDG_WARN, + DA9061_IRQ_SEQ_RDY, + /* IRQ B*/ + DA9061_IRQ_TEMP, + DA9061_IRQ_LDO_LIM, + DA9061_IRQ_DVC_RDY, + DA9061_IRQ_VDD_WARN, + /* IRQ C */ + DA9061_IRQ_GPI0, + DA9061_IRQ_GPI1, + DA9061_IRQ_GPI2, + DA9061_IRQ_GPI3, + DA9061_IRQ_GPI4, + + DA9061_NUM_IRQ, +}; + enum da9062_irqs { /* IRQ A */ DA9062_IRQ_ONKEY, @@ -45,6 +69,7 @@ struct da9062 { struct device *dev; struct regmap *regmap; struct regmap_irq_chip_data *regmap_irq; + enum da9062_compatible_types chip_type; }; #endif /* __MFD_DA9062_CORE_H__ */ diff --git a/include/linux/mfd/da9062/registers.h b/include/linux/mfd/da9062/registers.h index 97790d1b02c5..18d576aed902 100644 --- a/include/linux/mfd/da9062/registers.h +++ b/include/linux/mfd/da9062/registers.h @@ -1,6 +1,5 @@ /* - * registers.h - REGISTERS H for DA9062 - * Copyright (C) 2015 Dialog Semiconductor Ltd. + * Copyright (C) 2015-2017 Dialog Semiconductor * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -18,6 +17,8 @@ #define DA9062_PMIC_DEVICE_ID 0x62 #define DA9062_PMIC_VARIANT_MRC_AA 0x01 +#define DA9062_PMIC_VARIANT_VRC_DA9061 0x01 +#define DA9062_PMIC_VARIANT_VRC_DA9062 0x02 #define DA9062_I2C_PAGE_SEL_SHIFT 1 -- cgit v1.2.3-59-g8ed1b From b4aeceb694a3ba353c0531f3b075642905c6b523 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Wed, 5 Apr 2017 10:10:55 +0200 Subject: mfd: axp20x: Add MFD cells for AXP20X and AXP22X battery driver The X-Powers AXP20X and AXP22X PMICs can have a battery as power supply. This patch adds the AXP20X/AXP22X battery driver to the MFD cells of the AXP209, AXP221 and AXP223 MFD. Signed-off-by: Quentin Schulz Acked-by: Chen-Yu Tsai Signed-off-by: Lee Jones --- drivers/mfd/axp20x.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 5ba3b04cc9b1..e6f55079876e 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -593,6 +593,9 @@ static struct mfd_cell axp20x_cells[] = { .name = "axp20x-regulator", }, { .name = "axp20x-adc", + }, { + .name = "axp20x-battery-power-supply", + .of_compatible = "x-powers,axp209-battery-power-supply", }, { .name = "axp20x-ac-power-supply", .of_compatible = "x-powers,axp202-ac-power-supply", @@ -620,6 +623,9 @@ static struct mfd_cell axp221_cells[] = { .of_compatible = "x-powers,axp221-ac-power-supply", .num_resources = ARRAY_SIZE(axp20x_ac_power_supply_resources), .resources = axp20x_ac_power_supply_resources, + }, { + .name = "axp20x-battery-power-supply", + .of_compatible = "x-powers,axp221-battery-power-supply", }, { .name = "axp20x-usb-power-supply", .of_compatible = "x-powers,axp221-usb-power-supply", @@ -635,6 +641,9 @@ static struct mfd_cell axp223_cells[] = { .resources = axp22x_pek_resources, }, { .name = "axp22x-adc", + }, { + .name = "axp20x-battery-power-supply", + .of_compatible = "x-powers,axp221-battery-power-supply", }, { .name = "axp20x-regulator", }, { -- cgit v1.2.3-59-g8ed1b From fe37c4b98173ec4b82d3b225d4a7c4d40f77f304 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Wed, 5 Apr 2017 11:06:27 +0200 Subject: dt-bindings: mfd: Add A33 GPADC binding This patch adds documentation for the A33 GPADC binding. Signed-off-by: Quentin Schulz Acked-by: Rob Herring Signed-off-by: Lee Jones --- .../devicetree/bindings/mfd/sun4i-gpadc.txt | 59 ++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/sun4i-gpadc.txt diff --git a/Documentation/devicetree/bindings/mfd/sun4i-gpadc.txt b/Documentation/devicetree/bindings/mfd/sun4i-gpadc.txt new file mode 100644 index 000000000000..badff3611a98 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/sun4i-gpadc.txt @@ -0,0 +1,59 @@ +Allwinner SoCs' GPADC Device Tree bindings +------------------------------------------ +The Allwinner SoCs all have an ADC that can also act as a thermal sensor +and sometimes as a touchscreen controller. + +Required properties: + - compatible: "allwinner,sun8i-a33-ths", + - reg: mmio address range of the chip, + - #thermal-sensor-cells: shall be 0, + - #io-channel-cells: shall be 0, + +Example: + ths: ths@01c25000 { + compatible = "allwinner,sun8i-a33-ths"; + reg = <0x01c25000 0x100>; + #thermal-sensor-cells = <0>; + #io-channel-cells = <0>; + }; + +sun4i, sun5i and sun6i SoCs are also supported via the older binding: + +sun4i resistive touchscreen controller +-------------------------------------- + +Required properties: + - compatible: "allwinner,sun4i-a10-ts", "allwinner,sun5i-a13-ts" or + "allwinner,sun6i-a31-ts" + - reg: mmio address range of the chip + - interrupts: interrupt to which the chip is connected + - #thermal-sensor-cells: shall be 0 + +Optional properties: + - allwinner,ts-attached : boolean indicating that an actual touchscreen + is attached to the controller + - allwinner,tp-sensitive-adjust : integer (4 bits) + adjust sensitivity of pen down detection + between 0 (least sensitive) and 15 + (defaults to 15) + - allwinner,filter-type : integer (2 bits) + select median and averaging filter + samples used for median / averaging filter + 0: 4/2 + 1: 5/3 + 2: 8/4 + 3: 16/8 + (defaults to 1) + +Example: + + rtp: rtp@01c25000 { + compatible = "allwinner,sun4i-a10-ts"; + reg = <0x01c25000 0x100>; + interrupts = <29>; + allwinner,ts-attached; + #thermal-sensor-cells = <0>; + /* sensitive/noisy touch panel */ + allwinner,tp-sensitive-adjust = <0>; + allwinner,filter-type = <3>; + }; -- cgit v1.2.3-59-g8ed1b From 4d5db2a3a207451a8b11802c4d5f45a8931d996c Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Wed, 5 Apr 2017 11:06:28 +0200 Subject: dt-bindings: input: touschcreen: Remove sun4i documentation This patch removes the sun4i touchscreen controller binding documentation since it has been merged with the sun4i GPADC binding documentation. Signed-off-by: Quentin Schulz Acked-by: Rob Herring Acked-by: Dmitry Torokhov Signed-off-by: Lee Jones --- .../bindings/input/touchscreen/sun4i.txt | 38 ---------------------- 1 file changed, 38 deletions(-) delete mode 100644 Documentation/devicetree/bindings/input/touchscreen/sun4i.txt diff --git a/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt b/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt deleted file mode 100644 index 89abecd938cb..000000000000 --- a/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt +++ /dev/null @@ -1,38 +0,0 @@ -sun4i resistive touchscreen controller --------------------------------------- - -Required properties: - - compatible: "allwinner,sun4i-a10-ts", "allwinner,sun5i-a13-ts" or - "allwinner,sun6i-a31-ts" - - reg: mmio address range of the chip - - interrupts: interrupt to which the chip is connected - - #thermal-sensor-cells: shall be 0 - -Optional properties: - - allwinner,ts-attached : boolean indicating that an actual touchscreen - is attached to the controller - - allwinner,tp-sensitive-adjust : integer (4 bits) - adjust sensitivity of pen down detection - between 0 (least sensitive) and 15 - (defaults to 15) - - allwinner,filter-type : integer (2 bits) - select median and averaging filter - samples used for median / averaging filter - 0: 4/2 - 1: 5/3 - 2: 8/4 - 3: 16/8 - (defaults to 1) - -Example: - - rtp: rtp@01c25000 { - compatible = "allwinner,sun4i-a10-ts"; - reg = <0x01c25000 0x100>; - interrupts = <29>; - allwinner,ts-attached; - #thermal-sensor-cells = <0>; - /* sensitive/noisy touch panel */ - allwinner,tp-sensitive-adjust = <0>; - allwinner,filter-type = <3>; - }; -- cgit v1.2.3-59-g8ed1b From ac89473213c602b98172d92e40f5e78032b1aba0 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 3 Apr 2017 20:15:54 -0700 Subject: mfd: cpcap: Fix interrupt to use level interrupt I made a mistake assuming the device tree configuration for interrupt triggering was somehow passed to the SPI device but it's not. In the Motorola Linux kernel tree CPCAP PMIC is configured as a rising edge triggered interrupt, but then then it's interrupt handler keeps looping until the GPIO line goes down. So the CPCAP interrupt is clearly a level interrupt and not an edge interrupt. Earlier when I tried to configure it as level interrupt using the device tree, I did not account that the triggering only gets passed to the SPI core and it also needs to be specified in the CPCAP driver when we do devm_regmap_add_irq_chip(). Fixes: 56e1d40d3bea ("mfd: cpcap: Add minimal support") Signed-off-by: Tony Lindgren Acked-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/motorola-cpcap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/motorola-cpcap.c b/drivers/mfd/motorola-cpcap.c index a9097efcefa5..cd93f5c7b063 100644 --- a/drivers/mfd/motorola-cpcap.c +++ b/drivers/mfd/motorola-cpcap.c @@ -154,7 +154,7 @@ static int cpcap_init_irq_chip(struct cpcap_ddata *cpcap, int irq_chip, ret = devm_regmap_add_irq_chip(&cpcap->spi->dev, cpcap->regmap, cpcap->spi->irq, - IRQF_TRIGGER_RISING | + irq_get_trigger_type(cpcap->spi->irq) | IRQF_SHARED, -1, chip, &cpcap->irqdata[irq_chip]); if (ret) { -- cgit v1.2.3-59-g8ed1b From 5a88d4120029601a0868e20774382d312952d3b5 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 3 Apr 2017 20:15:55 -0700 Subject: mfd: cpcap: Use ack_invert interrupts We should use ack_invert as the int_read_and_clear() in the Motorola kernel tree does "ireg_val & ~mreg_val" before writing to the mask register. Fixes: 56e1d40d3bea ("mfd: cpcap: Add minimal support") Tested-by: Sebastian Reichel Signed-off-by: Tony Lindgren Signed-off-by: Lee Jones --- drivers/mfd/motorola-cpcap.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mfd/motorola-cpcap.c b/drivers/mfd/motorola-cpcap.c index cd93f5c7b063..a6c0c3419553 100644 --- a/drivers/mfd/motorola-cpcap.c +++ b/drivers/mfd/motorola-cpcap.c @@ -99,6 +99,7 @@ static struct regmap_irq_chip cpcap_irq_chip[CPCAP_NR_IRQ_CHIPS] = { .ack_base = CPCAP_REG_MI1, .mask_base = CPCAP_REG_MIM1, .use_ack = true, + .ack_invert = true, }, { .name = "cpcap-m2", @@ -107,6 +108,7 @@ static struct regmap_irq_chip cpcap_irq_chip[CPCAP_NR_IRQ_CHIPS] = { .ack_base = CPCAP_REG_MI2, .mask_base = CPCAP_REG_MIM2, .use_ack = true, + .ack_invert = true, }, { .name = "cpcap1-4", @@ -116,6 +118,7 @@ static struct regmap_irq_chip cpcap_irq_chip[CPCAP_NR_IRQ_CHIPS] = { .mask_base = CPCAP_REG_INTM1, .type_base = CPCAP_REG_INTS1, .use_ack = true, + .ack_invert = true, }, }; -- cgit v1.2.3-59-g8ed1b From be269180c9335c257a2763c3fd3a44e65c90c1f0 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 3 Apr 2017 20:15:56 -0700 Subject: mfd: cpcap: Fix bad use of IRQ sense register The cpcap INTS registers are for getting the value of the line, not for configuring the type. Fixes: 56e1d40d3bea ("mfd: cpcap: Add minimal support") Reviewed-By: Sebastian Reichel Tested-by: Sebastian Reichel Signed-off-by: Tony Lindgren Signed-off-by: Lee Jones --- drivers/mfd/motorola-cpcap.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mfd/motorola-cpcap.c b/drivers/mfd/motorola-cpcap.c index a6c0c3419553..3cab58ab0b84 100644 --- a/drivers/mfd/motorola-cpcap.c +++ b/drivers/mfd/motorola-cpcap.c @@ -116,7 +116,6 @@ static struct regmap_irq_chip cpcap_irq_chip[CPCAP_NR_IRQ_CHIPS] = { .status_base = CPCAP_REG_INT1, .ack_base = CPCAP_REG_INT1, .mask_base = CPCAP_REG_INTM1, - .type_base = CPCAP_REG_INTS1, .use_ack = true, .ack_invert = true, }, -- cgit v1.2.3-59-g8ed1b From 0e34d5de961d5a8a84cf77878ca871c6e8104620 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Tue, 4 Apr 2017 08:34:41 +0200 Subject: iio: adc: add support for X-Powers AXP20X and AXP22X PMICs ADCs The X-Powers AXP20X and AXP22X PMICs have multiple ADCs. They expose the battery voltage, battery charge and discharge currents, AC-in and VBUS voltages and currents, 2 GPIOs muxable in ADC mode and PMIC temperature. This adds support for most of AXP20X and AXP22X ADCs. Signed-off-by: Quentin Schulz Acked-by: Maxime Ripard Acked-by: Chen-Yu Tsai Reviewed-by: Jonathan Cameron Signed-off-by: Lee Jones --- drivers/iio/adc/Kconfig | 10 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/axp20x_adc.c | 617 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 628 insertions(+) create mode 100644 drivers/iio/adc/axp20x_adc.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 5d134053a1bc..ff5ad3be55b4 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -154,6 +154,16 @@ config AT91_SAMA5D2_ADC To compile this driver as a module, choose M here: the module will be called at91-sama5d2_adc. +config AXP20X_ADC + tristate "X-Powers AXP20X and AXP22X ADC driver" + depends on MFD_AXP20X + help + Say yes here to have support for X-Powers power management IC (PMIC) + AXP20X and AXP22X ADC devices. + + To compile this driver as a module, choose M here: the module will be + called axp20x_adc. + config AXP288_ADC tristate "X-Powers AXP288 ADC driver" depends on MFD_AXP20X diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 54b5f7259063..a01de757f42c 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_AD7887) += ad7887.o obj-$(CONFIG_AD799X) += ad799x.o obj-$(CONFIG_AT91_ADC) += at91_adc.o obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o +obj-$(CONFIG_AXP20X_ADC) += axp20x_adc.o obj-$(CONFIG_AXP288_ADC) += axp288_adc.o obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c new file mode 100644 index 000000000000..11e177180ea0 --- /dev/null +++ b/drivers/iio/adc/axp20x_adc.c @@ -0,0 +1,617 @@ +/* ADC driver for AXP20X and AXP22X PMICs + * + * Copyright (c) 2016 Free Electrons NextThing Co. + * Quentin Schulz + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define AXP20X_ADC_EN1_MASK GENMASK(7, 0) + +#define AXP20X_ADC_EN2_MASK (GENMASK(3, 2) | BIT(7)) +#define AXP22X_ADC_EN1_MASK (GENMASK(7, 5) | BIT(0)) + +#define AXP20X_GPIO10_IN_RANGE_GPIO0 BIT(0) +#define AXP20X_GPIO10_IN_RANGE_GPIO1 BIT(1) +#define AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(x) ((x) & BIT(0)) +#define AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(x) (((x) & BIT(0)) << 1) + +#define AXP20X_ADC_RATE_MASK GENMASK(7, 6) +#define AXP20X_ADC_RATE_HZ(x) ((ilog2((x) / 25) << 6) & AXP20X_ADC_RATE_MASK) +#define AXP22X_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 6) & AXP20X_ADC_RATE_MASK) + +#define AXP20X_ADC_CHANNEL(_channel, _name, _type, _reg) \ + { \ + .type = _type, \ + .indexed = 1, \ + .channel = _channel, \ + .address = _reg, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = _name, \ + } + +#define AXP20X_ADC_CHANNEL_OFFSET(_channel, _name, _type, _reg) \ + { \ + .type = _type, \ + .indexed = 1, \ + .channel = _channel, \ + .address = _reg, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) |\ + BIT(IIO_CHAN_INFO_OFFSET),\ + .datasheet_name = _name, \ + } + +struct axp_data; + +struct axp20x_adc_iio { + struct regmap *regmap; + struct axp_data *data; +}; + +enum axp20x_adc_channel_v { + AXP20X_ACIN_V = 0, + AXP20X_VBUS_V, + AXP20X_TS_IN, + AXP20X_GPIO0_V, + AXP20X_GPIO1_V, + AXP20X_IPSOUT_V, + AXP20X_BATT_V, +}; + +enum axp20x_adc_channel_i { + AXP20X_ACIN_I = 0, + AXP20X_VBUS_I, + AXP20X_BATT_CHRG_I, + AXP20X_BATT_DISCHRG_I, +}; + +enum axp22x_adc_channel_v { + AXP22X_TS_IN = 0, + AXP22X_BATT_V, +}; + +enum axp22x_adc_channel_i { + AXP22X_BATT_CHRG_I = 1, + AXP22X_BATT_DISCHRG_I, +}; + +static struct iio_map axp20x_maps[] = { + { + .consumer_dev_name = "axp20x-usb-power-supply", + .consumer_channel = "vbus_v", + .adc_channel_label = "vbus_v", + }, { + .consumer_dev_name = "axp20x-usb-power-supply", + .consumer_channel = "vbus_i", + .adc_channel_label = "vbus_i", + }, { + .consumer_dev_name = "axp20x-ac-power-supply", + .consumer_channel = "acin_v", + .adc_channel_label = "acin_v", + }, { + .consumer_dev_name = "axp20x-ac-power-supply", + .consumer_channel = "acin_i", + .adc_channel_label = "acin_i", + }, { + .consumer_dev_name = "axp20x-battery-power-supply", + .consumer_channel = "batt_v", + .adc_channel_label = "batt_v", + }, { + .consumer_dev_name = "axp20x-battery-power-supply", + .consumer_channel = "batt_chrg_i", + .adc_channel_label = "batt_chrg_i", + }, { + .consumer_dev_name = "axp20x-battery-power-supply", + .consumer_channel = "batt_dischrg_i", + .adc_channel_label = "batt_dischrg_i", + }, { /* sentinel */ } +}; + +static struct iio_map axp22x_maps[] = { + { + .consumer_dev_name = "axp20x-battery-power-supply", + .consumer_channel = "batt_v", + .adc_channel_label = "batt_v", + }, { + .consumer_dev_name = "axp20x-battery-power-supply", + .consumer_channel = "batt_chrg_i", + .adc_channel_label = "batt_chrg_i", + }, { + .consumer_dev_name = "axp20x-battery-power-supply", + .consumer_channel = "batt_dischrg_i", + .adc_channel_label = "batt_dischrg_i", + }, { /* sentinel */ } +}; + +/* + * Channels are mapped by physical system. Their channels share the same index. + * i.e. acin_i is in_current0_raw and acin_v is in_voltage0_raw. + * The only exception is for the battery. batt_v will be in_voltage6_raw and + * charge current in_current6_raw and discharge current will be in_current7_raw. + */ +static const struct iio_chan_spec axp20x_adc_channels[] = { + AXP20X_ADC_CHANNEL(AXP20X_ACIN_V, "acin_v", IIO_VOLTAGE, + AXP20X_ACIN_V_ADC_H), + AXP20X_ADC_CHANNEL(AXP20X_ACIN_I, "acin_i", IIO_CURRENT, + AXP20X_ACIN_I_ADC_H), + AXP20X_ADC_CHANNEL(AXP20X_VBUS_V, "vbus_v", IIO_VOLTAGE, + AXP20X_VBUS_V_ADC_H), + AXP20X_ADC_CHANNEL(AXP20X_VBUS_I, "vbus_i", IIO_CURRENT, + AXP20X_VBUS_I_ADC_H), + { + .type = IIO_TEMP, + .address = AXP20X_TEMP_ADC_H, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .datasheet_name = "pmic_temp", + }, + AXP20X_ADC_CHANNEL_OFFSET(AXP20X_GPIO0_V, "gpio0_v", IIO_VOLTAGE, + AXP20X_GPIO0_V_ADC_H), + AXP20X_ADC_CHANNEL_OFFSET(AXP20X_GPIO1_V, "gpio1_v", IIO_VOLTAGE, + AXP20X_GPIO1_V_ADC_H), + AXP20X_ADC_CHANNEL(AXP20X_IPSOUT_V, "ipsout_v", IIO_VOLTAGE, + AXP20X_IPSOUT_V_HIGH_H), + AXP20X_ADC_CHANNEL(AXP20X_BATT_V, "batt_v", IIO_VOLTAGE, + AXP20X_BATT_V_H), + AXP20X_ADC_CHANNEL(AXP20X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT, + AXP20X_BATT_CHRG_I_H), + AXP20X_ADC_CHANNEL(AXP20X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT, + AXP20X_BATT_DISCHRG_I_H), +}; + +static const struct iio_chan_spec axp22x_adc_channels[] = { + { + .type = IIO_TEMP, + .address = AXP22X_PMIC_TEMP_H, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .datasheet_name = "pmic_temp", + }, + AXP20X_ADC_CHANNEL(AXP22X_BATT_V, "batt_v", IIO_VOLTAGE, + AXP20X_BATT_V_H), + AXP20X_ADC_CHANNEL(AXP22X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT, + AXP20X_BATT_CHRG_I_H), + AXP20X_ADC_CHANNEL(AXP22X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT, + AXP20X_BATT_DISCHRG_I_H), +}; + +static int axp20x_adc_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct axp20x_adc_iio *info = iio_priv(indio_dev); + int size = 12; + + /* + * N.B.: Unlike the Chinese datasheets tell, the charging current is + * stored on 12 bits, not 13 bits. Only discharging current is on 13 + * bits. + */ + if (chan->type == IIO_CURRENT && chan->channel == AXP20X_BATT_DISCHRG_I) + size = 13; + else + size = 12; + + *val = axp20x_read_variable_width(info->regmap, chan->address, size); + if (*val < 0) + return *val; + + return IIO_VAL_INT; +} + +static int axp22x_adc_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct axp20x_adc_iio *info = iio_priv(indio_dev); + int size; + + /* + * N.B.: Unlike the Chinese datasheets tell, the charging current is + * stored on 12 bits, not 13 bits. Only discharging current is on 13 + * bits. + */ + if (chan->type == IIO_CURRENT && chan->channel == AXP22X_BATT_DISCHRG_I) + size = 13; + else + size = 12; + + *val = axp20x_read_variable_width(info->regmap, chan->address, size); + if (*val < 0) + return *val; + + return IIO_VAL_INT; +} + +static int axp20x_adc_scale_voltage(int channel, int *val, int *val2) +{ + switch (channel) { + case AXP20X_ACIN_V: + case AXP20X_VBUS_V: + *val = 1; + *val2 = 700000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP20X_GPIO0_V: + case AXP20X_GPIO1_V: + *val = 0; + *val2 = 500000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP20X_BATT_V: + *val = 1; + *val2 = 100000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP20X_IPSOUT_V: + *val = 1; + *val2 = 400000; + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } +} + +static int axp20x_adc_scale_current(int channel, int *val, int *val2) +{ + switch (channel) { + case AXP20X_ACIN_I: + *val = 0; + *val2 = 625000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP20X_VBUS_I: + *val = 0; + *val2 = 375000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP20X_BATT_DISCHRG_I: + case AXP20X_BATT_CHRG_I: + *val = 0; + *val2 = 500000; + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } +} + +static int axp20x_adc_scale(struct iio_chan_spec const *chan, int *val, + int *val2) +{ + switch (chan->type) { + case IIO_VOLTAGE: + return axp20x_adc_scale_voltage(chan->channel, val, val2); + + case IIO_CURRENT: + return axp20x_adc_scale_current(chan->channel, val, val2); + + case IIO_TEMP: + *val = 100; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int axp22x_adc_scale(struct iio_chan_spec const *chan, int *val, + int *val2) +{ + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->channel != AXP22X_BATT_V) + return -EINVAL; + + *val = 1; + *val2 = 100000; + return IIO_VAL_INT_PLUS_MICRO; + + case IIO_CURRENT: + *val = 0; + *val2 = 500000; + return IIO_VAL_INT_PLUS_MICRO; + + case IIO_TEMP: + *val = 100; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int axp20x_adc_offset_voltage(struct iio_dev *indio_dev, int channel, + int *val) +{ + struct axp20x_adc_iio *info = iio_priv(indio_dev); + int ret; + + ret = regmap_read(info->regmap, AXP20X_GPIO10_IN_RANGE, val); + if (ret < 0) + return ret; + + switch (channel) { + case AXP20X_GPIO0_V: + *val &= AXP20X_GPIO10_IN_RANGE_GPIO0; + break; + + case AXP20X_GPIO1_V: + *val &= AXP20X_GPIO10_IN_RANGE_GPIO1; + break; + + default: + return -EINVAL; + } + + *val = !!(*val) * 700000; + + return IIO_VAL_INT; +} + +static int axp20x_adc_offset(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + switch (chan->type) { + case IIO_VOLTAGE: + return axp20x_adc_offset_voltage(indio_dev, chan->channel, val); + + case IIO_TEMP: + *val = -1447; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int axp20x_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_OFFSET: + return axp20x_adc_offset(indio_dev, chan, val); + + case IIO_CHAN_INFO_SCALE: + return axp20x_adc_scale(chan, val, val2); + + case IIO_CHAN_INFO_RAW: + return axp20x_adc_raw(indio_dev, chan, val); + + default: + return -EINVAL; + } +} + +static int axp22x_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_OFFSET: + *val = -2677; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + return axp22x_adc_scale(chan, val, val2); + + case IIO_CHAN_INFO_RAW: + return axp22x_adc_raw(indio_dev, chan, val); + + default: + return -EINVAL; + } +} + +static int axp20x_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, + long mask) +{ + struct axp20x_adc_iio *info = iio_priv(indio_dev); + unsigned int reg, regval; + + /* + * The AXP20X PMIC allows the user to choose between 0V and 0.7V offsets + * for (independently) GPIO0 and GPIO1 when in ADC mode. + */ + if (mask != IIO_CHAN_INFO_OFFSET) + return -EINVAL; + + if (val != 0 && val != 700000) + return -EINVAL; + + switch (chan->channel) { + case AXP20X_GPIO0_V: + reg = AXP20X_GPIO10_IN_RANGE_GPIO0; + regval = AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(!!val); + break; + + case AXP20X_GPIO1_V: + reg = AXP20X_GPIO10_IN_RANGE_GPIO1; + regval = AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(!!val); + break; + + default: + return -EINVAL; + } + + return regmap_update_bits(info->regmap, AXP20X_GPIO10_IN_RANGE, reg, + regval); +} + +static const struct iio_info axp20x_adc_iio_info = { + .read_raw = axp20x_read_raw, + .write_raw = axp20x_write_raw, + .driver_module = THIS_MODULE, +}; + +static const struct iio_info axp22x_adc_iio_info = { + .read_raw = axp22x_read_raw, + .driver_module = THIS_MODULE, +}; + +static int axp20x_adc_rate(int rate) +{ + return AXP20X_ADC_RATE_HZ(rate); +} + +static int axp22x_adc_rate(int rate) +{ + return AXP22X_ADC_RATE_HZ(rate); +} + +struct axp_data { + const struct iio_info *iio_info; + int num_channels; + struct iio_chan_spec const *channels; + unsigned long adc_en1_mask; + int (*adc_rate)(int rate); + bool adc_en2; + struct iio_map *maps; +}; + +static const struct axp_data axp20x_data = { + .iio_info = &axp20x_adc_iio_info, + .num_channels = ARRAY_SIZE(axp20x_adc_channels), + .channels = axp20x_adc_channels, + .adc_en1_mask = AXP20X_ADC_EN1_MASK, + .adc_rate = axp20x_adc_rate, + .adc_en2 = true, + .maps = axp20x_maps, +}; + +static const struct axp_data axp22x_data = { + .iio_info = &axp22x_adc_iio_info, + .num_channels = ARRAY_SIZE(axp22x_adc_channels), + .channels = axp22x_adc_channels, + .adc_en1_mask = AXP22X_ADC_EN1_MASK, + .adc_rate = axp22x_adc_rate, + .adc_en2 = false, + .maps = axp22x_maps, +}; + +static const struct platform_device_id axp20x_adc_id_match[] = { + { .name = "axp20x-adc", .driver_data = (kernel_ulong_t)&axp20x_data, }, + { .name = "axp22x-adc", .driver_data = (kernel_ulong_t)&axp22x_data, }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(platform, axp20x_adc_id_match); + +static int axp20x_probe(struct platform_device *pdev) +{ + struct axp20x_adc_iio *info; + struct iio_dev *indio_dev; + struct axp20x_dev *axp20x_dev; + int ret; + + axp20x_dev = dev_get_drvdata(pdev->dev.parent); + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + platform_set_drvdata(pdev, indio_dev); + + info->regmap = axp20x_dev->regmap; + indio_dev->dev.parent = &pdev->dev; + indio_dev->dev.of_node = pdev->dev.of_node; + indio_dev->modes = INDIO_DIRECT_MODE; + + info->data = (struct axp_data *)platform_get_device_id(pdev)->driver_data; + + indio_dev->name = platform_get_device_id(pdev)->name; + indio_dev->info = info->data->iio_info; + indio_dev->num_channels = info->data->num_channels; + indio_dev->channels = info->data->channels; + + /* Enable the ADCs on IP */ + regmap_write(info->regmap, AXP20X_ADC_EN1, info->data->adc_en1_mask); + + if (info->data->adc_en2) + /* Enable GPIO0/1 and internal temperature ADCs */ + regmap_update_bits(info->regmap, AXP20X_ADC_EN2, + AXP20X_ADC_EN2_MASK, AXP20X_ADC_EN2_MASK); + + /* Configure ADCs rate */ + regmap_update_bits(info->regmap, AXP20X_ADC_RATE, AXP20X_ADC_RATE_MASK, + info->data->adc_rate(100)); + + ret = iio_map_array_register(indio_dev, info->data->maps); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register IIO maps: %d\n", ret); + goto fail_map; + } + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&pdev->dev, "could not register the device\n"); + goto fail_register; + } + + return 0; + +fail_register: + iio_map_array_unregister(indio_dev); + +fail_map: + regmap_write(info->regmap, AXP20X_ADC_EN1, 0); + + if (info->data->adc_en2) + regmap_write(info->regmap, AXP20X_ADC_EN2, 0); + + return ret; +} + +static int axp20x_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct axp20x_adc_iio *info = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_map_array_unregister(indio_dev); + + regmap_write(info->regmap, AXP20X_ADC_EN1, 0); + + if (info->data->adc_en2) + regmap_write(info->regmap, AXP20X_ADC_EN2, 0); + + return 0; +} + +static struct platform_driver axp20x_adc_driver = { + .driver = { + .name = "axp20x-adc", + }, + .id_table = axp20x_adc_id_match, + .probe = axp20x_probe, + .remove = axp20x_remove, +}; + +module_platform_driver(axp20x_adc_driver); + +MODULE_DESCRIPTION("ADC driver for AXP20X and AXP22X PMICs"); +MODULE_AUTHOR("Quentin Schulz "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From addebf1588ab812b891651ef5fba194659f71ea5 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Thu, 23 Mar 2017 09:03:24 +0100 Subject: mfd: exynos-lpass: Remove pad retention control Pad retention should be controlled from pin control driver, so remove it from Exynos LPASS driver. After this change, no more access to PMU regmap is needed, so remove also the code for handling PMU regmap. Signed-off-by: Marek Szyprowski Acked-by: Krzysztof Kozlowski Acked-by: Sylwester Nawrocki Acked-by: Rob Herring Acked-for-MFD-by: Lee Jones Signed-off-by: Lee Jones --- .../devicetree/bindings/mfd/samsung,exynos5433-lpass.txt | 2 -- drivers/mfd/exynos-lpass.c | 16 ---------------- include/linux/mfd/syscon/exynos5-pmu.h | 3 --- 3 files changed, 21 deletions(-) diff --git a/Documentation/devicetree/bindings/mfd/samsung,exynos5433-lpass.txt b/Documentation/devicetree/bindings/mfd/samsung,exynos5433-lpass.txt index c110e118b79f..a8deaee82c44 100644 --- a/Documentation/devicetree/bindings/mfd/samsung,exynos5433-lpass.txt +++ b/Documentation/devicetree/bindings/mfd/samsung,exynos5433-lpass.txt @@ -5,7 +5,6 @@ Required properties: - compatible : "samsung,exynos5433-lpass" - reg : should contain the LPASS top SFR region location and size - - samsung,pmu-syscon : the phandle to the Power Management Unit node - #address-cells : should be 1 - #size-cells : should be 1 - ranges : must be present @@ -25,7 +24,6 @@ Example: audio-subsystem { compatible = "samsung,exynos5433-lpass"; reg = <0x11400000 0x100>, <0x11500000 0x08>; - samsung,pmu-syscon = <&pmu_system_controller>; #address-cells = <1>; #size-cells = <1>; ranges; diff --git a/drivers/mfd/exynos-lpass.c b/drivers/mfd/exynos-lpass.c index 8bebad92a385..39be39bbefc4 100644 --- a/drivers/mfd/exynos-lpass.c +++ b/drivers/mfd/exynos-lpass.c @@ -51,8 +51,6 @@ #define LPASS_INTR_SFR BIT(0) struct exynos_lpass { - /* pointer to the Power Management Unit regmap */ - struct regmap *pmu; /* pointer to the LPASS TOP regmap */ struct regmap *top; }; @@ -81,10 +79,6 @@ static void exynos_lpass_enable(struct exynos_lpass *lpass) regmap_write(lpass->top, SFR_LPASS_INTR_CPU_MASK, LPASS_INTR_SFR | LPASS_INTR_DMA | LPASS_INTR_I2S); - /* Activate related PADs from retention state */ - regmap_write(lpass->pmu, EXYNOS5433_PAD_RETENTION_AUD_OPTION, - EXYNOS_WAKEUP_FROM_LOWPWR); - exynos_lpass_core_sw_reset(lpass, LPASS_I2S_SW_RESET); exynos_lpass_core_sw_reset(lpass, LPASS_DMA_SW_RESET); exynos_lpass_core_sw_reset(lpass, LPASS_MEM_SW_RESET); @@ -95,9 +89,6 @@ static void exynos_lpass_disable(struct exynos_lpass *lpass) /* Mask any unmasked IP interrupt sources */ regmap_write(lpass->top, SFR_LPASS_INTR_CPU_MASK, 0); regmap_write(lpass->top, SFR_LPASS_INTR_CA5_MASK, 0); - - /* Deactivate related PADs from retention state */ - regmap_write(lpass->pmu, EXYNOS5433_PAD_RETENTION_AUD_OPTION, 0); } static const struct regmap_config exynos_lpass_reg_conf = { @@ -131,13 +122,6 @@ static int exynos_lpass_probe(struct platform_device *pdev) return PTR_ERR(lpass->top); } - lpass->pmu = syscon_regmap_lookup_by_phandle(dev->of_node, - "samsung,pmu-syscon"); - if (IS_ERR(lpass->pmu)) { - dev_err(dev, "Failed to lookup PMU regmap\n"); - return PTR_ERR(lpass->pmu); - } - platform_set_drvdata(pdev, lpass); exynos_lpass_enable(lpass); diff --git a/include/linux/mfd/syscon/exynos5-pmu.h b/include/linux/mfd/syscon/exynos5-pmu.h index c28ff21ca4d2..0622ae86f9db 100644 --- a/include/linux/mfd/syscon/exynos5-pmu.h +++ b/include/linux/mfd/syscon/exynos5-pmu.h @@ -46,7 +46,4 @@ #define EXYNOS5_MIPI_PHY_S_RESETN BIT(1) #define EXYNOS5_MIPI_PHY_M_RESETN BIT(2) -#define EXYNOS5433_PAD_RETENTION_AUD_OPTION (0x3028) -#define EXYNOS5433_PAD_INITIATE_WAKEUP_FROM_LOWPWR BIT(28) - #endif /* _LINUX_MFD_SYSCON_PMU_EXYNOS5_H_ */ -- cgit v1.2.3-59-g8ed1b From 8f1be5bd14e8faf5a1255a32621601bb11a96232 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Thu, 23 Mar 2017 09:03:25 +0100 Subject: mfd: exynos-lpass: Add support for clocks Exynos LPASS requires some clocks to be enabled to make any access to its registers. This patch adds code for handling such clocks. For current set of registers it is enough to keep sfr0_ctrl clock enabled. Till now it worked only because those clocks were enabled by bootloader and driver probe() happened before they were disabled by clock core because of lack of users. Handling those clocks is also needed to make it possible to enable support for audio power domain. This patch requires adding sfr0_ctrl clock to device tree. Signed-off-by: Marek Szyprowski Reviewed-by: Krzysztof Kozlowski Acked-by: Sylwester Nawrocki Acked-by: Rob Herring Acked-for-MFD-by: Lee Jones Signed-off-by: Lee Jones --- .../devicetree/bindings/mfd/samsung,exynos5433-lpass.txt | 6 ++++++ drivers/mfd/exynos-lpass.c | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/samsung,exynos5433-lpass.txt b/Documentation/devicetree/bindings/mfd/samsung,exynos5433-lpass.txt index a8deaee82c44..df664018c148 100644 --- a/Documentation/devicetree/bindings/mfd/samsung,exynos5433-lpass.txt +++ b/Documentation/devicetree/bindings/mfd/samsung,exynos5433-lpass.txt @@ -5,6 +5,10 @@ Required properties: - compatible : "samsung,exynos5433-lpass" - reg : should contain the LPASS top SFR region location and size + - clock-names : should contain following required clocks: "sfr0_ctrl" + - clocks : should contain clock specifiers of all clocks, which + input names have been specified in clock-names + property, in same order. - #address-cells : should be 1 - #size-cells : should be 1 - ranges : must be present @@ -24,6 +28,8 @@ Example: audio-subsystem { compatible = "samsung,exynos5433-lpass"; reg = <0x11400000 0x100>, <0x11500000 0x08>; + clocks = <&cmu_aud CLK_PCLK_SFR0_CTRL>; + clock-names = "sfr0_ctrl"; #address-cells = <1>; #size-cells = <1>; ranges; diff --git a/drivers/mfd/exynos-lpass.c b/drivers/mfd/exynos-lpass.c index 39be39bbefc4..cbc4a48546c3 100644 --- a/drivers/mfd/exynos-lpass.c +++ b/drivers/mfd/exynos-lpass.c @@ -14,6 +14,7 @@ * only version 2 as published by the Free Software Foundation. */ +#include #include #include #include @@ -53,6 +54,7 @@ struct exynos_lpass { /* pointer to the LPASS TOP regmap */ struct regmap *top; + struct clk *sfr0_clk; }; static void exynos_lpass_core_sw_reset(struct exynos_lpass *lpass, int mask) @@ -72,6 +74,8 @@ static void exynos_lpass_core_sw_reset(struct exynos_lpass *lpass, int mask) static void exynos_lpass_enable(struct exynos_lpass *lpass) { + clk_prepare_enable(lpass->sfr0_clk); + /* Unmask SFR, DMA and I2S interrupt */ regmap_write(lpass->top, SFR_LPASS_INTR_CA5_MASK, LPASS_INTR_SFR | LPASS_INTR_DMA | LPASS_INTR_I2S); @@ -89,6 +93,8 @@ static void exynos_lpass_disable(struct exynos_lpass *lpass) /* Mask any unmasked IP interrupt sources */ regmap_write(lpass->top, SFR_LPASS_INTR_CPU_MASK, 0); regmap_write(lpass->top, SFR_LPASS_INTR_CA5_MASK, 0); + + clk_disable_unprepare(lpass->sfr0_clk); } static const struct regmap_config exynos_lpass_reg_conf = { @@ -115,6 +121,10 @@ static int exynos_lpass_probe(struct platform_device *pdev) if (IS_ERR(base_top)) return PTR_ERR(base_top); + lpass->sfr0_clk = devm_clk_get(dev, "sfr0_ctrl"); + if (IS_ERR(lpass->sfr0_clk)) + return PTR_ERR(lpass->sfr0_clk); + lpass->top = regmap_init_mmio(dev, base_top, &exynos_lpass_reg_conf); if (IS_ERR(lpass->top)) { -- cgit v1.2.3-59-g8ed1b From c414df12bdf7a3e730512ed3971e9d90a494cfa9 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Thu, 23 Mar 2017 09:03:26 +0100 Subject: mfd: exynos-lpass: Add missing remove() function Disable device on driver remove and release allocated regmap. Signed-off-by: Marek Szyprowski Reviewed-by: Krzysztof Kozlowski Acked-by: Sylwester Nawrocki Acked-for-MFD-by: Lee Jones Signed-off-by: Lee Jones --- drivers/mfd/exynos-lpass.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/mfd/exynos-lpass.c b/drivers/mfd/exynos-lpass.c index cbc4a48546c3..9325e0176761 100644 --- a/drivers/mfd/exynos-lpass.c +++ b/drivers/mfd/exynos-lpass.c @@ -138,6 +138,16 @@ static int exynos_lpass_probe(struct platform_device *pdev) return of_platform_populate(dev->of_node, NULL, NULL, dev); } +static int exynos_lpass_remove(struct platform_device *pdev) +{ + struct exynos_lpass *lpass = platform_get_drvdata(pdev); + + exynos_lpass_disable(lpass); + regmap_exit(lpass->top); + + return 0; +} + static int __maybe_unused exynos_lpass_suspend(struct device *dev) { struct exynos_lpass *lpass = dev_get_drvdata(dev); @@ -172,6 +182,7 @@ static struct platform_driver exynos_lpass_driver = { .of_match_table = exynos_lpass_of_match, }, .probe = exynos_lpass_probe, + .remove = exynos_lpass_remove, }; module_platform_driver(exynos_lpass_driver); -- cgit v1.2.3-59-g8ed1b From 90f447170c6f283a80a729ff92aabecdb2206cbe Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Thu, 23 Mar 2017 09:03:27 +0100 Subject: mfd: exynos-lpass: Add runtime PM support Convert exisitng lpass-suspend/resume callbacks into runtime PM callbacks. This way Exynos LPASS driver will be ready for use with power domains enabled. LPASS will be runtime resumed/suspended as a result of its child devices runtime PM transitions. Signed-off-by: Marek Szyprowski Acked-by: Krzysztof Kozlowski Acked-by: Sylwester Nawrocki Signed-off-by: Lee Jones --- drivers/mfd/exynos-lpass.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/exynos-lpass.c b/drivers/mfd/exynos-lpass.c index 9325e0176761..0bf3aebdac45 100644 --- a/drivers/mfd/exynos-lpass.c +++ b/drivers/mfd/exynos-lpass.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -133,6 +134,8 @@ static int exynos_lpass_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, lpass); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); exynos_lpass_enable(lpass); return of_platform_populate(dev->of_node, NULL, NULL, dev); @@ -143,6 +146,9 @@ static int exynos_lpass_remove(struct platform_device *pdev) struct exynos_lpass *lpass = platform_get_drvdata(pdev); exynos_lpass_disable(lpass); + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + exynos_lpass_disable(lpass); regmap_exit(lpass->top); return 0; @@ -166,8 +172,11 @@ static int __maybe_unused exynos_lpass_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(lpass_pm_ops, exynos_lpass_suspend, - exynos_lpass_resume); +static const struct dev_pm_ops lpass_pm_ops = { + SET_RUNTIME_PM_OPS(exynos_lpass_suspend, exynos_lpass_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; static const struct of_device_id exynos_lpass_of_match[] = { { .compatible = "samsung,exynos5433-lpass" }, -- cgit v1.2.3-59-g8ed1b From b5238b41858229b2dcb684cd71d81f4c6d6311c0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 4 Apr 2017 15:38:56 +0300 Subject: mfd: intel_soc_pmic: Fix a mess with compilation units Crystal Cove and Whiskey Cove are two different PMICs which are installed on Intel Atom SoC based platforms. Moreover there are two independent drivers that by some reason were supposed (*) to get into one kernel module. Fix the mess by clarifying Kconfig option for Crystal Cove and split Whiskey Cove out of it. (*) It looks like the configuration was never tested with INTEL_SOC_PMIC=n. The line in Makefile is actually wrong. Cc: "Rafael J. Wysocki" (supporter:ACPI) Acked-by: Linus Walleij Acked-by: Zhang Rui Signed-off-by: Andy Shevchenko Signed-off-by: Lee Jones --- drivers/acpi/Kconfig | 2 +- drivers/gpio/Kconfig | 2 +- drivers/mfd/Kconfig | 15 +++++++++++++-- drivers/mfd/Makefile | 2 +- drivers/platform/x86/Kconfig | 2 +- drivers/thermal/Kconfig | 2 +- 6 files changed, 18 insertions(+), 7 deletions(-) diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 83e5f7e1a20d..03708e08fcb4 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -512,7 +512,7 @@ config XPOWER_PMIC_OPREGION config BXT_WC_PMIC_OPREGION bool "ACPI operation region support for BXT WhiskeyCove PMIC" - depends on INTEL_SOC_PMIC + depends on INTEL_SOC_PMIC_BXTWC help This config adds ACPI operation region support for BXT WhiskeyCove PMIC. diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 05043071fc98..9b1bcb4d0df7 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1054,7 +1054,7 @@ config GPIO_UCB1400 config GPIO_WHISKEY_COVE tristate "GPIO support for Whiskey Cove PMIC" - depends on (X86 || COMPILE_TEST) && INTEL_SOC_PMIC + depends on (X86 || COMPILE_TEST) && INTEL_SOC_PMIC_BXTWC select GPIOLIB_IRQCHIP help Support for GPIO pins on Whiskey Cove PMIC. diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index de68b5ba8741..3eb5c93595f6 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -447,18 +447,29 @@ config LPC_SCH System Management Bus and General Purpose I/O. config INTEL_SOC_PMIC - bool "Support for Intel Atom SoC PMIC" + bool "Support for Crystal Cove PMIC" depends on GPIOLIB depends on I2C=y select MFD_CORE select REGMAP_I2C select REGMAP_IRQ help - Select this option to enable support for the PMIC device + Select this option to enable support for Crystal Cove PMIC on some Intel SoC systems. The PMIC provides ADC, GPIO, thermal, charger and related power management functions on these systems. +config INTEL_SOC_PMIC_BXTWC + tristate "Support for Intel Broxton Whiskey Cove PMIC" + depends on INTEL_PMC_IPC + select MFD_CORE + select REGMAP_IRQ + help + Select this option to enable support for Whiskey Cove PMIC + on Intel Broxton systems. The PMIC provides ADC, GPIO, + thermal, charger and related power management functions + on these systems. + config MFD_INTEL_LPSS tristate select COMMON_CLK diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index fa86dbe65e52..c16bf1ea0ea9 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -212,8 +212,8 @@ obj-$(CONFIG_MFD_RT5033) += rt5033.o obj-$(CONFIG_MFD_SKY81452) += sky81452.o intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o -intel-soc-pmic-$(CONFIG_INTEL_PMC_IPC) += intel_soc_pmic_bxtwc.o obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o +obj-$(CONFIG_INTEL_SOC_PMIC_BXTWC) += intel_soc_pmic_bxtwc.o obj-$(CONFIG_MFD_MT6397) += mt6397-core.o obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 4bc88eb52712..9a949d531162 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -1011,7 +1011,7 @@ config INTEL_PMC_IPC config INTEL_BXTWC_PMIC_TMU tristate "Intel BXT Whiskey Cove TMU Driver" depends on REGMAP - depends on INTEL_SOC_PMIC && INTEL_PMC_IPC + depends on INTEL_SOC_PMIC_BXTWC && INTEL_PMC_IPC ---help--- Select this driver to use Intel BXT Whiskey Cove PMIC TMU feature. This driver enables the alarm wakeup functionality in the TMU unit diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 776b34396144..751e50a3d946 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -365,7 +365,7 @@ endmenu config INTEL_BXT_PMIC_THERMAL tristate "Intel Broxton PMIC thermal driver" - depends on X86 && INTEL_SOC_PMIC && REGMAP + depends on X86 && INTEL_SOC_PMIC_BXTWC && REGMAP help Select this driver for Intel Broxton PMIC with ADC channels monitoring system temperature measurements and alerts. -- cgit v1.2.3-59-g8ed1b From ac03fec1d7d889192cce35edac6fef6026a4d6a1 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 17 Mar 2017 08:58:50 +0100 Subject: mfd: hi655x: Add the clock cell to provide WiFi and Bluetooth The hi655x is a PMIC with regulator but also provides a clock for the WiFi and the bluetooth which is missing in the current implementation. Add the clock cell so it can be used in the next patch via the dts. Signed-off-by: Daniel Lezcano Signed-off-by: Lee Jones --- drivers/mfd/hi655x-pmic.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/hi655x-pmic.c b/drivers/mfd/hi655x-pmic.c index ba706adee38b..c37ccbfd52f2 100644 --- a/drivers/mfd/hi655x-pmic.c +++ b/drivers/mfd/hi655x-pmic.c @@ -77,7 +77,8 @@ static const struct mfd_cell hi655x_pmic_devs[] = { .num_resources = ARRAY_SIZE(pwrkey_resources), .resources = &pwrkey_resources[0], }, - { .name = "hi655x-regulator", }, + { .name = "hi655x-regulator", }, + { .name = "hi655x-clk", }, }; static void hi655x_local_irq_clear(struct regmap *map) -- cgit v1.2.3-59-g8ed1b From a6450cb0388ee58659be5a54a7bfe5bff09532c7 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 10 Apr 2017 13:28:45 +0300 Subject: mfd: lpc_ich: Add support for Intel Gemini Lake SoC Like Intel Apollo Lake, Gemini Lake exposes the serial SPI flash device BAR through hidden P2SB PCI device. We use the same mechanism than Apollo Lake to read the BAR and pass it to the driver. Signed-off-by: Mika Westerberg Signed-off-by: Lee Jones --- drivers/mfd/lpc_ich.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index 7c1b0a32310c..773f1554d2f9 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -227,6 +227,7 @@ enum lpc_chipsets { LPC_LEWISBURG, /* Lewisburg */ LPC_9S, /* 9 Series */ LPC_APL, /* Apollo Lake SoC */ + LPC_GLK, /* Gemini Lake SoC */ LPC_COUGARMOUNTAIN,/* Cougar Mountain SoC*/ }; @@ -555,6 +556,10 @@ static struct lpc_ich_info lpc_chipset_info[] = { .iTCO_version = 5, .spi_type = INTEL_SPI_BXT, }, + [LPC_GLK] = { + .name = "Gemini Lake SoC", + .spi_type = INTEL_SPI_BXT, + }, [LPC_COUGARMOUNTAIN] = { .name = "Cougar Mountain SoC", .iTCO_version = 3, @@ -687,6 +692,7 @@ static const struct pci_device_id lpc_ich_ids[] = { { PCI_VDEVICE(INTEL, 0x2917), LPC_ICH9ME}, { PCI_VDEVICE(INTEL, 0x2918), LPC_ICH9}, { PCI_VDEVICE(INTEL, 0x2919), LPC_ICH9M}, + { PCI_VDEVICE(INTEL, 0x3197), LPC_GLK}, { PCI_VDEVICE(INTEL, 0x2b9c), LPC_COUGARMOUNTAIN}, { PCI_VDEVICE(INTEL, 0x3a14), LPC_ICH10DO}, { PCI_VDEVICE(INTEL, 0x3a16), LPC_ICH10R}, -- cgit v1.2.3-59-g8ed1b From b5b086abe08cb88533cf2c14f7d8b22636657e28 Mon Sep 17 00:00:00 2001 From: Willis Monroe Date: Tue, 11 Apr 2017 09:10:06 -0700 Subject: mfd: palmas: Fixed spelling mistake in error message Fixed a small spelling mistake ("updat" -> "update") in an error message. Signed-off-by: Willis Monroe Signed-off-by: Lee Jones --- drivers/mfd/palmas.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c index f0c559d9fa43..9103affedcbc 100644 --- a/drivers/mfd/palmas.c +++ b/drivers/mfd/palmas.c @@ -581,7 +581,7 @@ static int palmas_i2c_probe(struct i2c_client *i2c, PALMAS_POLARITY_CTRL, PALMAS_POLARITY_CTRL_INT_POLARITY, reg); if (ret < 0) { - dev_err(palmas->dev, "POLARITY_CTRL updat failed: %d\n", ret); + dev_err(palmas->dev, "POLARITY_CTRL update failed: %d\n", ret); goto err_i2c; } -- cgit v1.2.3-59-g8ed1b From 8b8a84c54aff4256d592dc18346c65ecf6811b45 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Sat, 15 Apr 2017 10:05:08 -0700 Subject: mfd: omap-usb-tll: Fix inverted bit use for USB TLL mode Commit 16fa3dc75c22 ("mfd: omap-usb-tll: HOST TLL platform driver") added support for USB TLL, but uses OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF bit the wrong way. The comments in the code are correct, but the inverted use of OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF causes the register to be enabled instead of disabled unlike what the comments say. Without this change the Wrigley 3G LTE modem on droid 4 EHCI bus can be only pinged few times before it stops responding. Fixes: 16fa3dc75c22 ("mfd: omap-usb-tll: HOST TLL platform driver") Signed-off-by: Tony Lindgren Acked-by: Roger Quadros Signed-off-by: Lee Jones --- drivers/mfd/omap-usb-tll.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index 1aa74c4c3ced..9d167c9af2c6 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -377,8 +377,8 @@ int omap_tll_init(struct usbhs_omap_platform_data *pdata) * and use SDR Mode */ reg &= ~(OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE - | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF | OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE); + reg |= OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF; } else if (pdata->port_mode[i] == OMAP_EHCI_PORT_MODE_HSIC) { /* -- cgit v1.2.3-59-g8ed1b From 76d3341b63a2ba173e7163c48918ec45934df98b Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Sat, 15 Apr 2017 10:05:09 -0700 Subject: mfd: omap-usb-tll: Configure ULPIAUTOIDLE The idle mode needs to be only disabled for UTMIAUTOIDLE while ULPIAUTOIDLE can be enabled. This matches the TLL_CHANNEL_CONF_i register configuration for ehci-tll in the Motorola Linux kernel tree for Wrigley 3G LTE modem on droid 4 and the modem still stays responsive. Signed-off-by: Tony Lindgren Acked-by: Roger Quadros Signed-off-by: Lee Jones --- drivers/mfd/omap-usb-tll.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index 9d167c9af2c6..6f5300b0eb31 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -373,12 +373,13 @@ int omap_tll_init(struct usbhs_omap_platform_data *pdata) } else if (pdata->port_mode[i] == OMAP_EHCI_PORT_MODE_TLL) { /* - * Disable AutoIdle, BitStuffing - * and use SDR Mode + * Disable UTMI AutoIdle, BitStuffing + * and use SDR Mode. Enable ULPI AutoIdle. */ reg &= ~(OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE | OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE); reg |= OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF; + reg |= OMAP_TLL_CHANNEL_CONF_ULPI_ULPIAUTOIDLE; } else if (pdata->port_mode[i] == OMAP_EHCI_PORT_MODE_HSIC) { /* -- cgit v1.2.3-59-g8ed1b From d1f4f01a994b13e7cc3d682b816de7fc7e3463d9 Mon Sep 17 00:00:00 2001 From: Martin Kepplinger Date: Mon, 10 Apr 2017 11:37:18 +0200 Subject: mfd: menelaus: Remove obsolete local_irq_disable() and local_irq_enable() Since commit e6229bec25be ("rtc: make rtc_update_irq callable with irqs enabled") rtc_update_irq() is callable with irqs enabled, see the rtc drivers. So update this accordingly. Signed-off-by: Martin Kepplinger Acked-by: Aaro Koskinen Signed-off-by: Lee Jones --- drivers/mfd/menelaus.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/mfd/menelaus.c b/drivers/mfd/menelaus.c index a4a8f1ec3fb6..29b7164a823b 100644 --- a/drivers/mfd/menelaus.c +++ b/drivers/mfd/menelaus.c @@ -1022,9 +1022,7 @@ static int menelaus_set_alarm(struct device *dev, struct rtc_wkalrm *w) static void menelaus_rtc_update_work(struct menelaus_chip *m) { /* report 1/sec update */ - local_irq_disable(); rtc_update_irq(m->rtc, 1, RTC_IRQF | RTC_UF); - local_irq_enable(); } static int menelaus_ioctl(struct device *dev, unsigned cmd, unsigned long arg) @@ -1086,9 +1084,7 @@ static const struct rtc_class_ops menelaus_rtc_ops = { static void menelaus_rtc_alarm_work(struct menelaus_chip *m) { /* report alarm */ - local_irq_disable(); rtc_update_irq(m->rtc, 1, RTC_IRQF | RTC_AF); - local_irq_enable(); /* then disable it; alarms are oneshot */ the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN; -- cgit v1.2.3-59-g8ed1b From db43b8d04d7a9fa0ac18db6de2c4741dcefbcc0b Mon Sep 17 00:00:00 2001 From: Pan Bian Date: Sun, 23 Apr 2017 20:29:24 +0800 Subject: mfd: intel_soc_pmic_core: Fix unchecked return value unction devm_regmap_init_i2c() returns an ERR_PTR on errors, and its return value should be checked before it is dereferenced. However, in function intel_soc_pmic_i2c_probe(), the return value of function devm_regmap_init_i2c() is used without validation. This patch fixes it. Signed-off-by: Pan Bian Signed-off-by: Lee Jones --- drivers/mfd/intel_soc_pmic_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mfd/intel_soc_pmic_core.c b/drivers/mfd/intel_soc_pmic_core.c index 3b5583586662..13737be6df35 100644 --- a/drivers/mfd/intel_soc_pmic_core.c +++ b/drivers/mfd/intel_soc_pmic_core.c @@ -66,6 +66,9 @@ static int intel_soc_pmic_i2c_probe(struct i2c_client *i2c, dev_set_drvdata(dev, pmic); pmic->regmap = devm_regmap_init_i2c(i2c, config->regmap_config); + if (IS_ERR(pmic->regmap)) + return PTR_ERR(pmic->regmap); + pmic->irq = i2c->irq; ret = regmap_add_irq_chip(pmic->regmap, pmic->irq, -- cgit v1.2.3-59-g8ed1b From 7366709c5ab72841bd6f74b0cf3fab5ca368ac23 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Mon, 17 Apr 2017 19:57:38 +0800 Subject: dt-bindings: Make AXP20X compatible strings one per line In the binding documentation of AXP20X mfd, the compatible strings used to be listed for three per line, which leads to some mess when trying to add AXP803 compatible string (as we have already AXP806 and AXP809 compatibles, which is after AXP803 in ascending order). Make the compatible strings one per line, so that inserting a new compatible string will be directly a new line. Signed-off-by: Icenowy Zheng Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/axp20x.txt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/mfd/axp20x.txt b/Documentation/devicetree/bindings/mfd/axp20x.txt index b41d2601c6ba..a3e813f6060a 100644 --- a/Documentation/devicetree/bindings/mfd/axp20x.txt +++ b/Documentation/devicetree/bindings/mfd/axp20x.txt @@ -9,9 +9,14 @@ axp223 (X-Powers) axp809 (X-Powers) Required properties: -- compatible: "x-powers,axp152", "x-powers,axp202", "x-powers,axp209", - "x-powers,axp221", "x-powers,axp223", "x-powers,axp806", - "x-powers,axp809" +- compatible: should be one of: + * "x-powers,axp152" + * "x-powers,axp202" + * "x-powers,axp209" + * "x-powers,axp221" + * "x-powers,axp223" + * "x-powers,axp806" + * "x-powers,axp809" - reg: The I2C slave address or RSB hardware address for the AXP chip - interrupt-parent: The parent interrupt controller - interrupts: SoC NMI / GPIO interrupt connected to the PMIC's IRQ pin -- cgit v1.2.3-59-g8ed1b From 19b465e9560c5fb415f1d1159ae8a580b731436b Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Mon, 17 Apr 2017 19:57:39 +0800 Subject: dt-bindings: Add device tree binding for X-Powers AXP803 PMIC AXP803 is a PMIC produced by Shenzhen X-Powers, with either I2C or RSB bus. Add a compatible for it. Signed-off-by: Icenowy Zheng Acked-by: Chen-Yu Tsai Acked-by: Rob Herring Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/axp20x.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/axp20x.txt b/Documentation/devicetree/bindings/mfd/axp20x.txt index a3e813f6060a..44df88be3c89 100644 --- a/Documentation/devicetree/bindings/mfd/axp20x.txt +++ b/Documentation/devicetree/bindings/mfd/axp20x.txt @@ -6,6 +6,7 @@ axp202 (X-Powers) axp209 (X-Powers) axp221 (X-Powers) axp223 (X-Powers) +axp803 (X-Powers) axp809 (X-Powers) Required properties: @@ -15,6 +16,7 @@ Required properties: * "x-powers,axp209" * "x-powers,axp221" * "x-powers,axp223" + * "x-powers,axp803" * "x-powers,axp806" * "x-powers,axp809" - reg: The I2C slave address or RSB hardware address for the AXP chip -- cgit v1.2.3-59-g8ed1b From 1578353e05cd23b10a9e5e8d1626e5bd0849d873 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Mon, 17 Apr 2017 19:57:40 +0800 Subject: mfd: axp20x: Support AXP803 variant AXP803 is a new PMIC chip produced by X-Powers, usually paired with A64 via RSB bus. The PMIC itself is like AXP288, but with RSB support and dedicated VBUS and ACIN. Add support for it in the axp20x mfd driver. Currently only power key function is supported. Signed-off-by: Icenowy Zheng Signed-off-by: Lee Jones --- drivers/mfd/axp20x-rsb.c | 1 + drivers/mfd/axp20x.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/axp20x.h | 40 ++++++++++++++++++++++- 3 files changed, 119 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/axp20x-rsb.c b/drivers/mfd/axp20x-rsb.c index a732cb50bcff..fd5c7267b136 100644 --- a/drivers/mfd/axp20x-rsb.c +++ b/drivers/mfd/axp20x-rsb.c @@ -61,6 +61,7 @@ static int axp20x_rsb_remove(struct sunxi_rsb_device *rdev) static const struct of_device_id axp20x_rsb_of_match[] = { { .compatible = "x-powers,axp223", .data = (void *)AXP223_ID }, + { .compatible = "x-powers,axp803", .data = (void *)AXP803_ID }, { .compatible = "x-powers,axp806", .data = (void *)AXP806_ID }, { .compatible = "x-powers,axp809", .data = (void *)AXP809_ID }, { }, diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index e6f55079876e..1dc6235778eb 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -41,6 +41,7 @@ static const char * const axp20x_model_names[] = { "AXP221", "AXP223", "AXP288", + "AXP803", "AXP806", "AXP809", }; @@ -117,6 +118,7 @@ static const struct regmap_access_table axp22x_volatile_table = { .n_yes_ranges = ARRAY_SIZE(axp22x_volatile_ranges), }; +/* AXP288 ranges are shared with the AXP803, as they cover the same range */ static const struct regmap_range axp288_writeable_ranges[] = { regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ6_STATE), regmap_reg_range(AXP20X_DCDC_MODE, AXP288_FG_TUNE5), @@ -264,6 +266,20 @@ static struct resource axp288_fuel_gauge_resources[] = { }, }; +static struct resource axp803_pek_resources[] = { + { + .name = "PEK_DBR", + .start = AXP803_IRQ_PEK_RIS_EDGE, + .end = AXP803_IRQ_PEK_RIS_EDGE, + .flags = IORESOURCE_IRQ, + }, { + .name = "PEK_DBF", + .start = AXP803_IRQ_PEK_FAL_EDGE, + .end = AXP803_IRQ_PEK_FAL_EDGE, + .flags = IORESOURCE_IRQ, + }, +}; + static struct resource axp809_pek_resources[] = { { .name = "PEK_DBR", @@ -457,6 +473,43 @@ static const struct regmap_irq axp288_regmap_irqs[] = { INIT_REGMAP_IRQ(AXP288, BC_USB_CHNG, 5, 1), }; +static const struct regmap_irq axp803_regmap_irqs[] = { + INIT_REGMAP_IRQ(AXP803, ACIN_OVER_V, 0, 7), + INIT_REGMAP_IRQ(AXP803, ACIN_PLUGIN, 0, 6), + INIT_REGMAP_IRQ(AXP803, ACIN_REMOVAL, 0, 5), + INIT_REGMAP_IRQ(AXP803, VBUS_OVER_V, 0, 4), + INIT_REGMAP_IRQ(AXP803, VBUS_PLUGIN, 0, 3), + INIT_REGMAP_IRQ(AXP803, VBUS_REMOVAL, 0, 2), + INIT_REGMAP_IRQ(AXP803, BATT_PLUGIN, 1, 7), + INIT_REGMAP_IRQ(AXP803, BATT_REMOVAL, 1, 6), + INIT_REGMAP_IRQ(AXP803, BATT_ENT_ACT_MODE, 1, 5), + INIT_REGMAP_IRQ(AXP803, BATT_EXIT_ACT_MODE, 1, 4), + INIT_REGMAP_IRQ(AXP803, CHARG, 1, 3), + INIT_REGMAP_IRQ(AXP803, CHARG_DONE, 1, 2), + INIT_REGMAP_IRQ(AXP803, BATT_CHG_TEMP_HIGH, 2, 7), + INIT_REGMAP_IRQ(AXP803, BATT_CHG_TEMP_HIGH_END, 2, 6), + INIT_REGMAP_IRQ(AXP803, BATT_CHG_TEMP_LOW, 2, 5), + INIT_REGMAP_IRQ(AXP803, BATT_CHG_TEMP_LOW_END, 2, 4), + INIT_REGMAP_IRQ(AXP803, BATT_ACT_TEMP_HIGH, 2, 3), + INIT_REGMAP_IRQ(AXP803, BATT_ACT_TEMP_HIGH_END, 2, 2), + INIT_REGMAP_IRQ(AXP803, BATT_ACT_TEMP_LOW, 2, 1), + INIT_REGMAP_IRQ(AXP803, BATT_ACT_TEMP_LOW_END, 2, 0), + INIT_REGMAP_IRQ(AXP803, DIE_TEMP_HIGH, 3, 7), + INIT_REGMAP_IRQ(AXP803, GPADC, 3, 2), + INIT_REGMAP_IRQ(AXP803, LOW_PWR_LVL1, 3, 1), + INIT_REGMAP_IRQ(AXP803, LOW_PWR_LVL2, 3, 0), + INIT_REGMAP_IRQ(AXP803, TIMER, 4, 7), + INIT_REGMAP_IRQ(AXP803, PEK_RIS_EDGE, 4, 6), + INIT_REGMAP_IRQ(AXP803, PEK_FAL_EDGE, 4, 5), + INIT_REGMAP_IRQ(AXP803, PEK_SHORT, 4, 4), + INIT_REGMAP_IRQ(AXP803, PEK_LONG, 4, 3), + INIT_REGMAP_IRQ(AXP803, PEK_OVER_OFF, 4, 2), + INIT_REGMAP_IRQ(AXP803, GPIO1_INPUT, 4, 1), + INIT_REGMAP_IRQ(AXP803, GPIO0_INPUT, 4, 0), + INIT_REGMAP_IRQ(AXP803, BC_USB_CHNG, 5, 1), + INIT_REGMAP_IRQ(AXP803, MV_CHNG, 5, 0), +}; + static const struct regmap_irq axp806_regmap_irqs[] = { INIT_REGMAP_IRQ(AXP806, DIE_TEMP_HIGH_LV1, 0, 0), INIT_REGMAP_IRQ(AXP806, DIE_TEMP_HIGH_LV2, 0, 1), @@ -557,6 +610,18 @@ static const struct regmap_irq_chip axp288_regmap_irq_chip = { }; +static const struct regmap_irq_chip axp803_regmap_irq_chip = { + .name = "axp803", + .status_base = AXP20X_IRQ1_STATE, + .ack_base = AXP20X_IRQ1_STATE, + .mask_base = AXP20X_IRQ1_EN, + .mask_invert = true, + .init_ack_masked = true, + .irqs = axp803_regmap_irqs, + .num_irqs = ARRAY_SIZE(axp803_regmap_irqs), + .num_regs = 6, +}; + static const struct regmap_irq_chip axp806_regmap_irq_chip = { .name = "axp806", .status_base = AXP20X_IRQ1_STATE, @@ -778,6 +843,14 @@ static struct mfd_cell axp288_cells[] = { }, }; +static struct mfd_cell axp803_cells[] = { + { + .name = "axp20x-pek", + .num_resources = ARRAY_SIZE(axp803_pek_resources), + .resources = axp803_pek_resources, + } +}; + static struct mfd_cell axp806_cells[] = { { .id = 2, @@ -864,6 +937,12 @@ int axp20x_match_device(struct axp20x_dev *axp20x) axp20x->regmap_irq_chip = &axp288_regmap_irq_chip; axp20x->irq_flags = IRQF_TRIGGER_LOW; break; + case AXP803_ID: + axp20x->nr_cells = ARRAY_SIZE(axp803_cells); + axp20x->cells = axp803_cells; + axp20x->regmap_cfg = &axp288_regmap_config; + axp20x->regmap_irq_chip = &axp803_regmap_irq_chip; + break; case AXP806_ID: axp20x->nr_cells = ARRAY_SIZE(axp806_cells); axp20x->cells = axp806_cells; diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h index dc8798cf2a24..cde56cfe8446 100644 --- a/include/linux/mfd/axp20x.h +++ b/include/linux/mfd/axp20x.h @@ -20,6 +20,7 @@ enum axp20x_variants { AXP221_ID, AXP223_ID, AXP288_ID, + AXP803_ID, AXP806_ID, AXP809_ID, NR_AXP20X_VARIANTS, @@ -234,7 +235,7 @@ enum axp20x_variants { #define AXP22X_TS_ADC_L 0x59 #define AXP22X_BATLOW_THRES1 0xe6 -/* AXP288 specific registers */ +/* AXP288/AXP803 specific registers */ #define AXP288_POWER_REASON 0x02 #define AXP288_BC_GLOBAL 0x2c #define AXP288_BC_VBUS_CNTL 0x2d @@ -475,6 +476,43 @@ enum axp288_irqs { AXP288_IRQ_BC_USB_CHNG, }; +enum axp803_irqs { + AXP803_IRQ_ACIN_OVER_V = 1, + AXP803_IRQ_ACIN_PLUGIN, + AXP803_IRQ_ACIN_REMOVAL, + AXP803_IRQ_VBUS_OVER_V, + AXP803_IRQ_VBUS_PLUGIN, + AXP803_IRQ_VBUS_REMOVAL, + AXP803_IRQ_BATT_PLUGIN, + AXP803_IRQ_BATT_REMOVAL, + AXP803_IRQ_BATT_ENT_ACT_MODE, + AXP803_IRQ_BATT_EXIT_ACT_MODE, + AXP803_IRQ_CHARG, + AXP803_IRQ_CHARG_DONE, + AXP803_IRQ_BATT_CHG_TEMP_HIGH, + AXP803_IRQ_BATT_CHG_TEMP_HIGH_END, + AXP803_IRQ_BATT_CHG_TEMP_LOW, + AXP803_IRQ_BATT_CHG_TEMP_LOW_END, + AXP803_IRQ_BATT_ACT_TEMP_HIGH, + AXP803_IRQ_BATT_ACT_TEMP_HIGH_END, + AXP803_IRQ_BATT_ACT_TEMP_LOW, + AXP803_IRQ_BATT_ACT_TEMP_LOW_END, + AXP803_IRQ_DIE_TEMP_HIGH, + AXP803_IRQ_GPADC, + AXP803_IRQ_LOW_PWR_LVL1, + AXP803_IRQ_LOW_PWR_LVL2, + AXP803_IRQ_TIMER, + AXP803_IRQ_PEK_RIS_EDGE, + AXP803_IRQ_PEK_FAL_EDGE, + AXP803_IRQ_PEK_SHORT, + AXP803_IRQ_PEK_LONG, + AXP803_IRQ_PEK_OVER_OFF, + AXP803_IRQ_GPIO1_INPUT, + AXP803_IRQ_GPIO0_INPUT, + AXP803_IRQ_BC_USB_CHNG, + AXP803_IRQ_MV_CHNG, +}; + enum axp806_irqs { AXP806_IRQ_DIE_TEMP_HIGH_LV1, AXP806_IRQ_DIE_TEMP_HIGH_LV2, -- cgit v1.2.3-59-g8ed1b From d096ae4bb0fa3116fdd4941c65fc577f76ad453c Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Mon, 17 Apr 2017 19:57:42 +0800 Subject: dt-bindings: Add AXP803's regulator info AXP803 have the most regulators in currently supported AXP PMICs. Add info for the regulators in the dt-bindings document. Signed-off-by: Icenowy Zheng Acked-by: Rob Herring Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/axp20x.txt | 27 ++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/axp20x.txt b/Documentation/devicetree/bindings/mfd/axp20x.txt index 44df88be3c89..aca09af66514 100644 --- a/Documentation/devicetree/bindings/mfd/axp20x.txt +++ b/Documentation/devicetree/bindings/mfd/axp20x.txt @@ -96,6 +96,33 @@ LDO_IO1 : LDO : ips-supply : GPIO 1 RTC_LDO : LDO : ips-supply : always on DRIVEVBUS : Enable output : drivevbus-supply : external regulator +AXP803 regulators, type, and corresponding input supply names: + +Regulator Type Supply Name Notes +--------- ---- ----------- ----- +DCDC1 : DC-DC buck : vin1-supply +DCDC2 : DC-DC buck : vin2-supply : poly-phase capable +DCDC3 : DC-DC buck : vin3-supply : poly-phase capable +DCDC4 : DC-DC buck : vin4-supply +DCDC5 : DC-DC buck : vin5-supply : poly-phase capable +DCDC6 : DC-DC buck : vin6-supply : poly-phase capable +DC1SW : On/Off Switch : : DCDC1 secondary output +ALDO1 : LDO : aldoin-supply : shared supply +ALDO2 : LDO : aldoin-supply : shared supply +ALDO3 : LDO : aldoin-supply : shared supply +DLDO1 : LDO : dldoin-supply : shared supply +DLDO2 : LDO : dldoin-supply : shared supply +DLDO3 : LDO : dldoin-supply : shared supply +DLDO4 : LDO : dldoin-supply : shared supply +ELDO1 : LDO : eldoin-supply : shared supply +ELDO2 : LDO : eldoin-supply : shared supply +ELDO3 : LDO : eldoin-supply : shared supply +FLDO1 : LDO : fldoin-supply : shared supply +FLDO2 : LDO : fldoin-supply : shared supply +LDO_IO0 : LDO : ips-supply : GPIO 0 +LDO_IO1 : LDO : ips-supply : GPIO 1 +RTC_LDO : LDO : ips-supply : always on + AXP806 regulators, type, and corresponding input supply names: Regulator Type Supply Name Notes -- cgit v1.2.3-59-g8ed1b From ab6241ae07c3c698543b565e4ea41995a29a3f62 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 27 Apr 2017 00:04:31 +0300 Subject: input: touchscreen: mxs-lradc: || vs && typos These tests are meaningless as is because "adapt" can't possibly be both less than 1 and greater than 32. Fixes: d81ca730e3e4 ("input: touchscreen: mxs-lradc: Add support for touchscreen") Signed-off-by: Dan Carpenter Acked-by: Dmitry Torokhov Signed-off-by: Lee Jones --- drivers/input/touchscreen/mxs-lradc-ts.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/mxs-lradc-ts.c b/drivers/input/touchscreen/mxs-lradc-ts.c index 4b4aebfe3e7f..58c016cd6809 100644 --- a/drivers/input/touchscreen/mxs-lradc-ts.c +++ b/drivers/input/touchscreen/mxs-lradc-ts.c @@ -642,7 +642,7 @@ static int mxs_lradc_ts_probe(struct platform_device *pdev) if (of_property_read_u32(node, "fsl,ave-ctrl", &adapt)) { ts->over_sample_cnt = 4; } else { - if (adapt >= 1 || adapt <= 32) { + if (adapt >= 1 && adapt <= 32) { ts->over_sample_cnt = adapt; } else { dev_err(ts->dev, "Invalid sample count (%u)\n", @@ -654,7 +654,7 @@ static int mxs_lradc_ts_probe(struct platform_device *pdev) if (of_property_read_u32(node, "fsl,ave-delay", &adapt)) { ts->over_sample_delay = 2; } else { - if (adapt >= 2 || adapt <= LRADC_DELAY_DELAY_MASK + 1) { + if (adapt >= 2 && adapt <= LRADC_DELAY_DELAY_MASK + 1) { ts->over_sample_delay = adapt; } else { dev_err(ts->dev, "Invalid sample delay (%u)\n", @@ -666,7 +666,7 @@ static int mxs_lradc_ts_probe(struct platform_device *pdev) if (of_property_read_u32(node, "fsl,settling", &adapt)) { ts->settling_delay = 10; } else { - if (adapt >= 1 || adapt <= LRADC_DELAY_DELAY_MASK) { + if (adapt >= 1 && adapt <= LRADC_DELAY_DELAY_MASK) { ts->settling_delay = adapt; } else { dev_err(ts->dev, "Invalid settling delay (%u)\n", -- cgit v1.2.3-59-g8ed1b