Merge branches 'ib-mfd-arm-leds-5.2', 'ib-mfd-gpio-input-leds-power-5.2', 'ib-mfd...
authorLee Jones <lee.jones@linaro.org>
Tue, 14 May 2019 07:09:23 +0000 (08:09 +0100)
committerLee Jones <lee.jones@linaro.org>
Tue, 14 May 2019 07:09:23 +0000 (08:09 +0100)
Immutable branch between MFD, ARM and Net due for the 5.2 merge window

29 files changed:
Documentation/devicetree/bindings/arm/altera/socfpga-system.txt
Documentation/devicetree/bindings/leds/leds-lm3532.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/max77620.txt
Documentation/devicetree/bindings/mfd/stmfx.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/ti-lmu.txt
Documentation/devicetree/bindings/pinctrl/pinctrl-stmfx.txt [new file with mode: 0644]
MAINTAINERS
arch/arm/boot/dts/omap4-droid4-xt894.dts
arch/arm/configs/socfpga_defconfig
arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi
arch/arm64/configs/defconfig
drivers/leds/Kconfig
drivers/leds/Makefile
drivers/leds/leds-lm3532.c [new file with mode: 0644]
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/altera-sysmgr.c [new file with mode: 0644]
drivers/mfd/max77620.c
drivers/mfd/stmfx.c [new file with mode: 0644]
drivers/mfd/ti-lmu.c
drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
drivers/pinctrl/Kconfig
drivers/pinctrl/Makefile
drivers/pinctrl/pinctrl-stmfx.c [new file with mode: 0644]
include/linux/mfd/altera-sysmgr.h [new file with mode: 0644]
include/linux/mfd/max77620.h
include/linux/mfd/stmfx.h [new file with mode: 0644]
include/linux/mfd/ti-lmu-register.h
include/linux/mfd/ti-lmu.h

index f4d04a0672824087de6987b6be4dfd5c4fd2bf84..82edbaaa3f85beeafdf3b96c957776f8b03e90fc 100644 (file)
@@ -11,3 +11,15 @@ Example:
                reg = <0xffd08000 0x1000>;
                cpu1-start-addr = <0xffd080c4>;
        };
+
+ARM64 - Stratix10
+Required properties:
+- compatible : "altr,sys-mgr-s10"
+- reg : Should contain 1 register range(address and length)
+        for system manager register.
+
+Example:
+        sysmgr@ffd12000 {
+               compatible = "altr,sys-mgr-s10";
+               reg = <0xffd12000 0x228>;
+       };
diff --git a/Documentation/devicetree/bindings/leds/leds-lm3532.txt b/Documentation/devicetree/bindings/leds/leds-lm3532.txt
new file mode 100644 (file)
index 0000000..c087f85
--- /dev/null
@@ -0,0 +1,101 @@
+* Texas Instruments - lm3532 White LED driver with ambient light sensing
+capability.
+
+The LM3532 provides the 3 high-voltage, low-side current sinks. The device is
+programmable over an I2C-compatible interface and has independent
+current control for all three channels. The adaptive current regulation
+method allows for different LED currents in each current sink thus allowing
+for a wide variety of backlight and keypad applications.
+
+The main features of the LM3532 include dual ambient light sensor inputs
+each with 32 internal voltage setting resistors, 8-bit logarithmic and linear
+brightness control, dual external PWM brightness control inputs, and up to
+1000:1 dimming ratio with programmable fade in and fade out settings.
+
+Required properties:
+       - compatible : "ti,lm3532"
+       - reg : I2C slave address
+       - #address-cells : 1
+       - #size-cells : 0
+
+Optional properties:
+       - enable-gpios : gpio pin to enable (active high)/disable the device.
+       - ramp-up-us - The Run time ramp rates/step are from one current
+                      set-point to another after the device has reached its
+                      initial target set point from turn-on
+       - ramp-down-us - The Run time ramp rates/step are from one current
+                        set-point to another after the device has reached its
+                        initial target set point from turn-on
+       Range for ramp settings: 8us - 65536us
+
+Optional properties if ALS mode is used:
+       - ti,als-vmin - Minimum ALS voltage defined in Volts
+       - ti,als-vmax - Maximum ALS voltage defined in Volts
+       Per the data sheet the max ALS voltage is 2V and the min is 0V
+
+       - ti,als1-imp-sel - ALS1 impedance resistor selection in Ohms
+       - ti,als2-imp-sel - ALS2 impedance resistor selection in Ohms
+       Range for impedance select: 37000 Ohms - 1190 Ohms
+       Values above 37kohms will be set to the "High Impedance" setting
+
+       - ti,als-avrg-time-us - Determines the length of time the device needs to
+                         average the two ALS inputs.  This is only used if
+                         the input mode is LM3532_ALS_INPUT_AVRG.
+                            Range: 17920us - 2293760us
+       - ti,als-input-mode - Determines how the device uses the attached ALS
+                          devices.
+                          0x00 - ALS1 and ALS2 input average
+                          0x01 - ALS1 Input
+                          0x02 - ALS2 Input
+                          0x03 - Max of ALS1 and ALS2
+
+Required child properties:
+       - reg : Indicates control bank the LED string is controlled by
+       - led-sources : see Documentation/devicetree/bindings/leds/common.txt
+       - ti,led-mode : Defines if the LED strings are manually controlled or
+                       if the LED strings are controlled by the ALS.
+                       0x00 - LED strings are I2C controlled via full scale
+                              brightness control register
+                       0x01 - LED strings are ALS controlled
+
+Optional LED child properties:
+       - label : see Documentation/devicetree/bindings/leds/common.txt
+       - linux,default-trigger :
+          see Documentation/devicetree/bindings/leds/common.txt
+
+Example:
+led-controller@38 {
+       compatible = "ti,lm3532";
+       #address-cells = <1>;
+       #size-cells = <0>;
+       reg = <0x38>;
+
+       enable-gpios = <&gpio6 12 GPIO_ACTIVE_HIGH>;
+       ramp-up-us = <1024>;
+       ramp-down-us = <65536>;
+
+       ti,als-vmin = <0>;
+       ti,als-vmax = <2000>;
+       ti,als1-imp-sel = <4110>;
+       ti,als2-imp-sel = <2180>;
+       ti,als-avrg-time-us = <17920>;
+       ti,als-input-mode = <0x00>;
+
+       led@0 {
+               reg = <0>;
+               led-sources = <2>;
+               ti,led-mode = <1>;
+               label = ":backlight";
+               linux,default-trigger = "backlight";
+       };
+
+       led@1 {
+               reg = <1>;
+               led-sources = <1>;
+               ti,led-mode = <0>;
+               label = ":kbd_backlight";
+       };
+};
+
+For more product information please see the links below:
+http://www.ti.com/product/LM3532
index 9c16d51cc15b913ce900c622741eb51904224686..5a642a51d58e6fd0da2cd4d3d76ad1a8bef409af 100644 (file)
@@ -4,7 +4,8 @@ Required properties:
 -------------------
 - compatible: Must be one of
                "maxim,max77620"
-               "maxim,max20024".
+               "maxim,max20024"
+               "maxim,max77663"
 - reg: I2C device address.
 
 Optional properties:
@@ -17,6 +18,11 @@ Optional properties:
                        IRQ numbers for different interrupt source of MAX77620
                        are defined at dt-bindings/mfd/max77620.h.
 
+- system-power-controller: Indicates that this PMIC is controlling the
+                          system power, see [1] for more details.
+
+[1] Documentation/devicetree/bindings/power/power-controller.txt
+
 Optional subnodes and their properties:
 =======================================
 
@@ -105,6 +111,7 @@ Optional properties:
 Here supported time periods by device in microseconds are as follows:
 MAX77620 supports 40, 80, 160, 320, 640, 1280, 2560 and 5120 microseconds.
 MAX20024 supports 20, 40, 80, 160, 320, 640, 1280 and 2540 microseconds.
+MAX77663 supports 20, 40, 80, 160, 320, 640, 1280 and 2540 microseconds.
 
 -maxim,power-ok-control: configure map power ok bit
                        1: Enables POK(Power OK) to control nRST_IO and GPIO1
diff --git a/Documentation/devicetree/bindings/mfd/stmfx.txt b/Documentation/devicetree/bindings/mfd/stmfx.txt
new file mode 100644 (file)
index 0000000..f0c2f7f
--- /dev/null
@@ -0,0 +1,28 @@
+STMicroelectonics Multi-Function eXpander (STMFX) Core bindings
+
+ST Multi-Function eXpander (STMFX) is a slave controller using I2C for
+communication with the main MCU. Its main features are GPIO expansion, main
+MCU IDD measurement (IDD is the amount of current that flows through VDD) and
+resistive touchscreen controller.
+
+Required properties:
+- compatible: should be "st,stmfx-0300".
+- reg: I2C slave address of the device.
+- interrupts: interrupt specifier triggered by MFX_IRQ_OUT signal.
+  Please refer to ../interrupt-controller/interrupt.txt
+
+Optional properties:
+- drive-open-drain: configure MFX_IRQ_OUT as open drain.
+- vdd-supply: phandle of the regulator supplying STMFX.
+
+Example:
+
+       stmfx: stmfx@42 {
+               compatible = "st,stmfx-0300";
+               reg = <0x42>;
+               interrupts = <8 IRQ_TYPE_EDGE_RISING>;
+               interrupt-parent = <&gpioi>;
+               vdd-supply = <&v3v3>;
+       };
+
+Please refer to ../pinctrl/pinctrl-stmfx.txt for STMFX GPIO expander function bindings.
index c885cf89b8ce83ce29b9d603011859f5962493a4..980394d701a7275f5a26738ec13d90c349998d3b 100644 (file)
@@ -4,7 +4,6 @@ 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
@@ -13,7 +12,6 @@ TI LMU driver supports lighting devices below.
 
 Required properties:
   - compatible: Should be one of:
-                "ti,lm3532"
                 "ti,lm3631"
                 "ti,lm3632"
                 "ti,lm3633"
@@ -23,7 +21,6 @@ Required properties:
          0x11 for LM3632
          0x29 for LM3631
          0x36 for LM3633, LM3697
-         0x38 for LM3532
          0x63 for LM3695
 
 Optional property:
@@ -47,23 +44,6 @@ Optional nodes:
 [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>;
diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-stmfx.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-stmfx.txt
new file mode 100644 (file)
index 0000000..c1b4c18
--- /dev/null
@@ -0,0 +1,116 @@
+STMicroelectronics Multi-Function eXpander (STMFX) GPIO expander bindings
+
+ST Multi-Function eXpander (STMFX) offers up to 24 GPIOs expansion.
+Please refer to ../mfd/stmfx.txt for STMFX Core bindings.
+
+Required properties:
+- compatible: should be "st,stmfx-0300-pinctrl".
+- #gpio-cells: should be <2>, the first cell is the GPIO number and the second
+  cell is the gpio flags in accordance with <dt-bindings/gpio/gpio.h>.
+- gpio-controller: marks the device as a GPIO controller.
+- #interrupt-cells: should be <2>, the first cell is the GPIO number and the
+  second cell is the interrupt flags in accordance with
+  <dt-bindings/interrupt-controller/irq.h>.
+- interrupt-controller: marks the device as an interrupt controller.
+- gpio-ranges: specifies the mapping between gpio controller and pin
+  controller pins. Check "Concerning gpio-ranges property" below.
+Please refer to ../gpio/gpio.txt.
+
+Please refer to pinctrl-bindings.txt for pin configuration.
+
+Required properties for pin configuration sub-nodes:
+- pins: list of pins to which the configuration applies.
+
+Optional properties for pin configuration sub-nodes (pinconf-generic ones):
+- bias-disable: disable any bias on the pin.
+- bias-pull-up: the pin will be pulled up.
+- bias-pull-pin-default: use the pin-default pull state.
+- bias-pull-down: the pin will be pulled down.
+- drive-open-drain: the pin will be driven with open drain.
+- drive-push-pull: the pin will be driven actively high and low.
+- output-high: the pin will be configured as an output driving high level.
+- output-low: the pin will be configured as an output driving low level.
+
+Note that STMFX pins[15:0] are called "gpio[15:0]", and STMFX pins[23:16] are
+called "agpio[7:0]". Example, to refer to pin 18 of STMFX, use "agpio2".
+
+Concerning gpio-ranges property:
+- if all STMFX pins[24:0] are available (no other STMFX function in use), you
+  should use gpio-ranges = <&stmfx_pinctrl 0 0 24>;
+- if agpio[3:0] are not available (STMFX Touchscreen function in use), you
+  should use gpio-ranges = <&stmfx_pinctrl 0 0 16>, <&stmfx_pinctrl 20 20 4>;
+- if agpio[7:4] are not available (STMFX IDD function in use), you
+  should use gpio-ranges = <&stmfx_pinctrl 0 0 20>;
+
+
+Example:
+
+       stmfx: stmfx@42 {
+               ...
+
+               stmfx_pinctrl: stmfx-pin-controller {
+                       compatible = "st,stmfx-0300-pinctrl";
+                       #gpio-cells = <2>;
+                       #interrupt-cells = <2>;
+                       gpio-controller;
+                       interrupt-controller;
+                       gpio-ranges = <&stmfx_pinctrl 0 0 24>;
+
+                       joystick_pins: joystick {
+                               pins = "gpio0", "gpio1", "gpio2", "gpio3", "gpio4";
+                               drive-push-pull;
+                               bias-pull-up;
+                       };
+               };
+       };
+
+Example of STMFX GPIO consumers:
+
+       joystick {
+               compatible = "gpio-keys";
+               #address-cells = <1>;
+               #size-cells = <0>;
+               pinctrl-0 = <&joystick_pins>;
+               pinctrl-names = "default";
+               button-0 {
+                       label = "JoySel";
+                       linux,code = <KEY_ENTER>;
+                       interrupt-parent = <&stmfx_pinctrl>;
+                       interrupts = <0 IRQ_TYPE_EDGE_RISING>;
+               };
+               button-1 {
+                       label = "JoyDown";
+                       linux,code = <KEY_DOWN>;
+                       interrupt-parent = <&stmfx_pinctrl>;
+                       interrupts = <1 IRQ_TYPE_EDGE_RISING>;
+               };
+               button-2 {
+                       label = "JoyLeft";
+                       linux,code = <KEY_LEFT>;
+                       interrupt-parent = <&stmfx_pinctrl>;
+                       interrupts = <2 IRQ_TYPE_EDGE_RISING>;
+               };
+               button-3 {
+                       label = "JoyRight";
+                       linux,code = <KEY_RIGHT>;
+                       interrupt-parent = <&stmfx_pinctrl>;
+                       interrupts = <3 IRQ_TYPE_EDGE_RISING>;
+               };
+               button-4 {
+                       label = "JoyUp";
+                       linux,code = <KEY_UP>;
+                       interrupt-parent = <&stmfx_pinctrl>;
+                       interrupts = <4 IRQ_TYPE_EDGE_RISING>;
+               };
+       };
+
+       leds {
+               compatible = "gpio-leds";
+               orange {
+                       gpios = <&stmfx_pinctrl 17 1>;
+               };
+
+               blue {
+                       gpios = <&stmfx_pinctrl 19 1>;
+               };
+       }
index 1ab64e75b9ae654acb4bb2afb54dac88a0ada2e8..1effe978902312b1738434ada432b6bcf0b6275d 100644 (file)
@@ -709,6 +709,12 @@ L: linux-gpio@vger.kernel.org
 S:     Maintained
 F:     drivers/gpio/gpio-altera.c
 
+ALTERA SYSTEM MANAGER DRIVER
+M:     Thor Thayer <thor.thayer@linux.intel.com>
+S:     Maintained
+F:     drivers/mfd/altera-sysmgr.c
+F:     include/linux/mfd/altera-sysgmr.h
+
 ALTERA SYSTEM RESOURCE DRIVER FOR ARRIA10 DEVKIT
 M:     Thor Thayer <thor.thayer@linux.intel.com>
 S:     Maintained
index e21ec929f096f2634fad94269632f435cc09d5e7..714863f8f261b3ac3ff55a100b5a39a010770233 100644 (file)
 
                width-mm = <50>;
                height-mm = <89>;
-               backlight = <&lcd_backlight>;
 
                panel-timing {
                        clock-frequency = <0>;          /* Calculated by dsi */
 };
 
 &i2c1 {
-       lm3532@38 {
+       led-controller@38 {
                compatible = "ti,lm3532";
+               #address-cells = <1>;
+               #size-cells = <0>;
                reg = <0x38>;
 
                enable-gpios = <&gpio6 12 GPIO_ACTIVE_HIGH>;
 
-               lcd_backlight: backlight {
-                       compatible = "ti,lm3532-backlight";
+               ramp-up-us = <1024>;
+               ramp-down-us = <8193>;
 
-                       lcd {
-                               led-sources = <0 1 2>;
-                               ramp-up-msec = <1>;
-                               ramp-down-msec = <0>;
-                       };
+               led@0 {
+                       reg = <0>;
+                       led-sources = <2>;
+                       ti,led-mode = <0>;
+                       label = ":backlight";
+                       linux,default-trigger = "backlight";
+               };
+
+               led@1 {
+                       reg = <1>;
+                       led-sources = <1>;
+                       ti,led-mode = <0>;
+                       label = ":kbd_backlight";
                };
        };
 };
index 08d1b3e11d68c81cd917eae56b9d226671888809..2d29992a60d24a5bf009b4802006febfab2a0e02 100644 (file)
@@ -106,6 +106,7 @@ CONFIG_SENSORS_LTC2978_REGULATOR=y
 CONFIG_WATCHDOG=y
 CONFIG_DW_WATCHDOG=y
 CONFIG_MFD_ALTERA_A10SR=y
+CONFIG_MFD_ALTERA_SYSMGR=y
 CONFIG_MFD_STMPE=y
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
index cd7c76e58b09a60f75ccd510083bab730a2378b0..30195843014952525b1de86ef85a821f4000284e 100644 (file)
                };
 
                sysmgr: sysmgr@ffd12000 {
-                       compatible = "altr,sys-mgr", "syscon";
+                       compatible = "altr,sys-mgr-s10","altr,sys-mgr";
                        reg = <0xffd12000 0x228>;
                };
 
index 2d9c39033c1aa91cff516457aed405d5de1dc2c2..9e0b7ab639b3dcd69e385726a60b57f9352a75d4 100644 (file)
@@ -432,6 +432,7 @@ CONFIG_MESON_WATCHDOG=m
 CONFIG_RENESAS_WDT=y
 CONFIG_UNIPHIER_WATCHDOG=y
 CONFIG_BCM2835_WDT=y
+CONFIG_MFD_ALTERA_SYSMGR=y
 CONFIG_MFD_BD9571MWV=y
 CONFIG_MFD_AXP20X_I2C=y
 CONFIG_MFD_AXP20X_RSB=y
index d8c70cc6a71441cb35fa361585d3fea8f60b3bca..45914c66c8520f3c4952f6d60addd68068df9655 100644 (file)
@@ -138,6 +138,16 @@ config LEDS_LM3530
          controlled manually or using PWM input or using ambient
          light automatically.
 
+config LEDS_LM3532
+       tristate "LCD Backlight driver for LM3532"
+       depends on LEDS_CLASS
+       depends on I2C
+       help
+         This option enables support for the LCD backlight using
+         LM3532 ambient light sensor chip. This ALS chip can be
+         controlled manually or using PWM input or using ambient
+         light automatically.
+
 config LEDS_LM3533
        tristate "LED support for LM3533"
        depends on LEDS_CLASS
index f48b2404dbb773cd36306085ddc901ea36e00ead..1e9702ebffeed65064c365df5ea7a7208af8d86e 100644 (file)
@@ -18,6 +18,7 @@ obj-$(CONFIG_LEDS_BD2802)             += leds-bd2802.o
 obj-$(CONFIG_LEDS_CPCAP)               += leds-cpcap.o
 obj-$(CONFIG_LEDS_LOCOMO)              += leds-locomo.o
 obj-$(CONFIG_LEDS_LM3530)              += leds-lm3530.o
+obj-$(CONFIG_LEDS_LM3532)              += leds-lm3532.o
 obj-$(CONFIG_LEDS_LM3533)              += leds-lm3533.o
 obj-$(CONFIG_LEDS_LM3642)              += leds-lm3642.o
 obj-$(CONFIG_LEDS_MIKROTIK_RB532)      += leds-rb532.o
diff --git a/drivers/leds/leds-lm3532.c b/drivers/leds/leds-lm3532.c
new file mode 100644 (file)
index 0000000..180895b
--- /dev/null
@@ -0,0 +1,683 @@
+// SPDX-License-Identifier: GPL-2.0
+// TI LM3532 LED driver
+// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
+
+#include <linux/i2c.h>
+#include <linux/leds.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/regulator/consumer.h>
+#include <linux/module.h>
+#include <uapi/linux/uleds.h>
+#include <linux/gpio/consumer.h>
+
+#define LM3532_NAME "lm3532-led"
+#define LM3532_BL_MODE_MANUAL  0x00
+#define LM3532_BL_MODE_ALS     0x01
+
+#define LM3532_REG_OUTPUT_CFG  0x10
+#define LM3532_REG_STARTSHUT_RAMP      0x11
+#define LM3532_REG_RT_RAMP     0x12
+#define LM3532_REG_PWM_A_CFG   0x13
+#define LM3532_REG_PWM_B_CFG   0x14
+#define LM3532_REG_PWM_C_CFG   0x15
+#define LM3532_REG_ZONE_CFG_A  0x16
+#define LM3532_REG_CTRL_A_BRT  0x17
+#define LM3532_REG_ZONE_CFG_B  0x18
+#define LM3532_REG_CTRL_B_BRT  0x19
+#define LM3532_REG_ZONE_CFG_C  0x1a
+#define LM3532_REG_CTRL_C_BRT  0x1b
+#define LM3532_REG_ENABLE      0x1d
+#define LM3532_ALS_CONFIG      0x23
+#define LM3532_REG_ZN_0_HI     0x60
+#define LM3532_REG_ZN_0_LO     0x61
+#define LM3532_REG_ZN_1_HI     0x62
+#define LM3532_REG_ZN_1_LO     0x63
+#define LM3532_REG_ZN_2_HI     0x64
+#define LM3532_REG_ZN_2_LO     0x65
+#define LM3532_REG_ZN_3_HI     0x66
+#define LM3532_REG_ZN_3_LO     0x67
+#define LM3532_REG_MAX         0x7e
+
+/* Contorl Enable */
+#define LM3532_CTRL_A_ENABLE   BIT(0)
+#define LM3532_CTRL_B_ENABLE   BIT(1)
+#define LM3532_CTRL_C_ENABLE   BIT(2)
+
+/* PWM Zone Control */
+#define LM3532_PWM_ZONE_MASK   0x7c
+#define LM3532_PWM_ZONE_0_EN   BIT(2)
+#define LM3532_PWM_ZONE_1_EN   BIT(3)
+#define LM3532_PWM_ZONE_2_EN   BIT(4)
+#define LM3532_PWM_ZONE_3_EN   BIT(5)
+#define LM3532_PWM_ZONE_4_EN   BIT(6)
+
+/* Brightness Configuration */
+#define LM3532_I2C_CTRL                BIT(0)
+#define LM3532_ALS_CTRL                0
+#define LM3532_LINEAR_MAP      BIT(1)
+#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_ZONE_3          (BIT(2) | BIT(3))
+#define LM3532_ZONE_4          BIT(4)
+
+#define LM3532_ENABLE_ALS      BIT(3)
+#define LM3532_ALS_SEL_SHIFT   6
+
+/* Zone Boundary Register */
+#define LM3532_ALS_WINDOW_mV   2000
+#define LM3532_ALS_ZB_MAX      4
+#define LM3532_ALS_OFFSET_mV   2
+
+#define LM3532_CONTROL_A       0
+#define LM3532_CONTROL_B       1
+#define LM3532_CONTROL_C       2
+#define LM3532_MAX_CONTROL_BANKS 3
+#define LM3532_MAX_LED_STRINGS 3
+
+#define LM3532_OUTPUT_CFG_MASK 0x3
+#define LM3532_BRT_VAL_ADJUST  8
+#define LM3532_RAMP_DOWN_SHIFT 3
+
+#define LM3532_NUM_RAMP_VALS   8
+#define LM3532_NUM_AVG_VALS    8
+#define LM3532_NUM_IMP_VALS    32
+
+/*
+ * struct lm3532_als_data
+ * @config - value of ALS configuration register
+ * @als1_imp_sel - value of ALS1 resistor select register
+ * @als2_imp_sel - value of ALS2 resistor select register
+ * @als_avrg_time - ALS averaging time
+ * @als_input_mode - ALS input mode for brightness control
+ * @als_vmin - Minimum ALS voltage
+ * @als_vmax - Maximum ALS voltage
+ * @zone_lo - values of ALS lo ZB(Zone Boundary) registers
+ * @zone_hi - values of ALS hi ZB(Zone Boundary) registers
+ */
+struct lm3532_als_data {
+       u8 config;
+       u8 als1_imp_sel;
+       u8 als2_imp_sel;
+       u8 als_avrg_time;
+       u8 als_input_mode;
+       u32 als_vmin;
+       u32 als_vmax;
+       u8 zones_lo[LM3532_ALS_ZB_MAX];
+       u8 zones_hi[LM3532_ALS_ZB_MAX];
+};
+
+/**
+ * struct lm3532_led
+ * @led_dev: led class device
+ * @priv - Pointer the device data structure
+ * @control_bank - Control bank the LED is associated to
+ * @mode - Mode of the LED string
+ * @num_leds - Number of LED strings are supported in this array
+ * @led_strings - The LED strings supported in this array
+ * @label - LED label
+ */
+struct lm3532_led {
+       struct led_classdev led_dev;
+       struct lm3532_data *priv;
+
+       int control_bank;
+       int mode;
+       int num_leds;
+       u32 led_strings[LM3532_MAX_CONTROL_BANKS];
+       char label[LED_MAX_NAME_SIZE];
+};
+
+/**
+ * struct lm3532_data
+ * @enable_gpio - Hardware enable gpio
+ * @regulator: regulator
+ * @client: i2c client
+ * @regmap - Devices register map
+ * @dev - Pointer to the devices device struct
+ * @lock - Lock for reading/writing the device
+ * @als_data - Pointer to the als data struct
+ * @runtime_ramp_up - Runtime ramp up setting
+ * @runtime_ramp_down - Runtime ramp down setting
+ * @leds - Array of LED strings
+ */
+struct lm3532_data {
+       struct gpio_desc *enable_gpio;
+       struct regulator *regulator;
+       struct i2c_client *client;
+       struct regmap *regmap;
+       struct device *dev;
+       struct mutex lock;
+
+       struct lm3532_als_data *als_data;
+
+       u32 runtime_ramp_up;
+       u32 runtime_ramp_down;
+
+       struct lm3532_led leds[];
+};
+
+static const struct reg_default lm3532_reg_defs[] = {
+       {LM3532_REG_OUTPUT_CFG, 0xe4},
+       {LM3532_REG_STARTSHUT_RAMP, 0xc0},
+       {LM3532_REG_RT_RAMP, 0xc0},
+       {LM3532_REG_PWM_A_CFG, 0x82},
+       {LM3532_REG_PWM_B_CFG, 0x82},
+       {LM3532_REG_PWM_C_CFG, 0x82},
+       {LM3532_REG_ZONE_CFG_A, 0xf1},
+       {LM3532_REG_CTRL_A_BRT, 0xf3},
+       {LM3532_REG_ZONE_CFG_B, 0xf1},
+       {LM3532_REG_CTRL_B_BRT, 0xf3},
+       {LM3532_REG_ZONE_CFG_C, 0xf1},
+       {LM3532_REG_CTRL_C_BRT, 0xf3},
+       {LM3532_REG_ENABLE, 0xf8},
+       {LM3532_ALS_CONFIG, 0x44},
+       {LM3532_REG_ZN_0_HI, 0x35},
+       {LM3532_REG_ZN_0_LO, 0x33},
+       {LM3532_REG_ZN_1_HI, 0x6a},
+       {LM3532_REG_ZN_1_LO, 0x66},
+       {LM3532_REG_ZN_2_HI, 0xa1},
+       {LM3532_REG_ZN_2_LO, 0x99},
+       {LM3532_REG_ZN_3_HI, 0xdc},
+       {LM3532_REG_ZN_3_LO, 0xcc},
+};
+
+static const struct regmap_config lm3532_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .max_register = LM3532_REG_MAX,
+       .reg_defaults = lm3532_reg_defs,
+       .num_reg_defaults = ARRAY_SIZE(lm3532_reg_defs),
+       .cache_type = REGCACHE_FLAT,
+};
+
+const static int als_imp_table[LM3532_NUM_IMP_VALS] = {37000, 18500, 12330,
+                                                      92500, 7400, 6170, 5290,
+                                                      4630, 4110, 3700, 3360,
+                                                      3080, 2850, 2640, 2440,
+                                                      2310, 2180, 2060, 1950,
+                                                      1850, 1760, 1680, 1610,
+                                                      1540, 1480, 1420, 1370,
+                                                      1320, 1280, 1230, 1190};
+static int lm3532_get_als_imp_index(int als_imped)
+{
+       int i;
+
+       if (als_imped > als_imp_table[1])
+               return 0;
+
+       if (als_imped < als_imp_table[LM3532_NUM_IMP_VALS - 1])
+               return LM3532_NUM_IMP_VALS - 1;
+
+       for (i = 1; i < LM3532_NUM_IMP_VALS; i++) {
+               if (als_imped == als_imp_table[i])
+                       return i;
+
+               /* Find an approximate index by looking up the table */
+               if (als_imped < als_imp_table[i - 1] &&
+                   als_imped > als_imp_table[i]) {
+                       if (als_imped - als_imp_table[i - 1] <
+                           als_imp_table[i] - als_imped)
+                               return i + 1;
+                       else
+                               return i;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static int lm3532_get_index(const int table[], int size, int value)
+{
+       int i;
+
+       for (i = 1; i < size; i++) {
+               if (value == table[i])
+                       return i;
+
+               /* Find an approximate index by looking up the table */
+               if (value > table[i - 1] &&
+                   value < table[i]) {
+                       if (value - table[i - 1] < table[i] - value)
+                               return i - 1;
+                       else
+                               return i;
+               }
+       }
+
+       return -EINVAL;
+}
+
+const static int als_avrg_table[LM3532_NUM_AVG_VALS] = {17920, 35840, 71680,
+                                                       1433360, 286720, 573440,
+                                                       1146880, 2293760};
+static int lm3532_get_als_avg_index(int avg_time)
+{
+       if (avg_time <= als_avrg_table[0])
+               return 0;
+
+       if (avg_time > als_avrg_table[LM3532_NUM_AVG_VALS - 1])
+               return LM3532_NUM_AVG_VALS - 1;
+
+       return lm3532_get_index(&als_avrg_table[0], LM3532_NUM_AVG_VALS,
+                               avg_time);
+}
+
+const static int ramp_table[LM3532_NUM_RAMP_VALS] = { 8, 1024, 2048, 4096, 8192,
+                                                    16384, 32768, 65536};
+static int lm3532_get_ramp_index(int ramp_time)
+{
+       if (ramp_time <= ramp_table[0])
+               return 0;
+
+       if (ramp_time > ramp_table[LM3532_NUM_RAMP_VALS - 1])
+               return LM3532_NUM_RAMP_VALS - 1;
+
+       return lm3532_get_index(&ramp_table[0], LM3532_NUM_RAMP_VALS,
+                               ramp_time);
+}
+
+static int lm3532_led_enable(struct lm3532_led *led_data)
+{
+       int ctrl_en_val = BIT(led_data->control_bank);
+       int ret;
+
+       ret = regmap_update_bits(led_data->priv->regmap, LM3532_REG_ENABLE,
+                                        ctrl_en_val, ctrl_en_val);
+       if (ret) {
+               dev_err(led_data->priv->dev, "Failed to set ctrl:%d\n", ret);
+               return ret;
+       }
+
+       return regulator_enable(led_data->priv->regulator);
+}
+
+static int lm3532_led_disable(struct lm3532_led *led_data)
+{
+       int ctrl_en_val = BIT(led_data->control_bank);
+       int ret;
+
+       ret = regmap_update_bits(led_data->priv->regmap, LM3532_REG_ENABLE,
+                                        ctrl_en_val, ~ctrl_en_val);
+       if (ret) {
+               dev_err(led_data->priv->dev, "Failed to set ctrl:%d\n", ret);
+               return ret;
+       }
+
+       return regulator_disable(led_data->priv->regulator);
+}
+
+static int lm3532_brightness_set(struct led_classdev *led_cdev,
+                                enum led_brightness brt_val)
+{
+       struct lm3532_led *led =
+                       container_of(led_cdev, struct lm3532_led, led_dev);
+       u8 brightness_reg;
+       int ret;
+
+       mutex_lock(&led->priv->lock);
+
+       if (led->mode == LM3532_BL_MODE_ALS) {
+               if (brt_val > LED_OFF)
+                       ret = lm3532_led_enable(led);
+               else
+                       ret = lm3532_led_disable(led);
+
+               goto unlock;
+       }
+
+       if (brt_val == LED_OFF) {
+               ret = lm3532_led_disable(led);
+               goto unlock;
+       }
+
+       ret = lm3532_led_enable(led);
+       if (ret)
+               goto unlock;
+
+       brightness_reg = LM3532_REG_CTRL_A_BRT + led->control_bank * 2;
+       brt_val = brt_val / LM3532_BRT_VAL_ADJUST;
+
+       ret = regmap_write(led->priv->regmap, brightness_reg, brt_val);
+
+unlock:
+       mutex_unlock(&led->priv->lock);
+       return ret;
+}
+
+static int lm3532_init_registers(struct lm3532_led *led)
+{
+       struct lm3532_data *drvdata = led->priv;
+       unsigned int runtime_ramp_val;
+       unsigned int output_cfg_val = 0;
+       unsigned int output_cfg_shift = 0;
+       unsigned int output_cfg_mask = 0;
+       int ret, i;
+
+       for (i = 0; i < led->num_leds; i++) {
+               output_cfg_shift = led->led_strings[i] * 2;
+               output_cfg_val |= (led->control_bank << output_cfg_shift);
+               output_cfg_mask |= LM3532_OUTPUT_CFG_MASK << output_cfg_shift;
+       }
+
+       ret = regmap_update_bits(drvdata->regmap, LM3532_REG_OUTPUT_CFG,
+                                output_cfg_mask, output_cfg_val);
+       if (ret)
+               return ret;
+
+       runtime_ramp_val = drvdata->runtime_ramp_up |
+                        (drvdata->runtime_ramp_down << LM3532_RAMP_DOWN_SHIFT);
+
+       return regmap_write(drvdata->regmap, LM3532_REG_RT_RAMP,
+                           runtime_ramp_val);
+}
+
+static int lm3532_als_configure(struct lm3532_data *priv,
+                               struct lm3532_led *led)
+{
+       struct lm3532_als_data *als = priv->als_data;
+       u32 als_vmin, als_vmax, als_vstep;
+       int zone_reg = LM3532_REG_ZN_0_HI;
+       int brightnes_config_reg;
+       int ret;
+       int i;
+
+       als_vmin = als->als_vmin;
+       als_vmax = als->als_vmax;
+
+       als_vstep = (als_vmax - als_vmin) / ((LM3532_ALS_ZB_MAX + 1) * 2);
+
+       for (i = 0; i < LM3532_ALS_ZB_MAX; i++) {
+               als->zones_lo[i] = ((als_vmin + als_vstep + (i * als_vstep)) *
+                               LED_FULL) / 1000;
+               als->zones_hi[i] = ((als_vmin + LM3532_ALS_OFFSET_mV +
+                               als_vstep + (i * als_vstep)) * LED_FULL) / 1000;
+
+               zone_reg = LM3532_REG_ZN_0_HI + i * 2;
+               ret = regmap_write(priv->regmap, zone_reg, als->zones_lo[i]);
+               if (ret)
+                       return ret;
+
+               zone_reg += 1;
+               ret = regmap_write(priv->regmap, zone_reg, als->zones_hi[i]);
+               if (ret)
+                       return ret;
+       }
+
+       als->config = (als->als_avrg_time | (LM3532_ENABLE_ALS) |
+               (als->als_input_mode << LM3532_ALS_SEL_SHIFT));
+
+       ret = regmap_write(priv->regmap, LM3532_ALS_CONFIG, als->config);
+       if (ret)
+               return ret;
+
+       brightnes_config_reg = LM3532_REG_ZONE_CFG_A + led->control_bank * 2;
+
+       return regmap_update_bits(priv->regmap, brightnes_config_reg,
+                                 LM3532_I2C_CTRL, LM3532_ALS_CTRL);
+}
+
+static int lm3532_parse_als(struct lm3532_data *priv)
+{
+       struct lm3532_als_data *als;
+       int als_avg_time;
+       int als_impedance;
+       int ret;
+
+       als = devm_kzalloc(priv->dev, sizeof(*als), GFP_KERNEL);
+       if (als == NULL)
+               return -ENOMEM;
+
+       ret = device_property_read_u32(&priv->client->dev, "ti,als-vmin",
+                                      &als->als_vmin);
+       if (ret)
+               als->als_vmin = 0;
+
+       ret = device_property_read_u32(&priv->client->dev, "ti,als-vmax",
+                                      &als->als_vmax);
+       if (ret)
+               als->als_vmax = LM3532_ALS_WINDOW_mV;
+
+       if (als->als_vmax > LM3532_ALS_WINDOW_mV) {
+               ret = -EINVAL;
+               return ret;
+       }
+
+       ret = device_property_read_u32(&priv->client->dev, "ti,als1-imp-sel",
+                                     &als_impedance);
+       if (ret)
+               als->als1_imp_sel = 0;
+       else
+               als->als1_imp_sel = lm3532_get_als_imp_index(als_impedance);
+
+       ret = device_property_read_u32(&priv->client->dev, "ti,als2-imp-sel",
+                                     &als_impedance);
+       if (ret)
+               als->als2_imp_sel = 0;
+       else
+               als->als2_imp_sel = lm3532_get_als_imp_index(als_impedance);
+
+       ret = device_property_read_u32(&priv->client->dev, "ti,als-avrg-time-us",
+                                     &als_avg_time);
+       if (ret)
+               als->als_avrg_time = 0;
+       else
+               als->als_avrg_time = lm3532_get_als_avg_index(als_avg_time);
+
+       ret = device_property_read_u8(&priv->client->dev, "ti,als-input-mode",
+                                     &als->als_input_mode);
+       if (ret)
+               als->als_input_mode = 0;
+
+       if (als->als_input_mode > LM3532_BL_MODE_ALS) {
+               ret = -EINVAL;
+               return ret;
+       }
+
+       priv->als_data = als;
+
+       return ret;
+}
+
+static int lm3532_parse_node(struct lm3532_data *priv)
+{
+       struct fwnode_handle *child = NULL;
+       struct lm3532_led *led;
+       const char *name;
+       int control_bank;
+       u32 ramp_time;
+       size_t i = 0;
+       int ret;
+
+       priv->enable_gpio = devm_gpiod_get_optional(&priv->client->dev,
+                                                  "enable", GPIOD_OUT_LOW);
+       if (IS_ERR(priv->enable_gpio))
+               priv->enable_gpio = NULL;
+
+       priv->regulator = devm_regulator_get(&priv->client->dev, "vin");
+       if (IS_ERR(priv->regulator))
+               priv->regulator = NULL;
+
+       ret = device_property_read_u32(&priv->client->dev, "ramp-up-us",
+                                      &ramp_time);
+       if (ret)
+               dev_info(&priv->client->dev, "ramp-up-ms property missing\n");
+       else
+               priv->runtime_ramp_up = lm3532_get_ramp_index(ramp_time);
+
+       ret = device_property_read_u32(&priv->client->dev, "ramp-down-us",
+                                      &ramp_time);
+       if (ret)
+               dev_info(&priv->client->dev, "ramp-down-ms property missing\n");
+       else
+               priv->runtime_ramp_down = lm3532_get_ramp_index(ramp_time);
+
+       device_for_each_child_node(priv->dev, child) {
+               led = &priv->leds[i];
+
+               ret = fwnode_property_read_u32(child, "reg", &control_bank);
+               if (ret) {
+                       dev_err(&priv->client->dev, "reg property missing\n");
+                       fwnode_handle_put(child);
+                       goto child_out;
+               }
+
+               if (control_bank > LM3532_CONTROL_C) {
+                       dev_err(&priv->client->dev, "Control bank invalid\n");
+                       continue;
+               }
+
+               led->control_bank = control_bank;
+
+               ret = fwnode_property_read_u32(child, "ti,led-mode",
+                                              &led->mode);
+               if (ret) {
+                       dev_err(&priv->client->dev, "ti,led-mode property missing\n");
+                       fwnode_handle_put(child);
+                       goto child_out;
+               }
+
+               if (led->mode == LM3532_BL_MODE_ALS) {
+                       ret = lm3532_parse_als(priv);
+                       if (ret)
+                               dev_err(&priv->client->dev, "Failed to parse als\n");
+                       else
+                               lm3532_als_configure(priv, led);
+               }
+
+               led->num_leds = fwnode_property_read_u32_array(child,
+                                                              "led-sources",
+                                                              NULL, 0);
+
+               if (led->num_leds > LM3532_MAX_LED_STRINGS) {
+                       dev_err(&priv->client->dev, "To many LED string defined\n");
+                       continue;
+               }
+
+               ret = fwnode_property_read_u32_array(child, "led-sources",
+                                                   led->led_strings,
+                                                   led->num_leds);
+               if (ret) {
+                       dev_err(&priv->client->dev, "led-sources property missing\n");
+                       fwnode_handle_put(child);
+                       goto child_out;
+               }
+
+               fwnode_property_read_string(child, "linux,default-trigger",
+                                           &led->led_dev.default_trigger);
+
+               ret = fwnode_property_read_string(child, "label", &name);
+               if (ret)
+                       snprintf(led->label, sizeof(led->label),
+                               "%s::", priv->client->name);
+               else
+                       snprintf(led->label, sizeof(led->label),
+                                "%s:%s", priv->client->name, name);
+
+               led->priv = priv;
+               led->led_dev.name = led->label;
+               led->led_dev.brightness_set_blocking = lm3532_brightness_set;
+
+               ret = devm_led_classdev_register(priv->dev, &led->led_dev);
+               if (ret) {
+                       dev_err(&priv->client->dev, "led register err: %d\n",
+                               ret);
+                       fwnode_handle_put(child);
+                       goto child_out;
+               }
+
+               lm3532_init_registers(led);
+
+               i++;
+       }
+
+child_out:
+       return ret;
+}
+
+static int lm3532_probe(struct i2c_client *client,
+                          const struct i2c_device_id *id)
+{
+       struct lm3532_data *drvdata;
+       int ret = 0;
+       int count;
+
+       count = device_get_child_node_count(&client->dev);
+       if (!count) {
+               dev_err(&client->dev, "LEDs are not defined in device tree!");
+               return -ENODEV;
+       }
+
+       drvdata = devm_kzalloc(&client->dev, struct_size(drvdata, leds, count),
+                          GFP_KERNEL);
+       if (drvdata == NULL)
+               return -ENOMEM;
+
+       drvdata->client = client;
+       drvdata->dev = &client->dev;
+
+       drvdata->regmap = devm_regmap_init_i2c(client, &lm3532_regmap_config);
+       if (IS_ERR(drvdata->regmap)) {
+               ret = PTR_ERR(drvdata->regmap);
+               dev_err(&client->dev, "Failed to allocate register map: %d\n",
+                       ret);
+               return ret;
+       }
+
+       mutex_init(&drvdata->lock);
+       i2c_set_clientdata(client, drvdata);
+
+       ret = lm3532_parse_node(drvdata);
+       if (ret) {
+               dev_err(&client->dev, "Failed to parse node\n");
+               return ret;
+       }
+
+       if (drvdata->enable_gpio)
+               gpiod_direction_output(drvdata->enable_gpio, 1);
+
+       return ret;
+}
+
+static int lm3532_remove(struct i2c_client *client)
+{
+       struct lm3532_data *drvdata = i2c_get_clientdata(client);
+
+       mutex_destroy(&drvdata->lock);
+
+       if (drvdata->enable_gpio)
+               gpiod_direction_output(drvdata->enable_gpio, 0);
+
+       return 0;
+}
+
+static const struct of_device_id of_lm3532_leds_match[] = {
+       { .compatible = "ti,lm3532", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, of_lm3532_leds_match);
+
+static const struct i2c_device_id lm3532_id[] = {
+       {LM3532_NAME, 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, lm3532_id);
+
+static struct i2c_driver lm3532_i2c_driver = {
+       .probe = lm3532_probe,
+       .remove = lm3532_remove,
+       .id_table = lm3532_id,
+       .driver = {
+               .name = LM3532_NAME,
+               .of_match_table = of_lm3532_leds_match,
+       },
+};
+module_i2c_driver(lm3532_i2c_driver);
+
+MODULE_DESCRIPTION("Back Light driver for LM3532");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
index 0f394f08db6f14b9823735649d009bec474f344e..9d53480352a67cc55ede07819f86b644e832b0bd 100644 (file)
@@ -29,6 +29,16 @@ config MFD_ALTERA_A10SR
          accessing the external gpio extender (LEDs & buttons) and
          power supply alarms (hwmon).
 
+config MFD_ALTERA_SYSMGR
+       bool "Altera SOCFPGA System Manager"
+       depends on (ARCH_SOCFPGA || ARCH_STRATIX10) && OF
+       select MFD_SYSCON
+       help
+         Select this to get System Manager support for all Altera branded
+         SOCFPGAs. The SOCFPGA System Manager handles all SOCFPGAs by
+         using regmap_mmio accesses for ARM32 parts and SMC calls to
+         EL3 for ARM64 parts.
+
 config MFD_ACT8945A
        tristate "Active-semi ACT8945A"
        select MFD_CORE
@@ -1921,6 +1931,19 @@ config MFD_STPMIC1
          To compile this driver as a module, choose M here: the
          module will be called stpmic1.
 
+config MFD_STMFX
+       tristate "Support for STMicroelectronics Multi-Function eXpander (STMFX)"
+       depends on I2C
+       depends on OF || COMPILE_TEST
+       select MFD_CORE
+       select REGMAP_I2C
+       help
+         Support for the STMicroelectronics Multi-Function eXpander.
+
+         This driver provides common support for accessing the device,
+         additional drivers must be enabled in order to use the functionality
+         of the device.
+
 menu "Multimedia Capabilities Port drivers"
        depends on ARCH_SA1100
 
index 5727d099c16fef190026625a19d50c6091ab9fad..52b1a90ff5159308c984b8ea319d2ba848755b5f 100644 (file)
@@ -238,6 +238,7 @@ obj-$(CONFIG_INTEL_SOC_PMIC_CHTDC_TI)       += intel_soc_pmic_chtdc_ti.o
 obj-$(CONFIG_MFD_MT6397)       += mt6397-core.o
 
 obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o
+obj-$(CONFIG_MFD_ALTERA_SYSMGR) += altera-sysmgr.o
 obj-$(CONFIG_MFD_STPMIC1)      += stpmic1.o
 obj-$(CONFIG_MFD_SUN4I_GPADC)  += sun4i-gpadc.o
 
@@ -247,4 +248,4 @@ obj-$(CONFIG_MFD_MXS_LRADC)     += mxs-lradc.o
 obj-$(CONFIG_MFD_SC27XX_PMIC)  += sprd-sc27xx-spi.o
 obj-$(CONFIG_RAVE_SP_CORE)     += rave-sp.o
 obj-$(CONFIG_MFD_ROHM_BD718XX) += rohm-bd718x7.o
-
+obj-$(CONFIG_MFD_STMFX)        += stmfx.o
diff --git a/drivers/mfd/altera-sysmgr.c b/drivers/mfd/altera-sysmgr.c
new file mode 100644 (file)
index 0000000..8976f82
--- /dev/null
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Copyright (C) 2018-2019, Intel Corporation.
+ *  Copyright (C) 2012 Freescale Semiconductor, Inc.
+ *  Copyright (C) 2012 Linaro Ltd.
+ *
+ *  Based on syscon driver.
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/mfd/altera-sysmgr.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+/**
+ * struct altr_sysmgr - Altera SOCFPGA System Manager
+ * @regmap: the regmap used for System Manager accesses.
+ * @base  : the base address for the System Manager
+ */
+struct altr_sysmgr {
+       struct regmap   *regmap;
+       resource_size_t *base;
+};
+
+static struct platform_driver altr_sysmgr_driver;
+
+/**
+ * s10_protected_reg_write
+ * Write to a protected SMC register.
+ * @base: Base address of System Manager
+ * @reg:  Address offset of register
+ * @val:  Value to write
+ * Return: INTEL_SIP_SMC_STATUS_OK (0) on success
+ *        INTEL_SIP_SMC_REG_ERROR on error
+ *        INTEL_SIP_SMC_RETURN_UNKNOWN_FUNCTION if not supported
+ */
+static int s10_protected_reg_write(void *base,
+                                  unsigned int reg, unsigned int val)
+{
+       struct arm_smccc_res result;
+       unsigned long sysmgr_base = (unsigned long)base;
+
+       arm_smccc_smc(INTEL_SIP_SMC_REG_WRITE, sysmgr_base + reg,
+                     val, 0, 0, 0, 0, 0, &result);
+
+       return (int)result.a0;
+}
+
+/**
+ * s10_protected_reg_read
+ * Read the status of a protected SMC register
+ * @base: Base address of System Manager.
+ * @reg:  Address of register
+ * @val:  Value read.
+ * Return: INTEL_SIP_SMC_STATUS_OK (0) on success
+ *        INTEL_SIP_SMC_REG_ERROR on error
+ *        INTEL_SIP_SMC_RETURN_UNKNOWN_FUNCTION if not supported
+ */
+static int s10_protected_reg_read(void *base,
+                                 unsigned int reg, unsigned int *val)
+{
+       struct arm_smccc_res result;
+       unsigned long sysmgr_base = (unsigned long)base;
+
+       arm_smccc_smc(INTEL_SIP_SMC_REG_READ, sysmgr_base + reg,
+                     0, 0, 0, 0, 0, 0, &result);
+
+       *val = (unsigned int)result.a1;
+
+       return (int)result.a0;
+}
+
+static struct regmap_config altr_sysmgr_regmap_cfg = {
+       .name = "altr_sysmgr",
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+       .fast_io = true,
+       .use_single_read = true,
+       .use_single_write = true,
+};
+
+/**
+ * sysmgr_match_phandle
+ * Matching function used by driver_find_device().
+ * Return: True if match is found, otherwise false.
+ */
+static int sysmgr_match_phandle(struct device *dev, void *data)
+{
+       return dev->of_node == (struct device_node *)data;
+}
+
+/**
+ * altr_sysmgr_regmap_lookup_by_phandle
+ * Find the sysmgr previous configured in probe() and return regmap property.
+ * Return: regmap if found or error if not found.
+ */
+struct regmap *altr_sysmgr_regmap_lookup_by_phandle(struct device_node *np,
+                                                   const char *property)
+{
+       struct device *dev;
+       struct altr_sysmgr *sysmgr;
+       struct device_node *sysmgr_np;
+
+       if (property)
+               sysmgr_np = of_parse_phandle(np, property, 0);
+       else
+               sysmgr_np = np;
+
+       if (!sysmgr_np)
+               return ERR_PTR(-ENODEV);
+
+       dev = driver_find_device(&altr_sysmgr_driver.driver, NULL,
+                                (void *)sysmgr_np, sysmgr_match_phandle);
+       of_node_put(sysmgr_np);
+       if (!dev)
+               return ERR_PTR(-EPROBE_DEFER);
+
+       sysmgr = dev_get_drvdata(dev);
+
+       return sysmgr->regmap;
+}
+EXPORT_SYMBOL_GPL(altr_sysmgr_regmap_lookup_by_phandle);
+
+static int sysmgr_probe(struct platform_device *pdev)
+{
+       struct altr_sysmgr *sysmgr;
+       struct regmap *regmap;
+       struct resource *res;
+       struct regmap_config sysmgr_config = altr_sysmgr_regmap_cfg;
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+
+       sysmgr = devm_kzalloc(dev, sizeof(*sysmgr), GFP_KERNEL);
+       if (!sysmgr)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -ENOENT;
+
+       sysmgr_config.max_register = resource_size(res) -
+                                    sysmgr_config.reg_stride;
+       if (of_device_is_compatible(np, "altr,sys-mgr-s10")) {
+               /* Need physical address for SMCC call */
+               sysmgr->base = (resource_size_t *)res->start;
+               sysmgr_config.reg_read = s10_protected_reg_read;
+               sysmgr_config.reg_write = s10_protected_reg_write;
+
+               regmap = devm_regmap_init(dev, NULL, sysmgr->base,
+                                         &sysmgr_config);
+       } else {
+               sysmgr->base = devm_ioremap(dev, res->start,
+                                           resource_size(res));
+               if (!sysmgr->base)
+                       return -ENOMEM;
+
+               sysmgr_config.max_register = res->end - res->start - 3;
+               regmap = devm_regmap_init_mmio(dev, sysmgr->base,
+                                              &sysmgr_config);
+       }
+
+       if (IS_ERR(regmap)) {
+               pr_err("regmap init failed\n");
+               return PTR_ERR(regmap);
+       }
+
+       sysmgr->regmap = regmap;
+
+       platform_set_drvdata(pdev, sysmgr);
+
+       return 0;
+}
+
+static const struct of_device_id altr_sysmgr_of_match[] = {
+       { .compatible = "altr,sys-mgr" },
+       { .compatible = "altr,sys-mgr-s10" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, altr_sysmgr_of_match);
+
+static struct platform_driver altr_sysmgr_driver = {
+       .probe =  sysmgr_probe,
+       .driver = {
+               .name = "altr,system_manager",
+               .of_match_table = altr_sysmgr_of_match,
+       },
+};
+
+static int __init altr_sysmgr_init(void)
+{
+       return platform_driver_register(&altr_sysmgr_driver);
+}
+core_initcall(altr_sysmgr_init);
+
+static void __exit altr_sysmgr_exit(void)
+{
+       platform_driver_unregister(&altr_sysmgr_driver);
+}
+module_exit(altr_sysmgr_exit);
+
+MODULE_AUTHOR("Thor Thayer <>");
+MODULE_DESCRIPTION("SOCFPGA System Manager driver");
+MODULE_LICENSE("GPL v2");
index d8ddd1a6f30497ff7a866b40fe21f25ad45badea..436361ce373758bb6a41d0ef56662e9824d53d9a 100644 (file)
@@ -37,6 +37,8 @@
 #include <linux/regmap.h>
 #include <linux/slab.h>
 
+static struct max77620_chip *max77620_scratch;
+
 static const struct resource gpio_resources[] = {
        DEFINE_RES_IRQ(MAX77620_IRQ_TOP_GPIO),
 };
@@ -111,6 +113,26 @@ static const struct mfd_cell max20024_children[] = {
        },
 };
 
+static const struct mfd_cell max77663_children[] = {
+       { .name = "max77620-pinctrl", },
+       { .name = "max77620-clock", },
+       { .name = "max77663-pmic", },
+       { .name = "max77620-watchdog", },
+       {
+               .name = "max77620-gpio",
+               .resources = gpio_resources,
+               .num_resources = ARRAY_SIZE(gpio_resources),
+       }, {
+               .name = "max77620-rtc",
+               .resources = rtc_resources,
+               .num_resources = ARRAY_SIZE(rtc_resources),
+       }, {
+               .name = "max77663-power",
+               .resources = power_resources,
+               .num_resources = ARRAY_SIZE(power_resources),
+       },
+};
+
 static const struct regmap_range max77620_readable_ranges[] = {
        regmap_reg_range(MAX77620_REG_CNFGGLBL1, MAX77620_REG_DVSSD4),
 };
@@ -171,6 +193,35 @@ static const struct regmap_config max20024_regmap_config = {
        .volatile_table = &max77620_volatile_table,
 };
 
+static const struct regmap_range max77663_readable_ranges[] = {
+       regmap_reg_range(MAX77620_REG_CNFGGLBL1, MAX77620_REG_CID5),
+};
+
+static const struct regmap_access_table max77663_readable_table = {
+       .yes_ranges = max77663_readable_ranges,
+       .n_yes_ranges = ARRAY_SIZE(max77663_readable_ranges),
+};
+
+static const struct regmap_range max77663_writable_ranges[] = {
+       regmap_reg_range(MAX77620_REG_CNFGGLBL1, MAX77620_REG_CID5),
+};
+
+static const struct regmap_access_table max77663_writable_table = {
+       .yes_ranges = max77663_writable_ranges,
+       .n_yes_ranges = ARRAY_SIZE(max77663_writable_ranges),
+};
+
+static const struct regmap_config max77663_regmap_config = {
+       .name = "power-slave",
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = MAX77620_REG_CID5 + 1,
+       .cache_type = REGCACHE_RBTREE,
+       .rd_table = &max77663_readable_table,
+       .wr_table = &max77663_writable_table,
+       .volatile_table = &max77620_volatile_table,
+};
+
 /*
  * MAX77620 and MAX20024 has the following steps of the interrupt handling
  * for TOP interrupts:
@@ -240,6 +291,9 @@ static int max77620_get_fps_period_reg_value(struct max77620_chip *chip,
        case MAX77620:
                fps_min_period = MAX77620_FPS_PERIOD_MIN_US;
                break;
+       case MAX77663:
+               fps_min_period = MAX20024_FPS_PERIOD_MIN_US;
+               break;
        default:
                return -EINVAL;
        }
@@ -274,6 +328,9 @@ static int max77620_config_fps(struct max77620_chip *chip,
        case MAX77620:
                fps_max_period = MAX77620_FPS_PERIOD_MAX_US;
                break;
+       case MAX77663:
+               fps_max_period = MAX20024_FPS_PERIOD_MAX_US;
+               break;
        default:
                return -EINVAL;
        }
@@ -375,6 +432,9 @@ static int max77620_initialise_fps(struct max77620_chip *chip)
        }
 
 skip_fps:
+       if (chip->chip_id == MAX77663)
+               return 0;
+
        /* Enable wake on EN0 pin */
        ret = regmap_update_bits(chip->rmap, MAX77620_REG_ONOFFCNFG2,
                                 MAX77620_ONOFFCNFG2_WK_EN0,
@@ -423,6 +483,15 @@ static int max77620_read_es_version(struct max77620_chip *chip)
        return ret;
 }
 
+static void max77620_pm_power_off(void)
+{
+       struct max77620_chip *chip = max77620_scratch;
+
+       regmap_update_bits(chip->rmap, MAX77620_REG_ONOFFCNFG1,
+                          MAX77620_ONOFFCNFG1_SFT_RST,
+                          MAX77620_ONOFFCNFG1_SFT_RST);
+}
+
 static int max77620_probe(struct i2c_client *client,
                          const struct i2c_device_id *id)
 {
@@ -430,6 +499,7 @@ static int max77620_probe(struct i2c_client *client,
        struct max77620_chip *chip;
        const struct mfd_cell *mfd_cells;
        int n_mfd_cells;
+       bool pm_off;
        int ret;
 
        chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
@@ -453,6 +523,11 @@ static int max77620_probe(struct i2c_client *client,
                n_mfd_cells = ARRAY_SIZE(max20024_children);
                rmap_config = &max20024_regmap_config;
                break;
+       case MAX77663:
+               mfd_cells = max77663_children;
+               n_mfd_cells = ARRAY_SIZE(max77663_children);
+               rmap_config = &max77663_regmap_config;
+               break;
        default:
                dev_err(chip->dev, "ChipID is invalid %d\n", chip->chip_id);
                return -EINVAL;
@@ -491,6 +566,12 @@ static int max77620_probe(struct i2c_client *client,
                return ret;
        }
 
+       pm_off = of_device_is_system_power_controller(client->dev.of_node);
+       if (pm_off && !pm_power_off) {
+               max77620_scratch = chip;
+               pm_power_off = max77620_pm_power_off;
+       }
+
        return 0;
 }
 
@@ -546,6 +627,9 @@ static int max77620_i2c_suspend(struct device *dev)
                return ret;
        }
 
+       if (chip->chip_id == MAX77663)
+               goto out;
+
        /* Disable WK_EN0 */
        ret = regmap_update_bits(chip->rmap, MAX77620_REG_ONOFFCNFG2,
                                 MAX77620_ONOFFCNFG2_WK_EN0, 0);
@@ -581,7 +665,7 @@ static int max77620_i2c_resume(struct device *dev)
         * For MAX20024: No need to configure WKEN0 on resume as
         * it is configured on Init.
         */
-       if (chip->chip_id == MAX20024)
+       if (chip->chip_id == MAX20024 || chip->chip_id == MAX77663)
                goto out;
 
        /* Enable WK_EN0 */
@@ -603,6 +687,7 @@ out:
 static const struct i2c_device_id max77620_id[] = {
        {"max77620", MAX77620},
        {"max20024", MAX20024},
+       {"max77663", MAX77663},
        {},
 };
 
diff --git a/drivers/mfd/stmfx.c b/drivers/mfd/stmfx.c
new file mode 100644 (file)
index 0000000..fe8efba
--- /dev/null
@@ -0,0 +1,545 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for STMicroelectronics Multi-Function eXpander (STMFX) core
+ *
+ * Copyright (C) 2019 STMicroelectronics
+ * Author(s): Amelie Delaunay <amelie.delaunay@st.com>.
+ */
+#include <linux/bitfield.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/stmfx.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+
+static bool stmfx_reg_volatile(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case STMFX_REG_SYS_CTRL:
+       case STMFX_REG_IRQ_SRC_EN:
+       case STMFX_REG_IRQ_PENDING:
+       case STMFX_REG_IRQ_GPI_PENDING1:
+       case STMFX_REG_IRQ_GPI_PENDING2:
+       case STMFX_REG_IRQ_GPI_PENDING3:
+       case STMFX_REG_GPIO_STATE1:
+       case STMFX_REG_GPIO_STATE2:
+       case STMFX_REG_GPIO_STATE3:
+       case STMFX_REG_IRQ_GPI_SRC1:
+       case STMFX_REG_IRQ_GPI_SRC2:
+       case STMFX_REG_IRQ_GPI_SRC3:
+       case STMFX_REG_GPO_SET1:
+       case STMFX_REG_GPO_SET2:
+       case STMFX_REG_GPO_SET3:
+       case STMFX_REG_GPO_CLR1:
+       case STMFX_REG_GPO_CLR2:
+       case STMFX_REG_GPO_CLR3:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool stmfx_reg_writeable(struct device *dev, unsigned int reg)
+{
+       return (reg >= STMFX_REG_SYS_CTRL);
+}
+
+static const struct regmap_config stmfx_regmap_config = {
+       .reg_bits       = 8,
+       .reg_stride     = 1,
+       .val_bits       = 8,
+       .max_register   = STMFX_REG_MAX,
+       .volatile_reg   = stmfx_reg_volatile,
+       .writeable_reg  = stmfx_reg_writeable,
+       .cache_type     = REGCACHE_RBTREE,
+};
+
+static const struct resource stmfx_pinctrl_resources[] = {
+       DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_GPIO),
+};
+
+static const struct resource stmfx_idd_resources[] = {
+       DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_IDD),
+       DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_ERROR),
+};
+
+static const struct resource stmfx_ts_resources[] = {
+       DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_TS_DET),
+       DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_TS_NE),
+       DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_TS_TH),
+       DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_TS_FULL),
+       DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_TS_OVF),
+};
+
+static struct mfd_cell stmfx_cells[] = {
+       {
+               .of_compatible = "st,stmfx-0300-pinctrl",
+               .name = "stmfx-pinctrl",
+               .resources = stmfx_pinctrl_resources,
+               .num_resources = ARRAY_SIZE(stmfx_pinctrl_resources),
+       },
+       {
+               .of_compatible = "st,stmfx-0300-idd",
+               .name = "stmfx-idd",
+               .resources = stmfx_idd_resources,
+               .num_resources = ARRAY_SIZE(stmfx_idd_resources),
+       },
+       {
+               .of_compatible = "st,stmfx-0300-ts",
+               .name = "stmfx-ts",
+               .resources = stmfx_ts_resources,
+               .num_resources = ARRAY_SIZE(stmfx_ts_resources),
+       },
+};
+
+static u8 stmfx_func_to_mask(u32 func)
+{
+       u8 mask = 0;
+
+       if (func & STMFX_FUNC_GPIO)
+               mask |= STMFX_REG_SYS_CTRL_GPIO_EN;
+
+       if ((func & STMFX_FUNC_ALTGPIO_LOW) || (func & STMFX_FUNC_ALTGPIO_HIGH))
+               mask |= STMFX_REG_SYS_CTRL_ALTGPIO_EN;
+
+       if (func & STMFX_FUNC_TS)
+               mask |= STMFX_REG_SYS_CTRL_TS_EN;
+
+       if (func & STMFX_FUNC_IDD)
+               mask |= STMFX_REG_SYS_CTRL_IDD_EN;
+
+       return mask;
+}
+
+int stmfx_function_enable(struct stmfx *stmfx, u32 func)
+{
+       u32 sys_ctrl;
+       u8 mask;
+       int ret;
+
+       ret = regmap_read(stmfx->map, STMFX_REG_SYS_CTRL, &sys_ctrl);
+       if (ret)
+               return ret;
+
+       /*
+        * IDD and TS have priority in STMFX FW, so if IDD and TS are enabled,
+        * ALTGPIO function is disabled by STMFX FW. If IDD or TS is enabled,
+        * the number of aGPIO available decreases. To avoid GPIO management
+        * disturbance, abort IDD or TS function enable in this case.
+        */
+       if (((func & STMFX_FUNC_IDD) || (func & STMFX_FUNC_TS)) &&
+           (sys_ctrl & STMFX_REG_SYS_CTRL_ALTGPIO_EN)) {
+               dev_err(stmfx->dev, "ALTGPIO function already enabled\n");
+               return -EBUSY;
+       }
+
+       /* If TS is enabled, aGPIO[3:0] cannot be used */
+       if ((func & STMFX_FUNC_ALTGPIO_LOW) &&
+           (sys_ctrl & STMFX_REG_SYS_CTRL_TS_EN)) {
+               dev_err(stmfx->dev, "TS in use, aGPIO[3:0] unavailable\n");
+               return -EBUSY;
+       }
+
+       /* If IDD is enabled, aGPIO[7:4] cannot be used */
+       if ((func & STMFX_FUNC_ALTGPIO_HIGH) &&
+           (sys_ctrl & STMFX_REG_SYS_CTRL_IDD_EN)) {
+               dev_err(stmfx->dev, "IDD in use, aGPIO[7:4] unavailable\n");
+               return -EBUSY;
+       }
+
+       mask = stmfx_func_to_mask(func);
+
+       return regmap_update_bits(stmfx->map, STMFX_REG_SYS_CTRL, mask, mask);
+}
+EXPORT_SYMBOL_GPL(stmfx_function_enable);
+
+int stmfx_function_disable(struct stmfx *stmfx, u32 func)
+{
+       u8 mask = stmfx_func_to_mask(func);
+
+       return regmap_update_bits(stmfx->map, STMFX_REG_SYS_CTRL, mask, 0);
+}
+EXPORT_SYMBOL_GPL(stmfx_function_disable);
+
+static void stmfx_irq_bus_lock(struct irq_data *data)
+{
+       struct stmfx *stmfx = irq_data_get_irq_chip_data(data);
+
+       mutex_lock(&stmfx->lock);
+}
+
+static void stmfx_irq_bus_sync_unlock(struct irq_data *data)
+{
+       struct stmfx *stmfx = irq_data_get_irq_chip_data(data);
+
+       regmap_write(stmfx->map, STMFX_REG_IRQ_SRC_EN, stmfx->irq_src);
+
+       mutex_unlock(&stmfx->lock);
+}
+
+static void stmfx_irq_mask(struct irq_data *data)
+{
+       struct stmfx *stmfx = irq_data_get_irq_chip_data(data);
+
+       stmfx->irq_src &= ~BIT(data->hwirq % 8);
+}
+
+static void stmfx_irq_unmask(struct irq_data *data)
+{
+       struct stmfx *stmfx = irq_data_get_irq_chip_data(data);
+
+       stmfx->irq_src |= BIT(data->hwirq % 8);
+}
+
+static struct irq_chip stmfx_irq_chip = {
+       .name                   = "stmfx-core",
+       .irq_bus_lock           = stmfx_irq_bus_lock,
+       .irq_bus_sync_unlock    = stmfx_irq_bus_sync_unlock,
+       .irq_mask               = stmfx_irq_mask,
+       .irq_unmask             = stmfx_irq_unmask,
+};
+
+static irqreturn_t stmfx_irq_handler(int irq, void *data)
+{
+       struct stmfx *stmfx = data;
+       unsigned long n, pending;
+       u32 ack;
+       int ret;
+
+       ret = regmap_read(stmfx->map, STMFX_REG_IRQ_PENDING,
+                         (u32 *)&pending);
+       if (ret)
+               return IRQ_NONE;
+
+       /*
+        * There is no ACK for GPIO, MFX_REG_IRQ_PENDING_GPIO is a logical OR
+        * of MFX_REG_IRQ_GPI _PENDING1/_PENDING2/_PENDING3
+        */
+       ack = pending & ~BIT(STMFX_REG_IRQ_SRC_EN_GPIO);
+       if (ack) {
+               ret = regmap_write(stmfx->map, STMFX_REG_IRQ_ACK, ack);
+               if (ret)
+                       return IRQ_NONE;
+       }
+
+       for_each_set_bit(n, &pending, STMFX_REG_IRQ_SRC_MAX)
+               handle_nested_irq(irq_find_mapping(stmfx->irq_domain, n));
+
+       return IRQ_HANDLED;
+}
+
+static int stmfx_irq_map(struct irq_domain *d, unsigned int virq,
+                        irq_hw_number_t hwirq)
+{
+       irq_set_chip_data(virq, d->host_data);
+       irq_set_chip_and_handler(virq, &stmfx_irq_chip, handle_simple_irq);
+       irq_set_nested_thread(virq, 1);
+       irq_set_noprobe(virq);
+
+       return 0;
+}
+
+static void stmfx_irq_unmap(struct irq_domain *d, unsigned int virq)
+{
+       irq_set_chip_and_handler(virq, NULL, NULL);
+       irq_set_chip_data(virq, NULL);
+}
+
+static const struct irq_domain_ops stmfx_irq_ops = {
+       .map    = stmfx_irq_map,
+       .unmap  = stmfx_irq_unmap,
+};
+
+static void stmfx_irq_exit(struct i2c_client *client)
+{
+       struct stmfx *stmfx = i2c_get_clientdata(client);
+       int hwirq;
+
+       for (hwirq = 0; hwirq < STMFX_REG_IRQ_SRC_MAX; hwirq++)
+               irq_dispose_mapping(irq_find_mapping(stmfx->irq_domain, hwirq));
+
+       irq_domain_remove(stmfx->irq_domain);
+}
+
+static int stmfx_irq_init(struct i2c_client *client)
+{
+       struct stmfx *stmfx = i2c_get_clientdata(client);
+       u32 irqoutpin = 0, irqtrigger;
+       int ret;
+
+       stmfx->irq_domain = irq_domain_add_simple(stmfx->dev->of_node,
+                                                 STMFX_REG_IRQ_SRC_MAX, 0,
+                                                 &stmfx_irq_ops, stmfx);
+       if (!stmfx->irq_domain) {
+               dev_err(stmfx->dev, "Failed to create IRQ domain\n");
+               return -EINVAL;
+       }
+
+       if (!of_property_read_bool(stmfx->dev->of_node, "drive-open-drain"))
+               irqoutpin |= STMFX_REG_IRQ_OUT_PIN_TYPE;
+
+       irqtrigger = irq_get_trigger_type(client->irq);
+       if ((irqtrigger & IRQ_TYPE_EDGE_RISING) ||
+           (irqtrigger & IRQ_TYPE_LEVEL_HIGH))
+               irqoutpin |= STMFX_REG_IRQ_OUT_PIN_POL;
+
+       ret = regmap_write(stmfx->map, STMFX_REG_IRQ_OUT_PIN, irqoutpin);
+       if (ret)
+               return ret;
+
+       ret = devm_request_threaded_irq(stmfx->dev, client->irq,
+                                       NULL, stmfx_irq_handler,
+                                       irqtrigger | IRQF_ONESHOT,
+                                       "stmfx", stmfx);
+       if (ret)
+               stmfx_irq_exit(client);
+
+       return ret;
+}
+
+static int stmfx_chip_reset(struct stmfx *stmfx)
+{
+       int ret;
+
+       ret = regmap_write(stmfx->map, STMFX_REG_SYS_CTRL,
+                          STMFX_REG_SYS_CTRL_SWRST);
+       if (ret)
+               return ret;
+
+       msleep(STMFX_BOOT_TIME_MS);
+
+       return ret;
+}
+
+static int stmfx_chip_init(struct i2c_client *client)
+{
+       struct stmfx *stmfx = i2c_get_clientdata(client);
+       u32 id;
+       u8 version[2];
+       int ret;
+
+       stmfx->vdd = devm_regulator_get_optional(&client->dev, "vdd");
+       ret = PTR_ERR_OR_ZERO(stmfx->vdd);
+       if (ret == -ENODEV) {
+               stmfx->vdd = NULL;
+       } else if (ret == -EPROBE_DEFER) {
+               return ret;
+       } else if (ret) {
+               dev_err(&client->dev, "Failed to get VDD regulator: %d\n", ret);
+               return ret;
+       }
+
+       if (stmfx->vdd) {
+               ret = regulator_enable(stmfx->vdd);
+               if (ret) {
+                       dev_err(&client->dev, "VDD enable failed: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       ret = regmap_read(stmfx->map, STMFX_REG_CHIP_ID, &id);
+       if (ret) {
+               dev_err(&client->dev, "Error reading chip ID: %d\n", ret);
+               goto err;
+       }
+
+       /*
+        * Check that ID is the complement of the I2C address:
+        * STMFX I2C address follows the 7-bit format (MSB), that's why
+        * client->addr is shifted.
+        *
+        * STMFX_I2C_ADDR|       STMFX         |        Linux
+        *   input pin   | I2C device address  | I2C device address
+        *---------------------------------------------------------
+        *       0       | b: 1000 010x h:0x84 |       0x42
+        *       1       | b: 1000 011x h:0x86 |       0x43
+        */
+       if (FIELD_GET(STMFX_REG_CHIP_ID_MASK, ~id) != (client->addr << 1)) {
+               dev_err(&client->dev, "Unknown chip ID: %#x\n", id);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       ret = regmap_bulk_read(stmfx->map, STMFX_REG_FW_VERSION_MSB,
+                              version, ARRAY_SIZE(version));
+       if (ret) {
+               dev_err(&client->dev, "Error reading FW version: %d\n", ret);
+               goto err;
+       }
+
+       dev_info(&client->dev, "STMFX id: %#x, fw version: %x.%02x\n",
+                id, version[0], version[1]);
+
+       ret = stmfx_chip_reset(stmfx);
+       if (ret) {
+               dev_err(&client->dev, "Failed to reset chip: %d\n", ret);
+               goto err;
+       }
+
+       return 0;
+
+err:
+       if (stmfx->vdd)
+               return regulator_disable(stmfx->vdd);
+
+       return ret;
+}
+
+static int stmfx_chip_exit(struct i2c_client *client)
+{
+       struct stmfx *stmfx = i2c_get_clientdata(client);
+
+       regmap_write(stmfx->map, STMFX_REG_IRQ_SRC_EN, 0);
+       regmap_write(stmfx->map, STMFX_REG_SYS_CTRL, 0);
+
+       if (stmfx->vdd)
+               return regulator_disable(stmfx->vdd);
+
+       return 0;
+}
+
+static int stmfx_probe(struct i2c_client *client,
+                      const struct i2c_device_id *id)
+{
+       struct device *dev = &client->dev;
+       struct stmfx *stmfx;
+       int ret;
+
+       stmfx = devm_kzalloc(dev, sizeof(*stmfx), GFP_KERNEL);
+       if (!stmfx)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, stmfx);
+
+       stmfx->dev = dev;
+
+       stmfx->map = devm_regmap_init_i2c(client, &stmfx_regmap_config);
+       if (IS_ERR(stmfx->map)) {
+               ret = PTR_ERR(stmfx->map);
+               dev_err(dev, "Failed to allocate register map: %d\n", ret);
+               return ret;
+       }
+
+       mutex_init(&stmfx->lock);
+
+       ret = stmfx_chip_init(client);
+       if (ret) {
+               if (ret == -ETIMEDOUT)
+                       return -EPROBE_DEFER;
+               return ret;
+       }
+
+       if (client->irq < 0) {
+               dev_err(dev, "Failed to get IRQ: %d\n", client->irq);
+               ret = client->irq;
+               goto err_chip_exit;
+       }
+
+       ret = stmfx_irq_init(client);
+       if (ret)
+               goto err_chip_exit;
+
+       ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
+                                  stmfx_cells, ARRAY_SIZE(stmfx_cells), NULL,
+                                  0, stmfx->irq_domain);
+       if (ret)
+               goto err_irq_exit;
+
+       return 0;
+
+err_irq_exit:
+       stmfx_irq_exit(client);
+err_chip_exit:
+       stmfx_chip_exit(client);
+
+       return ret;
+}
+
+static int stmfx_remove(struct i2c_client *client)
+{
+       stmfx_irq_exit(client);
+
+       return stmfx_chip_exit(client);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int stmfx_suspend(struct device *dev)
+{
+       struct stmfx *stmfx = dev_get_drvdata(dev);
+       int ret;
+
+       ret = regmap_raw_read(stmfx->map, STMFX_REG_SYS_CTRL,
+                             &stmfx->bkp_sysctrl, sizeof(stmfx->bkp_sysctrl));
+       if (ret)
+               return ret;
+
+       ret = regmap_raw_read(stmfx->map, STMFX_REG_IRQ_OUT_PIN,
+                             &stmfx->bkp_irqoutpin,
+                             sizeof(stmfx->bkp_irqoutpin));
+       if (ret)
+               return ret;
+
+       if (stmfx->vdd)
+               return regulator_disable(stmfx->vdd);
+
+       return 0;
+}
+
+static int stmfx_resume(struct device *dev)
+{
+       struct stmfx *stmfx = dev_get_drvdata(dev);
+       int ret;
+
+       if (stmfx->vdd) {
+               ret = regulator_enable(stmfx->vdd);
+               if (ret) {
+                       dev_err(stmfx->dev,
+                               "VDD enable failed: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       ret = regmap_raw_write(stmfx->map, STMFX_REG_SYS_CTRL,
+                              &stmfx->bkp_sysctrl, sizeof(stmfx->bkp_sysctrl));
+       if (ret)
+               return ret;
+
+       ret = regmap_raw_write(stmfx->map, STMFX_REG_IRQ_OUT_PIN,
+                              &stmfx->bkp_irqoutpin,
+                              sizeof(stmfx->bkp_irqoutpin));
+       if (ret)
+               return ret;
+
+       ret = regmap_raw_write(stmfx->map, STMFX_REG_IRQ_SRC_EN,
+                              &stmfx->irq_src, sizeof(stmfx->irq_src));
+       if (ret)
+               return ret;
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(stmfx_dev_pm_ops, stmfx_suspend, stmfx_resume);
+
+static const struct of_device_id stmfx_of_match[] = {
+       { .compatible = "st,stmfx-0300", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, stmfx_of_match);
+
+static struct i2c_driver stmfx_driver = {
+       .driver = {
+               .name = "stmfx-core",
+               .of_match_table = of_match_ptr(stmfx_of_match),
+               .pm = &stmfx_dev_pm_ops,
+       },
+       .probe = stmfx_probe,
+       .remove = stmfx_remove,
+};
+module_i2c_driver(stmfx_driver);
+
+MODULE_DESCRIPTION("STMFX core driver");
+MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");
+MODULE_LICENSE("GPL v2");
index 37d0bdb291c32fa5c1fe138b082a58af41d694dc..b06cb908d1aa482b16337f38a8de4e506f53f92b 100644 (file)
@@ -54,14 +54,6 @@ static void ti_lmu_disable_hw(void *data)
                gpiod_set_value(lmu->en_gpio, 0);
 }
 
-static const struct mfd_cell lm3532_devices[] = {
-       {
-               .name          = "ti-lmu-backlight",
-               .id            = LM3532,
-               .of_compatible = "ti,lm3532-backlight",
-       },
-};
-
 #define LM363X_REGULATOR(_id)                  \
 {                                              \
        .name          = "lm363x-regulator",    \
@@ -141,7 +133,6 @@ static const struct ti_lmu_data chip##_data =       \
        .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);
@@ -211,7 +202,6 @@ static int ti_lmu_probe(struct i2c_client *cl, const struct i2c_device_id *id)
 }
 
 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 },
@@ -222,7 +212,6 @@ static const struct of_device_id ti_lmu_of_match[] = {
 MODULE_DEVICE_TABLE(of, ti_lmu_of_match);
 
 static const struct i2c_device_id ti_lmu_ids[] = {
-       { "lm3532", LM3532 },
        { "lm3631", LM3631 },
        { "lm3632", LM3632 },
        { "lm3633", LM3633 },
index 5b3b06a0a3bf53e1eac9572ae8d14add0c3835e7..d466e33635b0cadf19fe1e4bc56dc4ac152fe01b 100644 (file)
@@ -15,7 +15,7 @@
  * Adopted from dwmac-sti.c
  */
 
-#include <linux/mfd/syscon.h>
+#include <linux/mfd/altera-sysmgr.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_net.h>
@@ -114,7 +114,8 @@ static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device *
 
        dwmac->interface = of_get_phy_mode(np);
 
-       sys_mgr_base_addr = syscon_regmap_lookup_by_phandle(np, "altr,sysmgr-syscon");
+       sys_mgr_base_addr =
+               altr_sysmgr_regmap_lookup_by_phandle(np, "altr,sysmgr-syscon");
        if (IS_ERR(sys_mgr_base_addr)) {
                dev_info(dev, "No sysmgr-syscon node found\n");
                return PTR_ERR(sys_mgr_base_addr);
index 2764d713fea8b37928d44110d3894e12f3e3eff3..1f380ca65578324d23f0626421863bac82caa57c 100644 (file)
@@ -265,6 +265,20 @@ config PINCTRL_ST
        select PINCONF
        select GPIOLIB_IRQCHIP
 
+config PINCTRL_STMFX
+       tristate "STMicroelectronics STMFX GPIO expander pinctrl driver"
+       depends on I2C
+       depends on OF || COMPILE_TEST
+       select GENERIC_PINCONF
+       select GPIOLIB_IRQCHIP
+       select MFD_STMFX
+       help
+         Driver for STMicroelectronics Multi-Function eXpander (STMFX)
+         GPIO expander.
+         This provides a GPIO interface supporting inputs and outputs,
+         and configuring push-pull, open-drain, and can also be used as
+         interrupt-controller.
+
 config PINCTRL_U300
        bool "U300 pin controller driver"
        depends on ARCH_U300
index 712184b74a5c7d547f0500ad1635917e0dceec41..c188e0f687b1aae7e6e205621b70e5c408854d5d 100644 (file)
@@ -40,6 +40,7 @@ obj-$(CONFIG_PINCTRL_LANTIQ)  += pinctrl-lantiq.o
 obj-$(CONFIG_PINCTRL_LPC18XX)  += pinctrl-lpc18xx.o
 obj-$(CONFIG_PINCTRL_TB10X)    += pinctrl-tb10x.o
 obj-$(CONFIG_PINCTRL_ST)       += pinctrl-st.o
+obj-$(CONFIG_PINCTRL_STMFX)    += pinctrl-stmfx.o
 obj-$(CONFIG_PINCTRL_ZYNQ)     += pinctrl-zynq.o
 obj-$(CONFIG_PINCTRL_INGENIC)  += pinctrl-ingenic.o
 obj-$(CONFIG_PINCTRL_RK805)    += pinctrl-rk805.o
diff --git a/drivers/pinctrl/pinctrl-stmfx.c b/drivers/pinctrl/pinctrl-stmfx.c
new file mode 100644 (file)
index 0000000..eba872c
--- /dev/null
@@ -0,0 +1,819 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for STMicroelectronics Multi-Function eXpander (STMFX) GPIO expander
+ *
+ * Copyright (C) 2019 STMicroelectronics
+ * Author(s): Amelie Delaunay <amelie.delaunay@st.com>.
+ */
+#include <linux/gpio/driver.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/stmfx.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinmux.h>
+
+#include "core.h"
+#include "pinctrl-utils.h"
+
+/* GPIOs expander */
+/* GPIO_STATE1 0x10, GPIO_STATE2 0x11, GPIO_STATE3 0x12 */
+#define STMFX_REG_GPIO_STATE           STMFX_REG_GPIO_STATE1 /* R */
+/* GPIO_DIR1 0x60, GPIO_DIR2 0x61, GPIO_DIR3 0x63 */
+#define STMFX_REG_GPIO_DIR             STMFX_REG_GPIO_DIR1 /* RW */
+/* GPIO_TYPE1 0x64, GPIO_TYPE2 0x65, GPIO_TYPE3 0x66 */
+#define STMFX_REG_GPIO_TYPE            STMFX_REG_GPIO_TYPE1 /* RW */
+/* GPIO_PUPD1 0x68, GPIO_PUPD2 0x69, GPIO_PUPD3 0x6A */
+#define STMFX_REG_GPIO_PUPD            STMFX_REG_GPIO_PUPD1 /* RW */
+/* GPO_SET1 0x6C, GPO_SET2 0x6D, GPO_SET3 0x6E */
+#define STMFX_REG_GPO_SET              STMFX_REG_GPO_SET1 /* RW */
+/* GPO_CLR1 0x70, GPO_CLR2 0x71, GPO_CLR3 0x72 */
+#define STMFX_REG_GPO_CLR              STMFX_REG_GPO_CLR1 /* RW */
+/* IRQ_GPI_SRC1 0x48, IRQ_GPI_SRC2 0x49, IRQ_GPI_SRC3 0x4A */
+#define STMFX_REG_IRQ_GPI_SRC          STMFX_REG_IRQ_GPI_SRC1 /* RW */
+/* IRQ_GPI_EVT1 0x4C, IRQ_GPI_EVT2 0x4D, IRQ_GPI_EVT3 0x4E */
+#define STMFX_REG_IRQ_GPI_EVT          STMFX_REG_IRQ_GPI_EVT1 /* RW */
+/* IRQ_GPI_TYPE1 0x50, IRQ_GPI_TYPE2 0x51, IRQ_GPI_TYPE3 0x52 */
+#define STMFX_REG_IRQ_GPI_TYPE         STMFX_REG_IRQ_GPI_TYPE1 /* RW */
+/* IRQ_GPI_PENDING1 0x0C, IRQ_GPI_PENDING2 0x0D, IRQ_GPI_PENDING3 0x0E*/
+#define STMFX_REG_IRQ_GPI_PENDING      STMFX_REG_IRQ_GPI_PENDING1 /* R */
+/* IRQ_GPI_ACK1 0x54, IRQ_GPI_ACK2 0x55, IRQ_GPI_ACK3 0x56 */
+#define STMFX_REG_IRQ_GPI_ACK          STMFX_REG_IRQ_GPI_ACK1 /* RW */
+
+#define NR_GPIO_REGS                   3
+#define NR_GPIOS_PER_REG               8
+#define get_reg(offset)                        ((offset) / NR_GPIOS_PER_REG)
+#define get_shift(offset)              ((offset) % NR_GPIOS_PER_REG)
+#define get_mask(offset)               (BIT(get_shift(offset)))
+
+/*
+ * STMFX pinctrl can have up to 24 pins if STMFX other functions are not used.
+ * Pins availability is managed thanks to gpio-ranges property.
+ */
+static const struct pinctrl_pin_desc stmfx_pins[] = {
+       PINCTRL_PIN(0, "gpio0"),
+       PINCTRL_PIN(1, "gpio1"),
+       PINCTRL_PIN(2, "gpio2"),
+       PINCTRL_PIN(3, "gpio3"),
+       PINCTRL_PIN(4, "gpio4"),
+       PINCTRL_PIN(5, "gpio5"),
+       PINCTRL_PIN(6, "gpio6"),
+       PINCTRL_PIN(7, "gpio7"),
+       PINCTRL_PIN(8, "gpio8"),
+       PINCTRL_PIN(9, "gpio9"),
+       PINCTRL_PIN(10, "gpio10"),
+       PINCTRL_PIN(11, "gpio11"),
+       PINCTRL_PIN(12, "gpio12"),
+       PINCTRL_PIN(13, "gpio13"),
+       PINCTRL_PIN(14, "gpio14"),
+       PINCTRL_PIN(15, "gpio15"),
+       PINCTRL_PIN(16, "agpio0"),
+       PINCTRL_PIN(17, "agpio1"),
+       PINCTRL_PIN(18, "agpio2"),
+       PINCTRL_PIN(19, "agpio3"),
+       PINCTRL_PIN(20, "agpio4"),
+       PINCTRL_PIN(21, "agpio5"),
+       PINCTRL_PIN(22, "agpio6"),
+       PINCTRL_PIN(23, "agpio7"),
+};
+
+struct stmfx_pinctrl {
+       struct device *dev;
+       struct stmfx *stmfx;
+       struct pinctrl_dev *pctl_dev;
+       struct pinctrl_desc pctl_desc;
+       struct gpio_chip gpio_chip;
+       struct irq_chip irq_chip;
+       struct mutex lock; /* IRQ bus lock */
+       unsigned long gpio_valid_mask;
+       /* Cache of IRQ_GPI_* registers for bus_lock */
+       u8 irq_gpi_src[NR_GPIO_REGS];
+       u8 irq_gpi_type[NR_GPIO_REGS];
+       u8 irq_gpi_evt[NR_GPIO_REGS];
+       u8 irq_toggle_edge[NR_GPIO_REGS];
+#ifdef CONFIG_PM
+       /* Backup of GPIO_* registers for suspend/resume */
+       u8 bkp_gpio_state[NR_GPIO_REGS];
+       u8 bkp_gpio_dir[NR_GPIO_REGS];
+       u8 bkp_gpio_type[NR_GPIO_REGS];
+       u8 bkp_gpio_pupd[NR_GPIO_REGS];
+#endif
+};
+
+static int stmfx_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+       struct stmfx_pinctrl *pctl = gpiochip_get_data(gc);
+       u32 reg = STMFX_REG_GPIO_STATE + get_reg(offset);
+       u32 mask = get_mask(offset);
+       u32 value;
+       int ret;
+
+       ret = regmap_read(pctl->stmfx->map, reg, &value);
+
+       return ret ? ret : !!(value & mask);
+}
+
+static void stmfx_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
+{
+       struct stmfx_pinctrl *pctl = gpiochip_get_data(gc);
+       u32 reg = value ? STMFX_REG_GPO_SET : STMFX_REG_GPO_CLR;
+       u32 mask = get_mask(offset);
+
+       regmap_write_bits(pctl->stmfx->map, reg + get_reg(offset),
+                         mask, mask);
+}
+
+static int stmfx_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+       struct stmfx_pinctrl *pctl = gpiochip_get_data(gc);
+       u32 reg = STMFX_REG_GPIO_DIR + get_reg(offset);
+       u32 mask = get_mask(offset);
+       u32 val;
+       int ret;
+
+       ret = regmap_read(pctl->stmfx->map, reg, &val);
+       /*
+        * On stmfx, gpio pins direction is (0)input, (1)output.
+        * .get_direction returns 0=out, 1=in
+        */
+
+       return ret ? ret : !(val & mask);
+}
+
+static int stmfx_gpio_direction_input(struct gpio_chip *gc, unsigned int offset)
+{
+       struct stmfx_pinctrl *pctl = gpiochip_get_data(gc);
+       u32 reg = STMFX_REG_GPIO_DIR + get_reg(offset);
+       u32 mask = get_mask(offset);
+
+       return regmap_write_bits(pctl->stmfx->map, reg, mask, 0);
+}
+
+static int stmfx_gpio_direction_output(struct gpio_chip *gc,
+                                      unsigned int offset, int value)
+{
+       struct stmfx_pinctrl *pctl = gpiochip_get_data(gc);
+       u32 reg = STMFX_REG_GPIO_DIR + get_reg(offset);
+       u32 mask = get_mask(offset);
+
+       stmfx_gpio_set(gc, offset, value);
+
+       return regmap_write_bits(pctl->stmfx->map, reg, mask, mask);
+}
+
+static int stmfx_pinconf_get_pupd(struct stmfx_pinctrl *pctl,
+                                 unsigned int offset)
+{
+       u32 reg = STMFX_REG_GPIO_PUPD + get_reg(offset);
+       u32 pupd, mask = get_mask(offset);
+       int ret;
+
+       ret = regmap_read(pctl->stmfx->map, reg, &pupd);
+       if (ret)
+               return ret;
+
+       return !!(pupd & mask);
+}
+
+static int stmfx_pinconf_set_pupd(struct stmfx_pinctrl *pctl,
+                                 unsigned int offset, u32 pupd)
+{
+       u32 reg = STMFX_REG_GPIO_PUPD + get_reg(offset);
+       u32 mask = get_mask(offset);
+
+       return regmap_write_bits(pctl->stmfx->map, reg, mask, pupd ? mask : 0);
+}
+
+static int stmfx_pinconf_get_type(struct stmfx_pinctrl *pctl,
+                                 unsigned int offset)
+{
+       u32 reg = STMFX_REG_GPIO_TYPE + get_reg(offset);
+       u32 type, mask = get_mask(offset);
+       int ret;
+
+       ret = regmap_read(pctl->stmfx->map, reg, &type);
+       if (ret)
+               return ret;
+
+       return !!(type & mask);
+}
+
+static int stmfx_pinconf_set_type(struct stmfx_pinctrl *pctl,
+                                 unsigned int offset, u32 type)
+{
+       u32 reg = STMFX_REG_GPIO_TYPE + get_reg(offset);
+       u32 mask = get_mask(offset);
+
+       return regmap_write_bits(pctl->stmfx->map, reg, mask, type ? mask : 0);
+}
+
+static int stmfx_pinconf_get(struct pinctrl_dev *pctldev,
+                            unsigned int pin, unsigned long *config)
+{
+       struct stmfx_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+       u32 param = pinconf_to_config_param(*config);
+       struct pinctrl_gpio_range *range;
+       u32 arg = 0;
+       int ret, dir, type, pupd;
+
+       range = pinctrl_find_gpio_range_from_pin_nolock(pctldev, pin);
+       if (!range)
+               return -EINVAL;
+
+       dir = stmfx_gpio_get_direction(&pctl->gpio_chip, pin);
+       if (dir < 0)
+               return dir;
+       type = stmfx_pinconf_get_type(pctl, pin);
+       if (type < 0)
+               return type;
+       pupd = stmfx_pinconf_get_pupd(pctl, pin);
+       if (pupd < 0)
+               return pupd;
+
+       switch (param) {
+       case PIN_CONFIG_BIAS_DISABLE:
+               if ((!dir && (!type || !pupd)) || (dir && !type))
+                       arg = 1;
+               break;
+       case PIN_CONFIG_BIAS_PULL_DOWN:
+               if (dir && type && !pupd)
+                       arg = 1;
+               break;
+       case PIN_CONFIG_BIAS_PULL_UP:
+               if (type && pupd)
+                       arg = 1;
+               break;
+       case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+               if ((!dir && type) || (dir && !type))
+                       arg = 1;
+               break;
+       case PIN_CONFIG_DRIVE_PUSH_PULL:
+               if ((!dir && !type) || (dir && type))
+                       arg = 1;
+               break;
+       case PIN_CONFIG_OUTPUT:
+               if (dir)
+                       return -EINVAL;
+
+               ret = stmfx_gpio_get(&pctl->gpio_chip, pin);
+               if (ret < 0)
+                       return ret;
+
+               arg = ret;
+               break;
+       default:
+               return -ENOTSUPP;
+       }
+
+       *config = pinconf_to_config_packed(param, arg);
+
+       return 0;
+}
+
+static int stmfx_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
+                            unsigned long *configs, unsigned int num_configs)
+{
+       struct stmfx_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+       struct pinctrl_gpio_range *range;
+       enum pin_config_param param;
+       u32 arg;
+       int dir, i, ret;
+
+       range = pinctrl_find_gpio_range_from_pin_nolock(pctldev, pin);
+       if (!range) {
+               dev_err(pctldev->dev, "pin %d is not available\n", pin);
+               return -EINVAL;
+       }
+
+       dir = stmfx_gpio_get_direction(&pctl->gpio_chip, pin);
+       if (dir < 0)
+               return dir;
+
+       for (i = 0; i < num_configs; i++) {
+               param = pinconf_to_config_param(configs[i]);
+               arg = pinconf_to_config_argument(configs[i]);
+
+               switch (param) {
+               case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
+               case PIN_CONFIG_BIAS_DISABLE:
+               case PIN_CONFIG_BIAS_PULL_DOWN:
+                       ret = stmfx_pinconf_set_pupd(pctl, pin, 0);
+                       if (ret)
+                               return ret;
+                       break;
+               case PIN_CONFIG_BIAS_PULL_UP:
+                       ret = stmfx_pinconf_set_pupd(pctl, pin, 1);
+                       if (ret)
+                               return ret;
+                       break;
+               case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+                       if (!dir)
+                               ret = stmfx_pinconf_set_type(pctl, pin, 1);
+                       else
+                               ret = stmfx_pinconf_set_type(pctl, pin, 0);
+                       if (ret)
+                               return ret;
+                       break;
+               case PIN_CONFIG_DRIVE_PUSH_PULL:
+                       if (!dir)
+                               ret = stmfx_pinconf_set_type(pctl, pin, 0);
+                       else
+                               ret = stmfx_pinconf_set_type(pctl, pin, 1);
+                       if (ret)
+                               return ret;
+                       break;
+               case PIN_CONFIG_OUTPUT:
+                       ret = stmfx_gpio_direction_output(&pctl->gpio_chip,
+                                                         pin, arg);
+                       if (ret)
+                               return ret;
+                       break;
+               default:
+                       return -ENOTSUPP;
+               }
+       }
+
+       return 0;
+}
+
+static void stmfx_pinconf_dbg_show(struct pinctrl_dev *pctldev,
+                                  struct seq_file *s, unsigned int offset)
+{
+       struct stmfx_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+       struct pinctrl_gpio_range *range;
+       int dir, type, pupd, val;
+
+       range = pinctrl_find_gpio_range_from_pin_nolock(pctldev, offset);
+       if (!range)
+               return;
+
+       dir = stmfx_gpio_get_direction(&pctl->gpio_chip, offset);
+       if (dir < 0)
+               return;
+       type = stmfx_pinconf_get_type(pctl, offset);
+       if (type < 0)
+               return;
+       pupd = stmfx_pinconf_get_pupd(pctl, offset);
+       if (pupd < 0)
+               return;
+       val = stmfx_gpio_get(&pctl->gpio_chip, offset);
+       if (val < 0)
+               return;
+
+       if (!dir) {
+               seq_printf(s, "output %s ", val ? "high" : "low");
+               if (type)
+                       seq_printf(s, "open drain %s internal pull-up ",
+                                  pupd ? "with" : "without");
+               else
+                       seq_puts(s, "push pull no pull ");
+       } else {
+               seq_printf(s, "input %s ", val ? "high" : "low");
+               if (type)
+                       seq_printf(s, "with internal pull-%s ",
+                                  pupd ? "up" : "down");
+               else
+                       seq_printf(s, "%s ", pupd ? "floating" : "analog");
+       }
+}
+
+static const struct pinconf_ops stmfx_pinconf_ops = {
+       .pin_config_get         = stmfx_pinconf_get,
+       .pin_config_set         = stmfx_pinconf_set,
+       .pin_config_dbg_show    = stmfx_pinconf_dbg_show,
+};
+
+static int stmfx_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
+{
+       return 0;
+}
+
+static const char *stmfx_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
+                                               unsigned int selector)
+{
+       return NULL;
+}
+
+static int stmfx_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
+                                       unsigned int selector,
+                                       const unsigned int **pins,
+                                       unsigned int *num_pins)
+{
+       return -ENOTSUPP;
+}
+
+static const struct pinctrl_ops stmfx_pinctrl_ops = {
+       .get_groups_count = stmfx_pinctrl_get_groups_count,
+       .get_group_name = stmfx_pinctrl_get_group_name,
+       .get_group_pins = stmfx_pinctrl_get_group_pins,
+       .dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
+       .dt_free_map = pinctrl_utils_free_map,
+};
+
+static void stmfx_pinctrl_irq_mask(struct irq_data *data)
+{
+       struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data);
+       struct stmfx_pinctrl *pctl = gpiochip_get_data(gpio_chip);
+       u32 reg = get_reg(data->hwirq);
+       u32 mask = get_mask(data->hwirq);
+
+       pctl->irq_gpi_src[reg] &= ~mask;
+}
+
+static void stmfx_pinctrl_irq_unmask(struct irq_data *data)
+{
+       struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data);
+       struct stmfx_pinctrl *pctl = gpiochip_get_data(gpio_chip);
+       u32 reg = get_reg(data->hwirq);
+       u32 mask = get_mask(data->hwirq);
+
+       pctl->irq_gpi_src[reg] |= mask;
+}
+
+static int stmfx_pinctrl_irq_set_type(struct irq_data *data, unsigned int type)
+{
+       struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data);
+       struct stmfx_pinctrl *pctl = gpiochip_get_data(gpio_chip);
+       u32 reg = get_reg(data->hwirq);
+       u32 mask = get_mask(data->hwirq);
+
+       if (type == IRQ_TYPE_NONE)
+               return -EINVAL;
+
+       if (type & IRQ_TYPE_EDGE_BOTH) {
+               pctl->irq_gpi_evt[reg] |= mask;
+               irq_set_handler_locked(data, handle_edge_irq);
+       } else {
+               pctl->irq_gpi_evt[reg] &= ~mask;
+               irq_set_handler_locked(data, handle_level_irq);
+       }
+
+       if ((type & IRQ_TYPE_EDGE_RISING) || (type & IRQ_TYPE_LEVEL_HIGH))
+               pctl->irq_gpi_type[reg] |= mask;
+       else
+               pctl->irq_gpi_type[reg] &= ~mask;
+
+       /*
+        * In case of (type & IRQ_TYPE_EDGE_BOTH), we need to know current
+        * GPIO value to set the right edge trigger. But in atomic context
+        * here we can't access registers over I2C. That's why (type &
+        * IRQ_TYPE_EDGE_BOTH) will be managed in .irq_sync_unlock.
+        */
+
+       if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
+               pctl->irq_toggle_edge[reg] |= mask;
+       else
+               pctl->irq_toggle_edge[reg] &= mask;
+
+       return 0;
+}
+
+static void stmfx_pinctrl_irq_bus_lock(struct irq_data *data)
+{
+       struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data);
+       struct stmfx_pinctrl *pctl = gpiochip_get_data(gpio_chip);
+
+       mutex_lock(&pctl->lock);
+}
+
+static void stmfx_pinctrl_irq_bus_sync_unlock(struct irq_data *data)
+{
+       struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data);
+       struct stmfx_pinctrl *pctl = gpiochip_get_data(gpio_chip);
+       u32 reg = get_reg(data->hwirq);
+       u32 mask = get_mask(data->hwirq);
+
+       /*
+        * In case of IRQ_TYPE_EDGE_BOTH), read the current GPIO value
+        * (this couldn't be done in .irq_set_type because of atomic context)
+        * to set the right irq trigger type.
+        */
+       if (pctl->irq_toggle_edge[reg] & mask) {
+               if (stmfx_gpio_get(gpio_chip, data->hwirq))
+                       pctl->irq_gpi_type[reg] &= ~mask;
+               else
+                       pctl->irq_gpi_type[reg] |= mask;
+       }
+
+       regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_EVT,
+                         pctl->irq_gpi_evt, NR_GPIO_REGS);
+       regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_TYPE,
+                         pctl->irq_gpi_type, NR_GPIO_REGS);
+       regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_SRC,
+                         pctl->irq_gpi_src, NR_GPIO_REGS);
+
+       mutex_unlock(&pctl->lock);
+}
+
+static void stmfx_pinctrl_irq_toggle_trigger(struct stmfx_pinctrl *pctl,
+                                            unsigned int offset)
+{
+       u32 reg = get_reg(offset);
+       u32 mask = get_mask(offset);
+       int val;
+
+       if (!(pctl->irq_toggle_edge[reg] & mask))
+               return;
+
+       val = stmfx_gpio_get(&pctl->gpio_chip, offset);
+       if (val < 0)
+               return;
+
+       if (val) {
+               pctl->irq_gpi_type[reg] &= mask;
+               regmap_write_bits(pctl->stmfx->map,
+                                 STMFX_REG_IRQ_GPI_TYPE + reg,
+                                 mask, 0);
+
+       } else {
+               pctl->irq_gpi_type[reg] |= mask;
+               regmap_write_bits(pctl->stmfx->map,
+                                 STMFX_REG_IRQ_GPI_TYPE + reg,
+                                 mask, mask);
+       }
+}
+
+static irqreturn_t stmfx_pinctrl_irq_thread_fn(int irq, void *dev_id)
+{
+       struct stmfx_pinctrl *pctl = (struct stmfx_pinctrl *)dev_id;
+       struct gpio_chip *gc = &pctl->gpio_chip;
+       u8 pending[NR_GPIO_REGS];
+       u8 src[NR_GPIO_REGS] = {0, 0, 0};
+       unsigned long n, status;
+       int ret;
+
+       ret = regmap_bulk_read(pctl->stmfx->map, STMFX_REG_IRQ_GPI_PENDING,
+                              &pending, NR_GPIO_REGS);
+       if (ret)
+               return IRQ_NONE;
+
+       regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_SRC,
+                         src, NR_GPIO_REGS);
+
+       status = *(unsigned long *)pending;
+       for_each_set_bit(n, &status, gc->ngpio) {
+               handle_nested_irq(irq_find_mapping(gc->irq.domain, n));
+               stmfx_pinctrl_irq_toggle_trigger(pctl, n);
+       }
+
+       regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_SRC,
+                         pctl->irq_gpi_src, NR_GPIO_REGS);
+
+       return IRQ_HANDLED;
+}
+
+static int stmfx_pinctrl_gpio_function_enable(struct stmfx_pinctrl *pctl)
+{
+       struct pinctrl_gpio_range *gpio_range;
+       struct pinctrl_dev *pctl_dev = pctl->pctl_dev;
+       u32 func = STMFX_FUNC_GPIO;
+
+       pctl->gpio_valid_mask = GENMASK(15, 0);
+
+       gpio_range = pinctrl_find_gpio_range_from_pin(pctl_dev, 16);
+       if (gpio_range) {
+               func |= STMFX_FUNC_ALTGPIO_LOW;
+               pctl->gpio_valid_mask |= GENMASK(19, 16);
+       }
+
+       gpio_range = pinctrl_find_gpio_range_from_pin(pctl_dev, 20);
+       if (gpio_range) {
+               func |= STMFX_FUNC_ALTGPIO_HIGH;
+               pctl->gpio_valid_mask |= GENMASK(23, 20);
+       }
+
+       return stmfx_function_enable(pctl->stmfx, func);
+}
+
+static int stmfx_pinctrl_probe(struct platform_device *pdev)
+{
+       struct stmfx *stmfx = dev_get_drvdata(pdev->dev.parent);
+       struct device_node *np = pdev->dev.of_node;
+       struct stmfx_pinctrl *pctl;
+       u32 n;
+       int irq, ret;
+
+       pctl = devm_kzalloc(stmfx->dev, sizeof(*pctl), GFP_KERNEL);
+       if (!pctl)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, pctl);
+
+       pctl->dev = &pdev->dev;
+       pctl->stmfx = stmfx;
+
+       if (!of_find_property(np, "gpio-ranges", NULL)) {
+               dev_err(pctl->dev, "missing required gpio-ranges property\n");
+               return -EINVAL;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq <= 0) {
+               dev_err(pctl->dev, "failed to get irq\n");
+               return -ENXIO;
+       }
+
+       mutex_init(&pctl->lock);
+
+       /* Register pin controller */
+       pctl->pctl_desc.name = "stmfx-pinctrl";
+       pctl->pctl_desc.pctlops = &stmfx_pinctrl_ops;
+       pctl->pctl_desc.confops = &stmfx_pinconf_ops;
+       pctl->pctl_desc.pins = stmfx_pins;
+       pctl->pctl_desc.npins = ARRAY_SIZE(stmfx_pins);
+       pctl->pctl_desc.owner = THIS_MODULE;
+
+       ret = devm_pinctrl_register_and_init(pctl->dev, &pctl->pctl_desc,
+                                            pctl, &pctl->pctl_dev);
+       if (ret) {
+               dev_err(pctl->dev, "pinctrl registration failed\n");
+               return ret;
+       }
+
+       ret = pinctrl_enable(pctl->pctl_dev);
+       if (ret) {
+               dev_err(pctl->dev, "pinctrl enable failed\n");
+               return ret;
+       }
+
+       /* Register gpio controller */
+       pctl->gpio_chip.label = "stmfx-gpio";
+       pctl->gpio_chip.parent = pctl->dev;
+       pctl->gpio_chip.get_direction = stmfx_gpio_get_direction;
+       pctl->gpio_chip.direction_input = stmfx_gpio_direction_input;
+       pctl->gpio_chip.direction_output = stmfx_gpio_direction_output;
+       pctl->gpio_chip.get = stmfx_gpio_get;
+       pctl->gpio_chip.set = stmfx_gpio_set;
+       pctl->gpio_chip.set_config = gpiochip_generic_config;
+       pctl->gpio_chip.base = -1;
+       pctl->gpio_chip.ngpio = pctl->pctl_desc.npins;
+       pctl->gpio_chip.can_sleep = true;
+       pctl->gpio_chip.of_node = np;
+       pctl->gpio_chip.need_valid_mask = true;
+
+       ret = devm_gpiochip_add_data(pctl->dev, &pctl->gpio_chip, pctl);
+       if (ret) {
+               dev_err(pctl->dev, "gpio_chip registration failed\n");
+               return ret;
+       }
+
+       ret = stmfx_pinctrl_gpio_function_enable(pctl);
+       if (ret)
+               return ret;
+
+       pctl->irq_chip.name = dev_name(pctl->dev);
+       pctl->irq_chip.irq_mask = stmfx_pinctrl_irq_mask;
+       pctl->irq_chip.irq_unmask = stmfx_pinctrl_irq_unmask;
+       pctl->irq_chip.irq_set_type = stmfx_pinctrl_irq_set_type;
+       pctl->irq_chip.irq_bus_lock = stmfx_pinctrl_irq_bus_lock;
+       pctl->irq_chip.irq_bus_sync_unlock = stmfx_pinctrl_irq_bus_sync_unlock;
+       for_each_clear_bit(n, &pctl->gpio_valid_mask, pctl->gpio_chip.ngpio)
+               clear_bit(n, pctl->gpio_chip.valid_mask);
+
+       ret = gpiochip_irqchip_add_nested(&pctl->gpio_chip, &pctl->irq_chip,
+                                         0, handle_bad_irq, IRQ_TYPE_NONE);
+       if (ret) {
+               dev_err(pctl->dev, "cannot add irqchip to gpiochip\n");
+               return ret;
+       }
+
+       ret = devm_request_threaded_irq(pctl->dev, irq, NULL,
+                                       stmfx_pinctrl_irq_thread_fn,
+                                       IRQF_ONESHOT,
+                                       pctl->irq_chip.name, pctl);
+       if (ret) {
+               dev_err(pctl->dev, "cannot request irq%d\n", irq);
+               return ret;
+       }
+
+       gpiochip_set_nested_irqchip(&pctl->gpio_chip, &pctl->irq_chip, irq);
+
+       dev_info(pctl->dev,
+                "%ld GPIOs available\n", hweight_long(pctl->gpio_valid_mask));
+
+       return 0;
+}
+
+static int stmfx_pinctrl_remove(struct platform_device *pdev)
+{
+       struct stmfx *stmfx = dev_get_platdata(&pdev->dev);
+
+       return stmfx_function_disable(stmfx,
+                                     STMFX_FUNC_GPIO |
+                                     STMFX_FUNC_ALTGPIO_LOW |
+                                     STMFX_FUNC_ALTGPIO_HIGH);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int stmfx_pinctrl_backup_regs(struct stmfx_pinctrl *pctl)
+{
+       int ret;
+
+       ret = regmap_bulk_read(pctl->stmfx->map, STMFX_REG_GPIO_STATE,
+                              &pctl->bkp_gpio_state, NR_GPIO_REGS);
+       if (ret)
+               return ret;
+       ret = regmap_bulk_read(pctl->stmfx->map, STMFX_REG_GPIO_DIR,
+                              &pctl->bkp_gpio_dir, NR_GPIO_REGS);
+       if (ret)
+               return ret;
+       ret = regmap_bulk_read(pctl->stmfx->map, STMFX_REG_GPIO_TYPE,
+                              &pctl->bkp_gpio_type, NR_GPIO_REGS);
+       if (ret)
+               return ret;
+       ret = regmap_bulk_read(pctl->stmfx->map, STMFX_REG_GPIO_PUPD,
+                              &pctl->bkp_gpio_pupd, NR_GPIO_REGS);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int stmfx_pinctrl_restore_regs(struct stmfx_pinctrl *pctl)
+{
+       int ret;
+
+       ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_GPIO_DIR,
+                               pctl->bkp_gpio_dir, NR_GPIO_REGS);
+       if (ret)
+               return ret;
+       ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_GPIO_TYPE,
+                               pctl->bkp_gpio_type, NR_GPIO_REGS);
+       if (ret)
+               return ret;
+       ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_GPIO_PUPD,
+                               pctl->bkp_gpio_pupd, NR_GPIO_REGS);
+       if (ret)
+               return ret;
+       ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_GPO_SET,
+                               pctl->bkp_gpio_state, NR_GPIO_REGS);
+       if (ret)
+               return ret;
+       ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_EVT,
+                               pctl->irq_gpi_evt, NR_GPIO_REGS);
+       if (ret)
+               return ret;
+       ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_TYPE,
+                               pctl->irq_gpi_type, NR_GPIO_REGS);
+       if (ret)
+               return ret;
+       ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_SRC,
+                               pctl->irq_gpi_src, NR_GPIO_REGS);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int stmfx_pinctrl_suspend(struct device *dev)
+{
+       struct stmfx_pinctrl *pctl = dev_get_drvdata(dev);
+       int ret;
+
+       ret = stmfx_pinctrl_backup_regs(pctl);
+       if (ret) {
+               dev_err(pctl->dev, "registers backup failure\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int stmfx_pinctrl_resume(struct device *dev)
+{
+       struct stmfx_pinctrl *pctl = dev_get_drvdata(dev);
+       int ret;
+
+       ret = stmfx_pinctrl_restore_regs(pctl);
+       if (ret) {
+               dev_err(pctl->dev, "registers restoration failure\n");
+               return ret;
+       }
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(stmfx_pinctrl_dev_pm_ops,
+                        stmfx_pinctrl_suspend, stmfx_pinctrl_resume);
+
+static const struct of_device_id stmfx_pinctrl_of_match[] = {
+       { .compatible = "st,stmfx-0300-pinctrl", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, stmfx_pinctrl_of_match);
+
+static struct platform_driver stmfx_pinctrl_driver = {
+       .driver = {
+               .name = "stmfx-pinctrl",
+               .of_match_table = stmfx_pinctrl_of_match,
+               .pm = &stmfx_pinctrl_dev_pm_ops,
+       },
+       .probe = stmfx_pinctrl_probe,
+       .remove = stmfx_pinctrl_remove,
+};
+module_platform_driver(stmfx_pinctrl_driver);
+
+MODULE_DESCRIPTION("STMFX pinctrl/GPIO driver");
+MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/altera-sysmgr.h b/include/linux/mfd/altera-sysmgr.h
new file mode 100644 (file)
index 0000000..b1ef11a
--- /dev/null
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018-2019 Intel Corporation
+ * Copyright (C) 2012 Freescale Semiconductor, Inc.
+ * Copyright (C) 2012 Linaro Ltd.
+ */
+
+#ifndef __LINUX_MFD_ALTERA_SYSMGR_H__
+#define __LINUX_MFD_ALTERA_SYSMGR_H__
+
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/firmware/intel/stratix10-smc.h>
+
+struct device_node;
+
+#ifdef CONFIG_MFD_ALTERA_SYSMGR
+struct regmap *altr_sysmgr_regmap_lookup_by_phandle(struct device_node *np,
+                                                   const char *property);
+#else
+static inline struct regmap *
+altr_sysmgr_regmap_lookup_by_phandle(struct device_node *np,
+                                    const char *property)
+{
+       return ERR_PTR(-ENOTSUPP);
+}
+#endif
+
+#endif /* __LINUX_MFD_ALTERA_SYSMGR_H__ */
index ad2a9a852aead143a69e7ada265e5b7832f3727f..82407fe85ca26144514a12327bcd695f0404ddd5 100644 (file)
 #define MAX77620_FPS_PERIOD_MIN_US             40
 #define MAX20024_FPS_PERIOD_MIN_US             20
 
-#define MAX77620_FPS_PERIOD_MAX_US             2560
-#define MAX20024_FPS_PERIOD_MAX_US             5120
+#define MAX20024_FPS_PERIOD_MAX_US             2560
+#define MAX77620_FPS_PERIOD_MAX_US             5120
 
 #define MAX77620_REG_FPS_GPIO1                 0x54
 #define MAX77620_REG_FPS_GPIO2                 0x55
@@ -324,6 +324,7 @@ enum max77620_fps_src {
 enum max77620_chip_id {
        MAX77620,
        MAX20024,
+       MAX77663,
 };
 
 struct max77620_chip {
diff --git a/include/linux/mfd/stmfx.h b/include/linux/mfd/stmfx.h
new file mode 100644 (file)
index 0000000..d890595
--- /dev/null
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 STMicroelectronics
+ * Author(s): Amelie Delaunay <amelie.delaunay@st.com>.
+ */
+
+#ifndef MFD_STMFX_H
+#define MFX_STMFX_H
+
+#include <linux/regmap.h>
+
+/* General */
+#define STMFX_REG_CHIP_ID              0x00 /* R */
+#define STMFX_REG_FW_VERSION_MSB       0x01 /* R */
+#define STMFX_REG_FW_VERSION_LSB       0x02 /* R */
+#define STMFX_REG_SYS_CTRL             0x40 /* RW */
+/* IRQ output management */
+#define STMFX_REG_IRQ_OUT_PIN          0x41 /* RW */
+#define STMFX_REG_IRQ_SRC_EN           0x42 /* RW */
+#define STMFX_REG_IRQ_PENDING          0x08 /* R */
+#define STMFX_REG_IRQ_ACK              0x44 /* RW */
+/* GPIO management */
+#define STMFX_REG_IRQ_GPI_PENDING1     0x0C /* R */
+#define STMFX_REG_IRQ_GPI_PENDING2     0x0D /* R */
+#define STMFX_REG_IRQ_GPI_PENDING3     0x0E /* R */
+#define STMFX_REG_GPIO_STATE1          0x10 /* R */
+#define STMFX_REG_GPIO_STATE2          0x11 /* R */
+#define STMFX_REG_GPIO_STATE3          0x12 /* R */
+#define STMFX_REG_IRQ_GPI_SRC1         0x48 /* RW */
+#define STMFX_REG_IRQ_GPI_SRC2         0x49 /* RW */
+#define STMFX_REG_IRQ_GPI_SRC3         0x4A /* RW */
+#define STMFX_REG_IRQ_GPI_EVT1         0x4C /* RW */
+#define STMFX_REG_IRQ_GPI_EVT2         0x4D /* RW */
+#define STMFX_REG_IRQ_GPI_EVT3         0x4E /* RW */
+#define STMFX_REG_IRQ_GPI_TYPE1                0x50 /* RW */
+#define STMFX_REG_IRQ_GPI_TYPE2                0x51 /* RW */
+#define STMFX_REG_IRQ_GPI_TYPE3                0x52 /* RW */
+#define STMFX_REG_IRQ_GPI_ACK1         0x54 /* RW */
+#define STMFX_REG_IRQ_GPI_ACK2         0x55 /* RW */
+#define STMFX_REG_IRQ_GPI_ACK3         0x56 /* RW */
+#define STMFX_REG_GPIO_DIR1            0x60 /* RW */
+#define STMFX_REG_GPIO_DIR2            0x61 /* RW */
+#define STMFX_REG_GPIO_DIR3            0x62 /* RW */
+#define STMFX_REG_GPIO_TYPE1           0x64 /* RW */
+#define STMFX_REG_GPIO_TYPE2           0x65 /* RW */
+#define STMFX_REG_GPIO_TYPE3           0x66 /* RW */
+#define STMFX_REG_GPIO_PUPD1           0x68 /* RW */
+#define STMFX_REG_GPIO_PUPD2           0x69 /* RW */
+#define STMFX_REG_GPIO_PUPD3           0x6A /* RW */
+#define STMFX_REG_GPO_SET1             0x6C /* RW */
+#define STMFX_REG_GPO_SET2             0x6D /* RW */
+#define STMFX_REG_GPO_SET3             0x6E /* RW */
+#define STMFX_REG_GPO_CLR1             0x70 /* RW */
+#define STMFX_REG_GPO_CLR2             0x71 /* RW */
+#define STMFX_REG_GPO_CLR3             0x72 /* RW */
+
+#define STMFX_REG_MAX                  0xB0
+
+/* MFX boot time is around 10ms, so after reset, we have to wait this delay */
+#define STMFX_BOOT_TIME_MS 10
+
+/* STMFX_REG_CHIP_ID bitfields */
+#define STMFX_REG_CHIP_ID_MASK         GENMASK(7, 0)
+
+/* STMFX_REG_SYS_CTRL bitfields */
+#define STMFX_REG_SYS_CTRL_GPIO_EN     BIT(0)
+#define STMFX_REG_SYS_CTRL_TS_EN       BIT(1)
+#define STMFX_REG_SYS_CTRL_IDD_EN      BIT(2)
+#define STMFX_REG_SYS_CTRL_ALTGPIO_EN  BIT(3)
+#define STMFX_REG_SYS_CTRL_SWRST       BIT(7)
+
+/* STMFX_REG_IRQ_OUT_PIN bitfields */
+#define STMFX_REG_IRQ_OUT_PIN_TYPE     BIT(0) /* 0-OD 1-PP */
+#define STMFX_REG_IRQ_OUT_PIN_POL      BIT(1) /* 0-active LOW 1-active HIGH */
+
+/* STMFX_REG_IRQ_(SRC_EN/PENDING/ACK) bit shift */
+enum stmfx_irqs {
+       STMFX_REG_IRQ_SRC_EN_GPIO = 0,
+       STMFX_REG_IRQ_SRC_EN_IDD,
+       STMFX_REG_IRQ_SRC_EN_ERROR,
+       STMFX_REG_IRQ_SRC_EN_TS_DET,
+       STMFX_REG_IRQ_SRC_EN_TS_NE,
+       STMFX_REG_IRQ_SRC_EN_TS_TH,
+       STMFX_REG_IRQ_SRC_EN_TS_FULL,
+       STMFX_REG_IRQ_SRC_EN_TS_OVF,
+       STMFX_REG_IRQ_SRC_MAX,
+};
+
+enum stmfx_functions {
+       STMFX_FUNC_GPIO         = BIT(0), /* GPIO[15:0] */
+       STMFX_FUNC_ALTGPIO_LOW  = BIT(1), /* aGPIO[3:0] */
+       STMFX_FUNC_ALTGPIO_HIGH = BIT(2), /* aGPIO[7:4] */
+       STMFX_FUNC_TS           = BIT(3),
+       STMFX_FUNC_IDD          = BIT(4),
+};
+
+/**
+ * struct stmfx_ddata - STMFX MFD structure
+ * @device:            device reference used for logs
+ * @map:               register map
+ * @vdd:               STMFX power supply
+ * @irq_domain:                IRQ domain
+ * @lock:              IRQ bus lock
+ * @irq_src:           cache of IRQ_SRC_EN register for bus_lock
+ * @bkp_sysctrl:       backup of SYS_CTRL register for suspend/resume
+ * @bkp_irqoutpin:     backup of IRQ_OUT_PIN register for suspend/resume
+ */
+struct stmfx {
+       struct device *dev;
+       struct regmap *map;
+       struct regulator *vdd;
+       struct irq_domain *irq_domain;
+       struct mutex lock; /* IRQ bus lock */
+       u8 irq_src;
+#ifdef CONFIG_PM
+       u8 bkp_sysctrl;
+       u8 bkp_irqoutpin;
+#endif
+};
+
+int stmfx_function_enable(struct stmfx *stmfx, u32 func);
+int stmfx_function_disable(struct stmfx *stmfx, u32 func);
+#endif
index 2125c7c02818b3c5c30eff67111e6e512ccfb065..f09510561a5555929c7b7e4e4f1d1b7bdb170e4a 100644 (file)
 
 #include <linux/bitops.h>
 
-/* 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)
index 1ef51ed36be56a82e20bfce286faa0c22244661c..7762c1bce55df9710e5abfd6da0d18add354b12e 100644 (file)
@@ -22,7 +22,6 @@
 #define LMU_EVENT_MONITOR_DONE         0x01
 
 enum ti_lmu_id {
-       LM3532,
        LM3631,
        LM3632,
        LM3633,