diff options
39 files changed, 2534 insertions, 92 deletions
diff --git a/Documentation/devicetree/bindings/arm/altera/socfpga-system.txt b/Documentation/devicetree/bindings/arm/altera/socfpga-system.txt index f4d04a067282..82edbaaa3f85 100644 --- a/Documentation/devicetree/bindings/arm/altera/socfpga-system.txt +++ b/Documentation/devicetree/bindings/arm/altera/socfpga-system.txt @@ -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/input/max77650-onkey.txt b/Documentation/devicetree/bindings/input/max77650-onkey.txt new file mode 100644 index 000000000000..477dc74f452a --- /dev/null +++ b/Documentation/devicetree/bindings/input/max77650-onkey.txt @@ -0,0 +1,26 @@ +Onkey driver for MAX77650 PMIC from Maxim Integrated. + +This module is part of the MAX77650 MFD device. For more details +see Documentation/devicetree/bindings/mfd/max77650.txt. + +The onkey controller is represented as a sub-node of the PMIC node on +the device tree. + +Required properties: +-------------------- +- compatible: Must be "maxim,max77650-onkey". + +Optional properties: +- linux,code: The key-code to be reported when the key is pressed. + Defaults to KEY_POWER. +- maxim,onkey-slide: The system's button is a slide switch, not the default + push button. + +Example: +-------- + + onkey { + compatible = "maxim,max77650-onkey"; + linux,code = <KEY_END>; + maxim,onkey-slide; + }; diff --git a/Documentation/devicetree/bindings/leds/leds-lm3532.txt b/Documentation/devicetree/bindings/leds/leds-lm3532.txt new file mode 100644 index 000000000000..c087f85ddddc --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-lm3532.txt @@ -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 diff --git a/Documentation/devicetree/bindings/leds/leds-max77650.txt b/Documentation/devicetree/bindings/leds/leds-max77650.txt new file mode 100644 index 000000000000..3a67115cc1da --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-max77650.txt @@ -0,0 +1,57 @@ +LED driver for MAX77650 PMIC from Maxim Integrated. + +This module is part of the MAX77650 MFD device. For more details +see Documentation/devicetree/bindings/mfd/max77650.txt. + +The LED controller is represented as a sub-node of the PMIC node on +the device tree. + +This device has three current sinks. + +Required properties: +-------------------- +- compatible: Must be "maxim,max77650-led" +- #address-cells: Must be <1>. +- #size-cells: Must be <0>. + +Each LED is represented as a sub-node of the LED-controller node. Up to +three sub-nodes can be defined. + +Required properties of the sub-node: +------------------------------------ + +- reg: Must be <0>, <1> or <2>. + +Optional properties of the sub-node: +------------------------------------ + +- label: See Documentation/devicetree/bindings/leds/common.txt +- linux,default-trigger: See Documentation/devicetree/bindings/leds/common.txt + +For more details, please refer to the generic GPIO DT binding document +<devicetree/bindings/gpio/gpio.txt>. + +Example: +-------- + + leds { + compatible = "maxim,max77650-led"; + #address-cells = <1>; + #size-cells = <0>; + + led@0 { + reg = <0>; + label = "blue:usr0"; + }; + + led@1 { + reg = <1>; + label = "red:usr1"; + linux,default-trigger = "heartbeat"; + }; + + led@2 { + reg = <2>; + label = "green:usr2"; + }; + }; diff --git a/Documentation/devicetree/bindings/mfd/max77620.txt b/Documentation/devicetree/bindings/mfd/max77620.txt index 9c16d51cc15b..5a642a51d58e 100644 --- a/Documentation/devicetree/bindings/mfd/max77620.txt +++ b/Documentation/devicetree/bindings/mfd/max77620.txt @@ -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/max77650.txt b/Documentation/devicetree/bindings/mfd/max77650.txt new file mode 100644 index 000000000000..b529d8d19335 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/max77650.txt @@ -0,0 +1,46 @@ +MAX77650 ultra low-power PMIC from Maxim Integrated. + +Required properties: +------------------- +- compatible: Must be "maxim,max77650" +- reg: I2C device address. +- interrupts: The interrupt on the parent the controller is + connected to. +- interrupt-controller: Marks the device node as an interrupt controller. +- #interrupt-cells: Must be <2>. + +- gpio-controller: Marks the device node as a gpio controller. +- #gpio-cells: Must be <2>. The first cell is the pin number and + the second cell is used to specify the gpio active + state. + +Optional properties: +-------------------- +gpio-line-names: Single string containing the name of the GPIO line. + +The GPIO-controller module is represented as part of the top-level PMIC +node. The device exposes a single GPIO line. + +For device-tree bindings of other sub-modules (regulator, power supply, +LEDs and onkey) refer to the binding documents under the respective +sub-system directories. + +For more details on GPIO bindings, please refer to the generic GPIO DT +binding document <devicetree/bindings/gpio/gpio.txt>. + +Example: +-------- + + pmic@48 { + compatible = "maxim,max77650"; + reg = <0x48>; + + interrupt-controller; + interrupt-parent = <&gpio2>; + #interrupt-cells = <2>; + interrupts = <3 IRQ_TYPE_LEVEL_LOW>; + + gpio-controller; + #gpio-cells = <2>; + gpio-line-names = "max77650-charger"; + }; diff --git a/Documentation/devicetree/bindings/mfd/ti-lmu.txt b/Documentation/devicetree/bindings/mfd/ti-lmu.txt index c885cf89b8ce..980394d701a7 100644 --- a/Documentation/devicetree/bindings/mfd/ti-lmu.txt +++ b/Documentation/devicetree/bindings/mfd/ti-lmu.txt @@ -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/power/supply/max77650-charger.txt b/Documentation/devicetree/bindings/power/supply/max77650-charger.txt new file mode 100644 index 000000000000..e6d0fb6ff94e --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/max77650-charger.txt @@ -0,0 +1,28 @@ +Battery charger driver for MAX77650 PMIC from Maxim Integrated. + +This module is part of the MAX77650 MFD device. For more details +see Documentation/devicetree/bindings/mfd/max77650.txt. + +The charger is represented as a sub-node of the PMIC node on the device tree. + +Required properties: +-------------------- +- compatible: Must be "maxim,max77650-charger" + +Optional properties: +-------------------- +- input-voltage-min-microvolt: Minimum CHGIN regulation voltage. Must be one + of: 4000000, 4100000, 4200000, 4300000, + 4400000, 4500000, 4600000, 4700000. +- input-current-limit-microamp: CHGIN input current limit (in microamps). Must + be one of: 95000, 190000, 285000, 380000, + 475000. + +Example: +-------- + + charger { + compatible = "maxim,max77650-charger"; + input-voltage-min-microvolt = <4200000>; + input-current-limit-microamp = <285000>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 2c2fce72e694..1effe9789023 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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 @@ -9408,6 +9414,20 @@ S: Maintained F: Documentation/devicetree/bindings/sound/max9860.txt F: sound/soc/codecs/max9860.* +MAXIM MAX77650 PMIC MFD DRIVER +M: Bartosz Golaszewski <bgolaszewski@baylibre.com> +L: linux-kernel@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/*/*max77650.txt +F: Documentation/devicetree/bindings/*/max77650*.txt +F: include/linux/mfd/max77650.h +F: drivers/mfd/max77650.c +F: drivers/regulator/max77650-regulator.c +F: drivers/power/supply/max77650-charger.c +F: drivers/input/misc/max77650-onkey.c +F: drivers/leds/leds-max77650.c +F: drivers/gpio/gpio-max77650.c + MAXIM MAX77802 PMIC REGULATOR DEVICE DRIVER M: Javier Martinez Canillas <javier@dowhile0.org> L: linux-kernel@vger.kernel.org diff --git a/arch/arm/boot/dts/omap4-droid4-xt894.dts b/arch/arm/boot/dts/omap4-droid4-xt894.dts index e21ec929f096..714863f8f261 100644 --- a/arch/arm/boot/dts/omap4-droid4-xt894.dts +++ b/arch/arm/boot/dts/omap4-droid4-xt894.dts @@ -214,7 +214,6 @@ width-mm = <50>; height-mm = <89>; - backlight = <&lcd_backlight>; panel-timing { clock-frequency = <0>; /* Calculated by dsi */ @@ -383,20 +382,30 @@ }; &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"; }; }; }; diff --git a/arch/arm/configs/socfpga_defconfig b/arch/arm/configs/socfpga_defconfig index 08d1b3e11d68..2d29992a60d2 100644 --- a/arch/arm/configs/socfpga_defconfig +++ b/arch/arm/configs/socfpga_defconfig @@ -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 diff --git a/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi b/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi index cd7c76e58b09..301958430149 100644 --- a/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi +++ b/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi @@ -393,7 +393,7 @@ }; sysmgr: sysmgr@ffd12000 { - compatible = "altr,sys-mgr", "syscon"; + compatible = "altr,sys-mgr-s10","altr,sys-mgr"; reg = <0xffd12000 0x228>; }; diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 2d9c39033c1a..9e0b7ab639b3 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -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 diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 3f50526a771f..c4f912104440 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1112,6 +1112,13 @@ config GPIO_MAX77620 driver also provides interrupt support for each of the gpios. Say yes here to enable the max77620 to be used as gpio controller. +config GPIO_MAX77650 + tristate "Maxim MAX77650/77651 GPIO support" + depends on MFD_MAX77650 + help + GPIO driver for MAX77650/77651 PMIC from Maxim Semiconductor. + These chips have a single pin that can be configured as GPIO. + config GPIO_MSIC bool "Intel MSIC mixed signal gpio support" depends on (X86 || COMPILE_TEST) && MFD_INTEL_MSIC diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 54d55274b93a..075722d8317d 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o obj-$(CONFIG_GPIO_MAX7301) += gpio-max7301.o obj-$(CONFIG_GPIO_MAX732X) += gpio-max732x.o obj-$(CONFIG_GPIO_MAX77620) += gpio-max77620.o +obj-$(CONFIG_GPIO_MAX77650) += gpio-max77650.o obj-$(CONFIG_GPIO_MB86S7X) += gpio-mb86s7x.o obj-$(CONFIG_GPIO_MENZ127) += gpio-menz127.o obj-$(CONFIG_GPIO_MERRIFIELD) += gpio-merrifield.o diff --git a/drivers/gpio/gpio-max77650.c b/drivers/gpio/gpio-max77650.c new file mode 100644 index 000000000000..3f03f4e8956c --- /dev/null +++ b/drivers/gpio/gpio-max77650.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2018 BayLibre SAS +// Author: Bartosz Golaszewski <bgolaszewski@baylibre.com> +// +// GPIO driver for MAXIM 77650/77651 charger/power-supply. + +#include <linux/gpio/driver.h> +#include <linux/i2c.h> +#include <linux/mfd/max77650.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define MAX77650_GPIO_DIR_MASK BIT(0) +#define MAX77650_GPIO_INVAL_MASK BIT(1) +#define MAX77650_GPIO_DRV_MASK BIT(2) +#define MAX77650_GPIO_OUTVAL_MASK BIT(3) +#define MAX77650_GPIO_DEBOUNCE_MASK BIT(4) + +#define MAX77650_GPIO_DIR_OUT 0x00 +#define MAX77650_GPIO_DIR_IN BIT(0) +#define MAX77650_GPIO_OUT_LOW 0x00 +#define MAX77650_GPIO_OUT_HIGH BIT(3) +#define MAX77650_GPIO_DRV_OPEN_DRAIN 0x00 +#define MAX77650_GPIO_DRV_PUSH_PULL BIT(2) +#define MAX77650_GPIO_DEBOUNCE BIT(4) + +#define MAX77650_GPIO_DIR_BITS(_reg) \ + ((_reg) & MAX77650_GPIO_DIR_MASK) +#define MAX77650_GPIO_INVAL_BITS(_reg) \ + (((_reg) & MAX77650_GPIO_INVAL_MASK) >> 1) + +struct max77650_gpio_chip { + struct regmap *map; + struct gpio_chip gc; + int irq; +}; + +static int max77650_gpio_direction_input(struct gpio_chip *gc, + unsigned int offset) +{ + struct max77650_gpio_chip *chip = gpiochip_get_data(gc); + + return regmap_update_bits(chip->map, + MAX77650_REG_CNFG_GPIO, + MAX77650_GPIO_DIR_MASK, + MAX77650_GPIO_DIR_IN); +} + +static int max77650_gpio_direction_output(struct gpio_chip *gc, + unsigned int offset, int value) +{ + struct max77650_gpio_chip *chip = gpiochip_get_data(gc); + int mask, regval; + + mask = MAX77650_GPIO_DIR_MASK | MAX77650_GPIO_OUTVAL_MASK; + regval = value ? MAX77650_GPIO_OUT_HIGH : MAX77650_GPIO_OUT_LOW; + regval |= MAX77650_GPIO_DIR_OUT; + + return regmap_update_bits(chip->map, + MAX77650_REG_CNFG_GPIO, mask, regval); +} + +static void max77650_gpio_set_value(struct gpio_chip *gc, + unsigned int offset, int value) +{ + struct max77650_gpio_chip *chip = gpiochip_get_data(gc); + int rv, regval; + + regval = value ? MAX77650_GPIO_OUT_HIGH : MAX77650_GPIO_OUT_LOW; + + rv = regmap_update_bits(chip->map, MAX77650_REG_CNFG_GPIO, + MAX77650_GPIO_OUTVAL_MASK, regval); + if (rv) + dev_err(gc->parent, "cannot set GPIO value: %d\n", rv); +} + +static int max77650_gpio_get_value(struct gpio_chip *gc, + unsigned int offset) +{ + struct max77650_gpio_chip *chip = gpiochip_get_data(gc); + unsigned int val; + int rv; + + rv = regmap_read(chip->map, MAX77650_REG_CNFG_GPIO, &val); + if (rv) + return rv; + + return MAX77650_GPIO_INVAL_BITS(val); +} + +static int max77650_gpio_get_direction(struct gpio_chip *gc, + unsigned int offset) +{ + struct max77650_gpio_chip *chip = gpiochip_get_data(gc); + unsigned int val; + int rv; + + rv = regmap_read(chip->map, MAX77650_REG_CNFG_GPIO, &val); + if (rv) + return rv; + + return MAX77650_GPIO_DIR_BITS(val); +} + +static int max77650_gpio_set_config(struct gpio_chip *gc, + unsigned int offset, unsigned long cfg) +{ + struct max77650_gpio_chip *chip = gpiochip_get_data(gc); + + switch (pinconf_to_config_param(cfg)) { + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + return regmap_update_bits(chip->map, + MAX77650_REG_CNFG_GPIO, + MAX77650_GPIO_DRV_MASK, + MAX77650_GPIO_DRV_OPEN_DRAIN); + case PIN_CONFIG_DRIVE_PUSH_PULL: + return regmap_update_bits(chip->map, + MAX77650_REG_CNFG_GPIO, + MAX77650_GPIO_DRV_MASK, + MAX77650_GPIO_DRV_PUSH_PULL); + case PIN_CONFIG_INPUT_DEBOUNCE: + return regmap_update_bits(chip->map, + MAX77650_REG_CNFG_GPIO, + MAX77650_GPIO_DEBOUNCE_MASK, + MAX77650_GPIO_DEBOUNCE); + default: + return -ENOTSUPP; + } +} + +static int max77650_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) +{ + struct max77650_gpio_chip *chip = gpiochip_get_data(gc); + + return chip->irq; +} + +static int max77650_gpio_probe(struct platform_device *pdev) +{ + struct max77650_gpio_chip *chip; + struct device *dev, *parent; + struct i2c_client *i2c; + + dev = &pdev->dev; + parent = dev->parent; + i2c = to_i2c_client(parent); + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->map = dev_get_regmap(parent, NULL); + if (!chip->map) + return -ENODEV; + + chip->irq = platform_get_irq_byname(pdev, "GPI"); + if (chip->irq < 0) + return chip->irq; + + chip->gc.base = -1; + chip->gc.ngpio = 1; + chip->gc.label = i2c->name; + chip->gc.parent = dev; + chip->gc.owner = THIS_MODULE; + chip->gc.can_sleep = true; + + chip->gc.direction_input = max77650_gpio_direction_input; + chip->gc.direction_output = max77650_gpio_direction_output; + chip->gc.set = max77650_gpio_set_value; + chip->gc.get = max77650_gpio_get_value; + chip->gc.get_direction = max77650_gpio_get_direction; + chip->gc.set_config = max77650_gpio_set_config; + chip->gc.to_irq = max77650_gpio_to_irq; + + return devm_gpiochip_add_data(dev, &chip->gc, chip); +} + +static struct platform_driver max77650_gpio_driver = { + .driver = { + .name = "max77650-gpio", + }, + .probe = max77650_gpio_probe, +}; +module_platform_driver(max77650_gpio_driver); + +MODULE_DESCRIPTION("MAXIM 77650/77651 GPIO driver"); +MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index e15ed1bb8558..85bc675eecd3 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -190,6 +190,15 @@ config INPUT_M68K_BEEP tristate "M68k Beeper support" depends on M68K +config INPUT_MAX77650_ONKEY + tristate "Maxim MAX77650 ONKEY support" + depends on MFD_MAX77650 + help + Support the ONKEY of the MAX77650 PMIC as an input device. + + To compile this driver as a module, choose M here: the module + will be called max77650-onkey. + config INPUT_MAX77693_HAPTIC tristate "MAXIM MAX77693/MAX77843 haptic controller support" depends on (MFD_MAX77693 || MFD_MAX77843) && PWM diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index b936c5b1d4ac..ffd72161c79b 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o +obj-$(CONFIG_INPUT_MAX77650_ONKEY) += max77650-onkey.o obj-$(CONFIG_INPUT_MAX77693_HAPTIC) += max77693-haptic.o obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o diff --git a/drivers/input/misc/max77650-onkey.c b/drivers/input/misc/max77650-onkey.c new file mode 100644 index 000000000000..fbf6caab7217 --- /dev/null +++ b/drivers/input/misc/max77650-onkey.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2018 BayLibre SAS +// Author: Bartosz Golaszewski <bgolaszewski@baylibre.com> +// +// ONKEY driver for MAXIM 77650/77651 charger/power-supply. + +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/mfd/max77650.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define MAX77650_ONKEY_MODE_MASK BIT(3) +#define MAX77650_ONKEY_MODE_PUSH 0x00 +#define MAX77650_ONKEY_MODE_SLIDE BIT(3) + +struct max77650_onkey { + struct input_dev *input; + unsigned int code; +}; + +static irqreturn_t max77650_onkey_falling(int irq, void *data) +{ + struct max77650_onkey *onkey = data; + + input_report_key(onkey->input, onkey->code, 0); + input_sync(onkey->input); + + return IRQ_HANDLED; +} + +static irqreturn_t max77650_onkey_rising(int irq, void *data) +{ + struct max77650_onkey *onkey = data; + + input_report_key(onkey->input, onkey->code, 1); + input_sync(onkey->input); + + return IRQ_HANDLED; +} + +static int max77650_onkey_probe(struct platform_device *pdev) +{ + int irq_r, irq_f, error, mode; + struct max77650_onkey *onkey; + struct device *dev, *parent; + struct regmap *map; + unsigned int type; + + dev = &pdev->dev; + parent = dev->parent; + + map = dev_get_regmap(parent, NULL); + if (!map) + return -ENODEV; + + onkey = devm_kzalloc(dev, sizeof(*onkey), GFP_KERNEL); + if (!onkey) + return -ENOMEM; + + error = device_property_read_u32(dev, "linux,code", &onkey->code); + if (error) + onkey->code = KEY_POWER; + + if (device_property_read_bool(dev, "maxim,onkey-slide")) { + mode = MAX77650_ONKEY_MODE_SLIDE; + type = EV_SW; + } else { + mode = MAX77650_ONKEY_MODE_PUSH; + type = EV_KEY; + } + + error = regmap_update_bits(map, MAX77650_REG_CNFG_GLBL, + MAX77650_ONKEY_MODE_MASK, mode); + if (error) + return error; + + irq_f = platform_get_irq_byname(pdev, "nEN_F"); + if (irq_f < 0) + return irq_f; + + irq_r = platform_get_irq_byname(pdev, "nEN_R"); + if (irq_r < 0) + return irq_r; + + onkey->input = devm_input_allocate_device(dev); + if (!onkey->input) + return -ENOMEM; + + onkey->input->name = "max77650_onkey"; + onkey->input->phys = "max77650_onkey/input0"; + onkey->input->id.bustype = BUS_I2C; + input_set_capability(onkey->input, type, onkey->code); + + error = devm_request_any_context_irq(dev, irq_f, max77650_onkey_falling, + IRQF_ONESHOT, "onkey-down", onkey); + if (error < 0) + return error; + + error = devm_request_any_context_irq(dev, irq_r, max77650_onkey_rising, + IRQF_ONESHOT, "onkey-up", onkey); + if (error < 0) + return error; + + return input_register_device(onkey->input); +} + +static struct platform_driver max77650_onkey_driver = { + .driver = { + .name = "max77650-onkey", + }, + .probe = max77650_onkey_probe, +}; +module_platform_driver(max77650_onkey_driver); + +MODULE_DESCRIPTION("MAXIM 77650/77651 ONKEY driver"); +MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index a72f97fca57b..45914c66c852 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -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 @@ -608,6 +618,12 @@ config LEDS_TLC591XX This option enables support for Texas Instruments TLC59108 and TLC59116 LED controllers. +config LEDS_MAX77650 + tristate "LED support for Maxim MAX77650 PMIC" + depends on LEDS_CLASS && MFD_MAX77650 + help + LEDs driver for MAX77650 family of PMICs from Maxim Integrated. + config LEDS_MAX77693 tristate "LED support for MAX77693 Flash" depends on LEDS_CLASS_FLASH diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 4c1b0054f379..1e9702ebffee 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -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 @@ -61,6 +62,7 @@ obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o obj-$(CONFIG_LEDS_NS2) += leds-ns2.o obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o +obj-$(CONFIG_LEDS_MAX77650) += leds-max77650.o obj-$(CONFIG_LEDS_MAX77693) += leds-max77693.o obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o diff --git a/drivers/leds/leds-lm3532.c b/drivers/leds/leds-lm3532.c new file mode 100644 index 000000000000..180895b83b88 --- /dev/null +++ b/drivers/leds/leds-lm3532.c @@ -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>"); diff --git a/drivers/leds/leds-max77650.c b/drivers/leds/leds-max77650.c new file mode 100644 index 000000000000..6b74ce9cac12 --- /dev/null +++ b/drivers/leds/leds-max77650.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2018 BayLibre SAS +// Author: Bartosz Golaszewski <bgolaszewski@baylibre.com> +// +// LED driver for MAXIM 77650/77651 charger/power-supply. + +#include <linux/i2c.h> +#include <linux/leds.h> +#include <linux/mfd/max77650.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define MAX77650_LED_NUM_LEDS 3 + +#define MAX77650_LED_A_BASE 0x40 +#define MAX77650_LED_B_BASE 0x43 + +#define MAX77650_LED_BR_MASK GENMASK(4, 0) +#define MAX77650_LED_EN_MASK GENMASK(7, 6) + +#define MAX77650_LED_MAX_BRIGHTNESS MAX77650_LED_BR_MASK + +/* Enable EN_LED_MSTR. */ +#define MAX77650_LED_TOP_DEFAULT BIT(0) + +#define MAX77650_LED_ENABLE GENMASK(7, 6) +#define MAX77650_LED_DISABLE 0x00 + +#define MAX77650_LED_A_DEFAULT MAX77650_LED_DISABLE +/* 100% on duty */ +#define MAX77650_LED_B_DEFAULT GENMASK(3, 0) + +struct max77650_led { + struct led_classdev cdev; + struct regmap *map; + unsigned int regA; + unsigned int regB; +}; + +static struct max77650_led *max77650_to_led(struct led_classdev *cdev) +{ + return container_of(cdev, struct max77650_led, cdev); +} + +static int max77650_led_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct max77650_led *led = max77650_to_led(cdev); + int val, mask; + + mask = MAX77650_LED_BR_MASK | MAX77650_LED_EN_MASK; + + if (brightness == LED_OFF) + val = MAX77650_LED_DISABLE; + else + val = MAX77650_LED_ENABLE | brightness; + + return regmap_update_bits(led->map, led->regA, mask, val); +} + +static int max77650_led_probe(struct platform_device *pdev) +{ + struct device_node *of_node, *child; + struct max77650_led *leds, *led; + struct device *parent; + struct device *dev; + struct regmap *map; + const char *label; + int rv, num_leds; + u32 reg; + + dev = &pdev->dev; + parent = dev->parent; + of_node = dev->of_node; + + if (!of_node) + return -ENODEV; + + leds = devm_kcalloc(dev, sizeof(*leds), + MAX77650_LED_NUM_LEDS, GFP_KERNEL); + if (!leds) + return -ENOMEM; + + map = dev_get_regmap(dev->parent, NULL); + if (!map) + return -ENODEV; + + num_leds = of_get_child_count(of_node); + if (!num_leds || num_leds > MAX77650_LED_NUM_LEDS) + return -ENODEV; + + for_each_child_of_node(of_node, child) { + rv = of_property_read_u32(child, "reg", ®); + if (rv || reg >= MAX77650_LED_NUM_LEDS) + return -EINVAL; + + led = &leds[reg]; + led->map = map; + led->regA = MAX77650_LED_A_BASE + reg; + led->regB = MAX77650_LED_B_BASE + reg; + led->cdev.brightness_set_blocking = max77650_led_brightness_set; + led->cdev.max_brightness = MAX77650_LED_MAX_BRIGHTNESS; + + label = of_get_property(child, "label", NULL); + if (!label) { + led->cdev.name = "max77650::"; + } else { + led->cdev.name = devm_kasprintf(dev, GFP_KERNEL, + "max77650:%s", label); + if (!led->cdev.name) + return -ENOMEM; + } + + of_property_read_string(child, "linux,default-trigger", + &led->cdev.default_trigger); + + rv = devm_of_led_classdev_register(dev, child, &led->cdev); + if (rv) + return rv; + + rv = regmap_write(map, led->regA, MAX77650_LED_A_DEFAULT); + if (rv) + return rv; + + rv = regmap_write(map, led->regB, MAX77650_LED_B_DEFAULT); + if (rv) + return rv; + } + + return regmap_write(map, + MAX77650_REG_CNFG_LED_TOP, + MAX77650_LED_TOP_DEFAULT); +} + +static struct platform_driver max77650_led_driver = { + .driver = { + .name = "max77650-led", + }, + .probe = max77650_led_probe, +}; +module_platform_driver(max77650_led_driver); + +MODULE_DESCRIPTION("MAXIM 77650/77651 LED driver"); +MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 87c55249dff2..9d53480352a6 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -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 @@ -733,6 +743,20 @@ config MFD_MAX77620 provides common support for accessing the device; additional drivers must be enabled in order to use the functionality of the device. +config MFD_MAX77650 + tristate "Maxim MAX77650/77651 PMIC Support" + depends on I2C + depends on OF || COMPILE_TEST + select MFD_CORE + select REGMAP_I2C + help + Say Y here to add support for Maxim Semiconductor MAX77650 and + MAX77651 Power Management ICs. This is the core multifunction + driver for interacting with the device. The module name is + 'max77650'. Additional drivers can be enabled in order to use + the following functionalities of the device: GPIO, regulator, + charger, LED, onkey. + config MFD_MAX77686 tristate "Maxim Semiconductor MAX77686/802 PMIC Support" depends on I2C diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 614eea87990e..52b1a90ff515 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -155,6 +155,7 @@ obj-$(CONFIG_MFD_DA9150) += da9150-core.o obj-$(CONFIG_MFD_MAX14577) += max14577.o obj-$(CONFIG_MFD_MAX77620) += max77620.o +obj-$(CONFIG_MFD_MAX77650) += max77650.o obj-$(CONFIG_MFD_MAX77686) += max77686.o obj-$(CONFIG_MFD_MAX77693) += max77693.o obj-$(CONFIG_MFD_MAX77843) += max77843.o @@ -237,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 diff --git a/drivers/mfd/altera-sysmgr.c b/drivers/mfd/altera-sysmgr.c new file mode 100644 index 000000000000..8976f82785bb --- /dev/null +++ b/drivers/mfd/altera-sysmgr.c @@ -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"); diff --git a/drivers/mfd/max77620.c b/drivers/mfd/max77620.c index d8ddd1a6f304..436361ce3737 100644 --- a/drivers/mfd/max77620.c +++ b/drivers/mfd/max77620.c @@ -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/max77650.c b/drivers/mfd/max77650.c new file mode 100644 index 000000000000..60e07aca6ae5 --- /dev/null +++ b/drivers/mfd/max77650.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2018 BayLibre SAS +// Author: Bartosz Golaszewski <bgolaszewski@baylibre.com> +// +// Core MFD driver for MAXIM 77650/77651 charger/power-supply. +// Programming manual: https://pdfserv.maximintegrated.com/en/an/AN6428.pdf + +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/mfd/core.h> +#include <linux/mfd/max77650.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> + +#define MAX77650_INT_GPI_F_MSK BIT(0) +#define MAX77650_INT_GPI_R_MSK BIT(1) +#define MAX77650_INT_GPI_MSK \ + (MAX77650_INT_GPI_F_MSK | MAX77650_INT_GPI_R_MSK) +#define MAX77650_INT_nEN_F_MSK BIT(2) +#define MAX77650_INT_nEN_R_MSK BIT(3) +#define MAX77650_INT_TJAL1_R_MSK BIT(4) +#define MAX77650_INT_TJAL2_R_MSK BIT(5) +#define MAX77650_INT_DOD_R_MSK BIT(6) + +#define MAX77650_INT_THM_MSK BIT(0) +#define MAX77650_INT_CHG_MSK BIT(1) +#define MAX77650_INT_CHGIN_MSK BIT(2) +#define MAX77650_INT_TJ_REG_MSK BIT(3) +#define MAX77650_INT_CHGIN_CTRL_MSK BIT(4) +#define MAX77650_INT_SYS_CTRL_MSK BIT(5) +#define MAX77650_INT_SYS_CNFG_MSK BIT(6) + +#define MAX77650_INT_GLBL_OFFSET 0 +#define MAX77650_INT_CHG_OFFSET 1 + +#define MAX77650_SBIA_LPM_MASK BIT(5) +#define MAX77650_SBIA_LPM_DISABLED 0x00 + +enum { + MAX77650_INT_GPI, + MAX77650_INT_nEN_F, + MAX77650_INT_nEN_R, + MAX77650_INT_TJAL1_R, + MAX77650_INT_TJAL2_R, + MAX77650_INT_DOD_R, + MAX77650_INT_THM, + MAX77650_INT_CHG, + MAX77650_INT_CHGIN, + MAX77650_INT_TJ_REG, + MAX77650_INT_CHGIN_CTRL, + MAX77650_INT_SYS_CTRL, + MAX77650_INT_SYS_CNFG, +}; + +static const struct resource max77650_charger_resources[] = { + DEFINE_RES_IRQ_NAMED(MAX77650_INT_CHG, "CHG"), + DEFINE_RES_IRQ_NAMED(MAX77650_INT_CHGIN, "CHGIN"), +}; + +static const struct resource max77650_gpio_resources[] = { + DEFINE_RES_IRQ_NAMED(MAX77650_INT_GPI, "GPI"), +}; + +static const struct resource max77650_onkey_resources[] = { + DEFINE_RES_IRQ_NAMED(MAX77650_INT_nEN_F, "nEN_F"), + DEFINE_RES_IRQ_NAMED(MAX77650_INT_nEN_R, "nEN_R"), +}; + +static const struct mfd_cell max77650_cells[] = { + { + .name = "max77650-regulator", + .of_compatible = "maxim,max77650-regulator", + }, { + .name = "max77650-charger", + .of_compatible = "maxim,max77650-charger", + .resources = max77650_charger_resources, + .num_resources = ARRAY_SIZE(max77650_charger_resources), + }, { + .name = "max77650-gpio", + .of_compatible = "maxim,max77650-gpio", + .resources = max77650_gpio_resources, + .num_resources = ARRAY_SIZE(max77650_gpio_resources), + }, { + .name = "max77650-led", + .of_compatible = "maxim,max77650-led", + }, { + .name = "max77650-onkey", + .of_compatible = "maxim,max77650-onkey", + .resources = max77650_onkey_resources, + .num_resources = ARRAY_SIZE(max77650_onkey_resources), + }, +}; + +static const struct regmap_irq max77650_irqs[] = { + [MAX77650_INT_GPI] = { + .reg_offset = MAX77650_INT_GLBL_OFFSET, + .mask = MAX77650_INT_GPI_MSK, + .type = { + .type_falling_val = MAX77650_INT_GPI_F_MSK, + .type_rising_val = MAX77650_INT_GPI_R_MSK, + .types_supported = IRQ_TYPE_EDGE_BOTH, + }, + }, + REGMAP_IRQ_REG(MAX77650_INT_nEN_F, + MAX77650_INT_GLBL_OFFSET, MAX77650_INT_nEN_F_MSK), + REGMAP_IRQ_REG(MAX77650_INT_nEN_R, + MAX77650_INT_GLBL_OFFSET, MAX77650_INT_nEN_R_MSK), + REGMAP_IRQ_REG(MAX77650_INT_TJAL1_R, + MAX77650_INT_GLBL_OFFSET, MAX77650_INT_TJAL1_R_MSK), + REGMAP_IRQ_REG(MAX77650_INT_TJAL2_R, + MAX77650_INT_GLBL_OFFSET, MAX77650_INT_TJAL2_R_MSK), + REGMAP_IRQ_REG(MAX77650_INT_DOD_R, + MAX77650_INT_GLBL_OFFSET, MAX77650_INT_DOD_R_MSK), + REGMAP_IRQ_REG(MAX77650_INT_THM, + MAX77650_INT_CHG_OFFSET, MAX77650_INT_THM_MSK), + REGMAP_IRQ_REG(MAX77650_INT_CHG, + MAX77650_INT_CHG_OFFSET, MAX77650_INT_CHG_MSK), + REGMAP_IRQ_REG(MAX77650_INT_CHGIN, + MAX77650_INT_CHG_OFFSET, MAX77650_INT_CHGIN_MSK), + REGMAP_IRQ_REG(MAX77650_INT_TJ_REG, + MAX77650_INT_CHG_OFFSET, MAX77650_INT_TJ_REG_MSK), + REGMAP_IRQ_REG(MAX77650_INT_CHGIN_CTRL, + MAX77650_INT_CHG_OFFSET, MAX77650_INT_CHGIN_CTRL_MSK), + REGMAP_IRQ_REG(MAX77650_INT_SYS_CTRL, + MAX77650_INT_CHG_OFFSET, MAX77650_INT_SYS_CTRL_MSK), + REGMAP_IRQ_REG(MAX77650_INT_SYS_CNFG, + MAX77650_INT_CHG_OFFSET, MAX77650_INT_SYS_CNFG_MSK), +}; + +static const struct regmap_irq_chip max77650_irq_chip = { + .name = "max77650-irq", + .irqs = max77650_irqs, + .num_irqs = ARRAY_SIZE(max77650_irqs), + .num_regs = 2, + .status_base = MAX77650_REG_INT_GLBL, + .mask_base = MAX77650_REG_INTM_GLBL, + .type_in_mask = true, + .type_invert = true, + .init_ack_masked = true, + .clear_on_unmask = true, +}; + +static const struct regmap_config max77650_regmap_config = { + .name = "max77650", + .reg_bits = 8, + .val_bits = 8, +}; + +static int max77650_i2c_probe(struct i2c_client *i2c) +{ + struct regmap_irq_chip_data *irq_data; + struct device *dev = &i2c->dev; + struct irq_domain *domain; + struct regmap *map; + unsigned int val; + int rv, id; + + map = devm_regmap_init_i2c(i2c, &max77650_regmap_config); + if (IS_ERR(map)) { + dev_err(dev, "Unable to initialise I2C Regmap\n"); + return PTR_ERR(map); + } + + rv = regmap_read(map, MAX77650_REG_CID, &val); + if (rv) { + dev_err(dev, "Unable to read Chip ID\n"); + return rv; + } + + id = MAX77650_CID_BITS(val); + switch (id) { + case MAX77650_CID_77650A: + case MAX77650_CID_77650C: + case MAX77650_CID_77651A: + case MAX77650_CID_77651B: + break; + default: + dev_err(dev, "Chip not supported - ID: 0x%02x\n", id); + return -ENODEV; + } + + /* + * This IC has a low-power mode which reduces the quiescent current + * consumption to ~5.6uA but is only suitable for systems consuming + * less than ~2mA. Since this is not likely the case even on + * linux-based wearables - keep the chip in normal power mode. + */ + rv = regmap_update_bits(map, + MAX77650_REG_CNFG_GLBL, + MAX77650_SBIA_LPM_MASK, + MAX77650_SBIA_LPM_DISABLED); + if (rv) { + dev_err(dev, "Unable to change the power mode\n"); + return rv; + } + + rv = devm_regmap_add_irq_chip(dev, map, i2c->irq, + IRQF_ONESHOT | IRQF_SHARED, 0, + &max77650_irq_chip, &irq_data); + if (rv) { + dev_err(dev, "Unable to add Regmap IRQ chip\n"); + return rv; + } + + domain = regmap_irq_get_domain(irq_data); + + return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, + max77650_cells, ARRAY_SIZE(max77650_cells), + NULL, 0, domain); +} + +static const struct of_device_id max77650_of_match[] = { + { .compatible = "maxim,max77650" }, + { } +}; +MODULE_DEVICE_TABLE(of, max77650_of_match); + +static struct i2c_driver max77650_i2c_driver = { + .driver = { + .name = "max77650", + .of_match_table = of_match_ptr(max77650_of_match), + }, + .probe_new = max77650_i2c_probe, +}; +module_i2c_driver(max77650_i2c_driver); + +MODULE_DESCRIPTION("MAXIM 77650/77651 multi-function core driver"); +MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 94e3f32ce935..1ade4c8cc91f 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -269,6 +269,19 @@ fail_alloc: return ret; } +/** + * mfd_add_devices - register child devices + * + * @parent: Pointer to parent device. + * @id: Can be PLATFORM_DEVID_AUTO to let the Platform API take care + * of device numbering, or will be added to a device's cell_id. + * @cells: Array of (struct mfd_cell)s describing child devices. + * @n_devs: Number of child devices to register. + * @mem_base: Parent register range resource for child devices. + * @irq_base: Base of the range of virtual interrupt numbers allocated for + * this MFD device. Unused if @domain is specified. + * @domain: Interrupt domain to create mappings for hardware interrupts. + */ int mfd_add_devices(struct device *parent, int id, const struct mfd_cell *cells, int n_devs, struct resource *mem_base, diff --git a/drivers/mfd/ti-lmu.c b/drivers/mfd/ti-lmu.c index 37d0bdb291c3..b06cb908d1aa 100644 --- a/drivers/mfd/ti-lmu.c +++ b/drivers/mfd/ti-lmu.c @@ -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 }, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c index 5b3b06a0a3bf..d466e33635b0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c @@ -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); diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index e901b9879e7e..0230c96fa94d 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -499,6 +499,13 @@ config CHARGER_DETECTOR_MAX14656 Revision 1.2 and can be found e.g. in Kindle 4/5th generation readers and certain LG devices. +config CHARGER_MAX77650 + tristate "Maxim MAX77650 battery charger driver" + depends on MFD_MAX77650 + help + Say Y to enable support for the battery charger control of MAX77650 + PMICs. + config CHARGER_MAX77693 tristate "Maxim MAX77693 battery charger driver" depends on MFD_MAX77693 diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index b731c2a9b695..b73eb8c5c1a9 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o obj-$(CONFIG_CHARGER_LTC3651) += ltc3651-charger.o obj-$(CONFIG_CHARGER_MAX14577) += max14577_charger.o obj-$(CONFIG_CHARGER_DETECTOR_MAX14656) += max14656_charger_detector.o +obj-$(CONFIG_CHARGER_MAX77650) += max77650-charger.o obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o diff --git a/drivers/power/supply/max77650-charger.c b/drivers/power/supply/max77650-charger.c new file mode 100644 index 000000000000..e34714cb05ec --- /dev/null +++ b/drivers/power/supply/max77650-charger.c @@ -0,0 +1,368 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2018 BayLibre SAS +// Author: Bartosz Golaszewski <bgolaszewski@baylibre.com> +// +// Battery charger driver for MAXIM 77650/77651 charger/power-supply. + +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/mfd/max77650.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/regmap.h> + +#define MAX77650_CHARGER_ENABLED BIT(0) +#define MAX77650_CHARGER_DISABLED 0x00 +#define MAX77650_CHARGER_CHG_EN_MASK BIT(0) + +#define MAX77650_CHG_DETAILS_MASK GENMASK(7, 4) +#define MAX77650_CHG_DETAILS_BITS(_reg) \ + (((_reg) & MAX77650_CHG_DETAILS_MASK) >> 4) + +/* Charger is OFF. */ +#define MAX77650_CHG_OFF 0x00 +/* Charger is in prequalification mode. */ +#define MAX77650_CHG_PREQ 0x01 +/* Charger is in fast-charge constant current mode. */ +#define MAX77650_CHG_ON_CURR 0x02 +/* Charger is in JEITA modified fast-charge constant-current mode. */ +#define MAX77650_CHG_ON_CURR_JEITA 0x03 +/* Charger is in fast-charge constant-voltage mode. */ +#define MAX77650_CHG_ON_VOLT 0x04 +/* Charger is in JEITA modified fast-charge constant-voltage mode. */ +#define MAX77650_CHG_ON_VOLT_JEITA 0x05 +/* Charger is in top-off mode. */ +#define MAX77650_CHG_ON_TOPOFF 0x06 +/* Charger is in JEITA modified top-off mode. */ +#define MAX77650_CHG_ON_TOPOFF_JEITA 0x07 +/* Charger is done. */ +#define MAX77650_CHG_DONE 0x08 +/* Charger is JEITA modified done. */ +#define MAX77650_CHG_DONE_JEITA 0x09 +/* Charger is suspended due to a prequalification timer fault. */ +#define MAX77650_CHG_SUSP_PREQ_TIM_FAULT 0x0a +/* Charger is suspended due to a fast-charge timer fault. */ +#define MAX77650_CHG_SUSP_FAST_CHG_TIM_FAULT 0x0b +/* Charger is suspended due to a battery temperature fault. */ +#define MAX77650_CHG_SUSP_BATT_TEMP_FAULT 0x0c + +#define MAX77650_CHGIN_DETAILS_MASK GENMASK(3, 2) +#define MAX77650_CHGIN_DETAILS_BITS(_reg) \ + (((_reg) & MAX77650_CHGIN_DETAILS_MASK) >> 2) + +#define MAX77650_CHGIN_UNDERVOLTAGE_LOCKOUT 0x00 +#define MAX77650_CHGIN_OVERVOLTAGE_LOCKOUT 0x01 +#define MAX77650_CHGIN_OKAY 0x11 + +#define MAX77650_CHARGER_CHG_MASK BIT(1) +#define MAX77650_CHARGER_CHG_CHARGING(_reg) \ + (((_reg) & MAX77650_CHARGER_CHG_MASK) > 1) + +#define MAX77650_CHARGER_VCHGIN_MIN_MASK 0xc0 +#define MAX77650_CHARGER_VCHGIN_MIN_SHIFT(_val) ((_val) << 5) + +#define MAX77650_CHARGER_ICHGIN_LIM_MASK 0x1c +#define MAX77650_CHARGER_ICHGIN_LIM_SHIFT(_val) ((_val) << 2) + +struct max77650_charger_data { + struct regmap *map; + struct device *dev; +}; + +static enum power_supply_property max77650_charger_properties[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CHARGE_TYPE +}; + +static const unsigned int max77650_charger_vchgin_min_table[] = { + 4000000, 4100000, 4200000, 4300000, 4400000, 4500000, 4600000, 4700000 +}; + +static const unsigned int max77650_charger_ichgin_lim_table[] = { + 95000, 190000, 285000, 380000, 475000 +}; + +static int max77650_charger_set_vchgin_min(struct max77650_charger_data *chg, + unsigned int val) +{ + int i, rv; + + for (i = 0; i < ARRAY_SIZE(max77650_charger_vchgin_min_table); i++) { + if (val == max77650_charger_vchgin_min_table[i]) { + rv = regmap_update_bits(chg->map, + MAX77650_REG_CNFG_CHG_B, + MAX77650_CHARGER_VCHGIN_MIN_MASK, + MAX77650_CHARGER_VCHGIN_MIN_SHIFT(i)); + if (rv) + return rv; + + return 0; + } + } + + return -EINVAL; +} + +static int max77650_charger_set_ichgin_lim(struct max77650_charger_data *chg, + unsigned int val) +{ + int i, rv; + + for (i = 0; i < ARRAY_SIZE(max77650_charger_ichgin_lim_table); i++) { + if (val == max77650_charger_ichgin_lim_table[i]) { + rv = regmap_update_bits(chg->map, + MAX77650_REG_CNFG_CHG_B, + MAX77650_CHARGER_ICHGIN_LIM_MASK, + MAX77650_CHARGER_ICHGIN_LIM_SHIFT(i)); + if (rv) + return rv; + + return 0; + } + } + + return -EINVAL; +} + +static int max77650_charger_enable(struct max77650_charger_data *chg) +{ + int rv; + + rv = regmap_update_bits(chg->map, + MAX77650_REG_CNFG_CHG_B, + MAX77650_CHARGER_CHG_EN_MASK, + MAX77650_CHARGER_ENABLED); + if (rv) + dev_err(chg->dev, "unable to enable the charger: %d\n", rv); + + return rv; +} + +static int max77650_charger_disable(struct max77650_charger_data *chg) +{ + int rv; + + rv = regmap_update_bits(chg->map, + MAX77650_REG_CNFG_CHG_B, + MAX77650_CHARGER_CHG_EN_MASK, + MAX77650_CHARGER_DISABLED); + if (rv) + dev_err(chg->dev, "unable to disable the charger: %d\n", rv); + + return rv; +} + +static irqreturn_t max77650_charger_check_status(int irq, void *data) +{ + struct max77650_charger_data *chg = data; + int rv, reg; + + rv = regmap_read(chg->map, MAX77650_REG_STAT_CHG_B, ®); + if (rv) { + dev_err(chg->dev, + "unable to read the charger status: %d\n", rv); + return IRQ_HANDLED; + } + + switch (MAX77650_CHGIN_DETAILS_BITS(reg)) { + case MAX77650_CHGIN_UNDERVOLTAGE_LOCKOUT: + dev_err(chg->dev, "undervoltage lockout detected, disabling charger\n"); + max77650_charger_disable(chg); + break; + case MAX77650_CHGIN_OVERVOLTAGE_LOCKOUT: + dev_err(chg->dev, "overvoltage lockout detected, disabling charger\n"); + max77650_charger_disable(chg); + break; + case MAX77650_CHGIN_OKAY: + max77650_charger_enable(chg); + break; + default: + /* May be 0x10 - debouncing */ + break; + } + + return IRQ_HANDLED; +} + +static int max77650_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct max77650_charger_data *chg = power_supply_get_drvdata(psy); + int rv, reg; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + rv = regmap_read(chg->map, MAX77650_REG_STAT_CHG_B, ®); + if (rv) + return rv; + + if (MAX77650_CHARGER_CHG_CHARGING(reg)) { + val->intval = POWER_SUPPLY_STATUS_CHARGING; + break; + } + + switch (MAX77650_CHG_DETAILS_BITS(reg)) { + case MAX77650_CHG_OFF: + case MAX77650_CHG_SUSP_PREQ_TIM_FAULT: + case MAX77650_CHG_SUSP_FAST_CHG_TIM_FAULT: + case MAX77650_CHG_SUSP_BATT_TEMP_FAULT: + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case MAX77650_CHG_PREQ: + case MAX77650_CHG_ON_CURR: + case MAX77650_CHG_ON_CURR_JEITA: + case MAX77650_CHG_ON_VOLT: + case MAX77650_CHG_ON_VOLT_JEITA: + case MAX77650_CHG_ON_TOPOFF: + case MAX77650_CHG_ON_TOPOFF_JEITA: + val->intval = POWER_SUPPLY_STATUS_CHARGING; + break; + case MAX77650_CHG_DONE: + val->intval = POWER_SUPPLY_STATUS_FULL; + break; + default: + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + } + break; + case POWER_SUPPLY_PROP_ONLINE: + rv = regmap_read(chg->map, MAX77650_REG_STAT_CHG_B, ®); + if (rv) + return rv; + + val->intval = MAX77650_CHARGER_CHG_CHARGING(reg); + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + rv = regmap_read(chg->map, MAX77650_REG_STAT_CHG_B, ®); + if (rv) + return rv; + + if (!MAX77650_CHARGER_CHG_CHARGING(reg)) { + val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; + break; + } + + switch (MAX77650_CHG_DETAILS_BITS(reg)) { + case MAX77650_CHG_PREQ: + case MAX77650_CHG_ON_CURR: + case MAX77650_CHG_ON_CURR_JEITA: + case MAX77650_CHG_ON_VOLT: + case MAX77650_CHG_ON_VOLT_JEITA: + val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; + break; + case MAX77650_CHG_ON_TOPOFF: + case MAX77650_CHG_ON_TOPOFF_JEITA: + val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + break; + default: + val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct power_supply_desc max77650_battery_desc = { + .name = "max77650", + .type = POWER_SUPPLY_TYPE_USB, + .get_property = max77650_charger_get_property, + .properties = max77650_charger_properties, + .num_properties = ARRAY_SIZE(max77650_charger_properties), +}; + +static int max77650_charger_probe(struct platform_device *pdev) +{ + struct power_supply_config pscfg = {}; + struct max77650_charger_data *chg; + struct power_supply *battery; + struct device *dev, *parent; + int rv, chg_irq, chgin_irq; + unsigned int prop; + + dev = &pdev->dev; + parent = dev->parent; + + chg = devm_kzalloc(dev, sizeof(*chg), GFP_KERNEL); + if (!chg) + return -ENOMEM; + + platform_set_drvdata(pdev, chg); + + chg->map = dev_get_regmap(parent, NULL); + if (!chg->map) + return -ENODEV; + + chg->dev = dev; + + pscfg.of_node = dev->of_node; + pscfg.drv_data = chg; + + chg_irq = platform_get_irq_byname(pdev, "CHG"); + if (chg_irq < 0) + return chg_irq; + + chgin_irq = platform_get_irq_byname(pdev, "CHGIN"); + if (chgin_irq < 0) + return chgin_irq; + + rv = devm_request_any_context_irq(dev, chg_irq, + max77650_charger_check_status, + IRQF_ONESHOT, "chg", chg); + if (rv < 0) + return rv; + + rv = devm_request_any_context_irq(dev, chgin_irq, + max77650_charger_check_status, + IRQF_ONESHOT, "chgin", chg); + if (rv < 0) + return rv; + + battery = devm_power_supply_register(dev, + &max77650_battery_desc, &pscfg); + if (IS_ERR(battery)) + return PTR_ERR(battery); + + rv = of_property_read_u32(dev->of_node, + "input-voltage-min-microvolt", &prop); + if (rv == 0) { + rv = max77650_charger_set_vchgin_min(chg, prop); + if (rv) + return rv; + } + + rv = of_property_read_u32(dev->of_node, + "input-current-limit-microamp", &prop); + if (rv == 0) { + rv = max77650_charger_set_ichgin_lim(chg, prop); + if (rv) + return rv; + } + + return max77650_charger_enable(chg); +} + +static int max77650_charger_remove(struct platform_device *pdev) +{ + struct max77650_charger_data *chg = platform_get_drvdata(pdev); + + return max77650_charger_disable(chg); +} + +static struct platform_driver max77650_charger_driver = { + .driver = { + .name = "max77650-charger", + }, + .probe = max77650_charger_probe, + .remove = max77650_charger_remove, +}; +module_platform_driver(max77650_charger_driver); + +MODULE_DESCRIPTION("MAXIM 77650/77651 charger driver"); +MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.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 index 000000000000..b1ef11a83872 --- /dev/null +++ b/include/linux/mfd/altera-sysmgr.h @@ -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__ */ diff --git a/include/linux/mfd/max77620.h b/include/linux/mfd/max77620.h index ad2a9a852aea..82407fe85ca2 100644 --- a/include/linux/mfd/max77620.h +++ b/include/linux/mfd/max77620.h @@ -136,8 +136,8 @@ #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/max77650.h b/include/linux/mfd/max77650.h new file mode 100644 index 000000000000..c809e211a8cd --- /dev/null +++ b/include/linux/mfd/max77650.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 BayLibre SAS + * Author: Bartosz Golaszewski <bgolaszewski@baylibre.com> + * + * Common definitions for MAXIM 77650/77651 charger/power-supply. + */ + +#ifndef MAX77650_H +#define MAX77650_H + +#include <linux/bits.h> + +#define MAX77650_REG_INT_GLBL 0x00 +#define MAX77650_REG_INT_CHG 0x01 +#define MAX77650_REG_STAT_CHG_A 0x02 +#define MAX77650_REG_STAT_CHG_B 0x03 +#define MAX77650_REG_ERCFLAG 0x04 +#define MAX77650_REG_STAT_GLBL 0x05 +#define MAX77650_REG_INTM_GLBL 0x06 +#define MAX77650_REG_INTM_CHG 0x07 +#define MAX77650_REG_CNFG_GLBL 0x10 +#define MAX77650_REG_CID 0x11 +#define MAX77650_REG_CNFG_GPIO 0x12 +#define MAX77650_REG_CNFG_CHG_A 0x18 +#define MAX77650_REG_CNFG_CHG_B 0x19 +#define MAX77650_REG_CNFG_CHG_C 0x1a +#define MAX77650_REG_CNFG_CHG_D 0x1b +#define MAX77650_REG_CNFG_CHG_E 0x1c +#define MAX77650_REG_CNFG_CHG_F 0x1d +#define MAX77650_REG_CNFG_CHG_G 0x1e +#define MAX77650_REG_CNFG_CHG_H 0x1f +#define MAX77650_REG_CNFG_CHG_I 0x20 +#define MAX77650_REG_CNFG_SBB_TOP 0x28 +#define MAX77650_REG_CNFG_SBB0_A 0x29 +#define MAX77650_REG_CNFG_SBB0_B 0x2a +#define MAX77650_REG_CNFG_SBB1_A 0x2b +#define MAX77650_REG_CNFG_SBB1_B 0x2c +#define MAX77650_REG_CNFG_SBB2_A 0x2d +#define MAX77650_REG_CNFG_SBB2_B 0x2e +#define MAX77650_REG_CNFG_LDO_A 0x38 +#define MAX77650_REG_CNFG_LDO_B 0x39 +#define MAX77650_REG_CNFG_LED0_A 0x40 +#define MAX77650_REG_CNFG_LED1_A 0x41 +#define MAX77650_REG_CNFG_LED2_A 0x42 +#define MAX77650_REG_CNFG_LED0_B 0x43 +#define MAX77650_REG_CNFG_LED1_B 0x44 +#define MAX77650_REG_CNFG_LED2_B 0x45 +#define MAX77650_REG_CNFG_LED_TOP 0x46 + +#define MAX77650_CID_MASK GENMASK(3, 0) +#define MAX77650_CID_BITS(_reg) (_reg & MAX77650_CID_MASK) + +#define MAX77650_CID_77650A 0x03 +#define MAX77650_CID_77650C 0x0a +#define MAX77650_CID_77651A 0x06 +#define MAX77650_CID_77651B 0x08 + +#endif /* MAX77650_H */ diff --git a/include/linux/mfd/ti-lmu-register.h b/include/linux/mfd/ti-lmu-register.h index 2125c7c02818..f09510561a55 100644 --- a/include/linux/mfd/ti-lmu-register.h +++ b/include/linux/mfd/ti-lmu-register.h @@ -15,50 +15,6 @@ #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) diff --git a/include/linux/mfd/ti-lmu.h b/include/linux/mfd/ti-lmu.h index 1ef51ed36be5..7762c1bce55d 100644 --- a/include/linux/mfd/ti-lmu.h +++ b/include/linux/mfd/ti-lmu.h @@ -22,7 +22,6 @@ #define LMU_EVENT_MONITOR_DONE 0x01 enum ti_lmu_id { - LM3532, LM3631, LM3632, LM3633, |