Commit ebcf5bb2 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'mfd-next-5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd

Pull MFD updates from Lee Jones:
 "Core Framework:
   - Document (kerneldoc) core mfd_add_devices() API

  New Drivers:
   - Altera SOCFPGA System Manager
   - Maxim MAX77650/77651 PMIC
   - Maxim MAX77663 PMIC
   - ST Multi-Function eXpander (STMFX)

  New Device Support:
   - LEDs support in Intel Cherry Trail Whiskey Cove PMIC
   - RTC support in SAMSUNG Electronics S2MPA01 PMIC
   - SAM9X60 support in Atmel HLCDC (High-end LCD Controller)
   - USB X-Powers AXP 8xx PMICs
   - Integrated Sensor Hub (ISH) in ChromeOS EC
   - USB PD Logger in ChromeOS EC
   - AXP223 in X-Powers AXP series PMICs
   - Power Supply in X-Powers AXP 803 PMICs
   - Comet Lake in Intel Low Power Subsystem
   - Fingerprint MCU in ChromeOS EC
   - Touchpad MCU in ChromeOS EC
   - Move TI LM3532 support to LED

  New Functionality:
   - max77650, max77620: Add/extend DT support
   - max77620 power-off
   - syscon clocking
   - croc_ec host sleep event

  Fix-ups:
   - Trivial; Formatting, spelling, etc; Kconfig, sec-core, ab8500-debugfs
   - Remove unused functionality; rk808, da9063-*
   - SPDX conversion; da9063-*, atmel-*,
   - Adapt/add new register definitions; cs47l35-tables, cs47l90-tables, imx6q-iomuxc-gpr
   - Fix-up DT bindings; ti-lmu, cirrus,lochnagar
   - Simply obtaining driver data; ssbi, t7l66xb, tc6387xb, tc6393xb

  Bug Fixes:
   - Fix incorrect defined values; max77620, da9063
   - Fix device initialisation; twl6040
   - Reset device on init; intel-lpss
   - Fix build warnings when !OF; sun6i-prcm
   - Register OF match tables; tps65912-spi
   - Fix DMI matching; intel_quark_i2c_gpio"

* tag 'mfd-next-5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd: (65 commits)
  mfd: Use dev_get_drvdata() directly
  mfd: cros_ec: Instantiate properly CrOS Touchpad MCU device
  mfd: cros_ec: Instantiate properly CrOS FP MCU device
  mfd: cros_ec: Update the EC feature codes
  mfd: intel-lpss: Add Intel Comet Lake PCI IDs
  mfd: lochnagar: Add links to binding docs for sound and hwmon
  mfd: ab8500-debugfs: Fix a typo ("deubgfs")
  mfd: imx6sx: Add MQS register definition for iomuxc gpr
  dt-bindings: mfd: LMU: Fix lm3632 dt binding example
  mfd: intel_quark_i2c_gpio: Adjust IOT2000 matching
  mfd: da9063: Fix OTP control register names to match datasheets for DA9063/63L
  mfd: tps65912-spi: Add missing of table registration
  mfd: axp20x: Add USB power supply mfd cell to AXP803
  mfd: sun6i-prcm: Fix build warning for non-OF configurations
  mfd: intel-lpss: Set the device in reset state when init
  platform/chrome: Add support for v1 of host sleep event
  mfd: cros_ec: Add host_sleep_event_v1 command
  mfd: cros_ec: Instantiate the CrOS USB PD logger driver
  mfd: cs47l90: Make DAC_AEC_CONTROL_2 readable
  mfd: cs47l35: Make DAC_AEC_CONTROL_2 readable
  ...
parents 414147d9 ed835136
......@@ -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>;
};
......@@ -5,10 +5,12 @@ Properties:
- " st,stm32mp157-syscfg " - for stm32mp157 based SoCs,
second value must be always "syscon".
- reg : offset and length of the register set.
- clocks: phandle to the syscfg clock
Example:
syscfg: syscon@50020000 {
compatible = "st,stm32mp157-syscfg", "syscon";
reg = <0x50020000 0x400>;
clocks = <&rcc SYSCFG>;
};
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;
};
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";
};
};
......@@ -7,6 +7,7 @@ Required properties:
"atmel,sama5d2-hlcdc"
"atmel,sama5d3-hlcdc"
"atmel,sama5d4-hlcdc"
"microchip,sam9x60-hlcdc"
- reg: base address and size of the HLCDC device registers.
- clock-names: the name of the 3 clocks requested by the HLCDC device.
Should contain "periph_clk", "sys_clk" and "slow_clk".
......
......@@ -19,6 +19,8 @@ And these documents for the required sub-node binding details:
[4] Clock: ../clock/cirrus,lochnagar.txt
[5] Pinctrl: ../pinctrl/cirrus,lochnagar.txt
[6] Regulator: ../regulator/cirrus,lochnagar.txt
[7] Sound: ../sound/cirrus,lochnagar.txt
[8] Hardware Monitor: ../hwmon/cirrus,lochnagar.txt
Required properties:
......@@ -41,6 +43,11 @@ Optional sub-nodes:
- Bindings for the regulator components, see [6]. Only available on
Lochnagar 2.
- lochnagar-sc : Binding for the sound card components, see [7].
Only available on Lochnagar 2.
- lochnagar-hwmon : Binding for the hardware monitor components, see [8].
Only available on Lochnagar 2.
Optional properties:
- present-gpios : Host present line, indicating the presence of a
......@@ -65,4 +72,14 @@ lochnagar: lochnagar@22 {
compatible = "cirrus,lochnagar-pinctrl";
...
};
lochnagar-sc {
compatible = "cirrus,lochnagar2-soundcard";
...
};
lochnagar-hwmon {
compatible = "cirrus,lochnagar2-hwmon";
...
};
};
......@@ -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
......
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";
};
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.
......@@ -104,8 +104,8 @@ lm3632@11 {
regulators {
compatible = "ti,lm363x-regulator";
ti,lcm-en1-gpio = <&pioC 0 GPIO_ACTIVE_HIGH>; /* PC0 */
ti,lcm-en2-gpio = <&pioC 1 GPIO_ACTIVE_HIGH>; /* PC1 */
enable-gpios = <&pioC 0 GPIO_ACTIVE_HIGH>,
<&pioC 1 GPIO_ACTIVE_HIGH>;
vboost {
regulator-name = "lcd_boost";
......
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>;
};
}
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>;
};
......@@ -710,6 +710,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
......@@ -9517,6 +9523,20 @@ S: Maintained
F: Documentation/devicetree/bindings/iio/proximity/maxbotix,mb1232.txt
F: drivers/iio/proximity/mb1232.c
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
......
......@@ -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
......
......@@ -393,7 +393,7 @@ spi1: spi@ffda5000 {
};
sysmgr: sysmgr@ffd12000 {
compatible = "altr,sys-mgr", "syscon";
compatible = "altr,sys-mgr-s10","altr,sys-mgr";
reg = <0xffd12000 0x228>;
};
......
......@@ -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
......
......@@ -1097,6 +1097,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
......
......@@ -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
......
// 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");
......@@ -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
......
......@@ -44,6 +44,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
......
// 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");
......@@ -619,6 +619,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
......
......@@ -62,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
......
// 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", &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");
......@@ -16,7 +16,7 @@ config MFD_CS5535
depends on PCI && (X86_32 || (X86 && COMPILE_TEST))
---help---
This is the core driver for CS5535/CS5536 MFD functions. This is
necessary for using the board's GPIO and MFGPT functionality.
necessary for using the board's GPIO and MFGPT functionality.
config MFD_ALTERA_A10SR
bool "Altera Arria10 DevKit System Resource chip"
......@@ -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
......@@ -213,13 +223,13 @@ config MFD_CROS_EC
protocol for talking to the EC is defined by the bus driver.
config MFD_CROS_EC_CHARDEV
tristate "Chrome OS Embedded Controller userspace device interface"
depends on MFD_CROS_EC
---help---
This driver adds support to talk with the ChromeOS EC from userspace.
tristate "Chrome OS Embedded Controller userspace device interface"
depends on MFD_CROS_EC
---help---
This driver adds support to talk with the ChromeOS EC from userspace.
If you have a supported Chromebook, choose Y or M here.
The module will be called cros_ec_dev.
If you have a supported Chromebook, choose Y or M here.
The module will be called cros_ec_dev.
config MFD_MADERA
tristate "Cirrus Logic Madera codecs"
......@@ -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
......@@ -867,7 +891,7 @@ config MFD_CPCAP
At least Motorola Droid 4 is known to use CPCAP.
config MFD_VIPERBOARD
tristate "Nano River Technologies Viperboard"
tristate "Nano River Technologies Viperboard"
select MFD_CORE
depends on USB
default n
......@@ -903,15 +927,15 @@ config PCF50633_ADC
tristate "NXP PCF50633 ADC"
depends on MFD_PCF50633
help
Say yes here if you want to include support for ADC in the
NXP PCF50633 chip.
Say yes here if you want to include support for ADC in the
NXP PCF50633 chip.
config PCF50633_GPIO
tristate "NXP PCF50633 GPIO"
depends on MFD_PCF50633
help
Say yes here if you want to include support GPIO for pins on
the PCF50633 chip.
Say yes here if you want to include support GPIO for pins on
the PCF50633 chip.
config UCB1400_CORE
tristate "Philips UCB1400 Core driver"
......@@ -1026,7 +1050,7 @@ config MFD_RN5T618
select REGMAP_I2C
help
Say yes here to add support for the Ricoh RN5T567,
RN5T618, RC5T619 PMIC.
RN5T618, RC5T619 PMIC.
This driver provides common support for accessing the device,
additional drivers must be enabled in order to use the
functionality of the device.
......@@ -1079,9 +1103,9 @@ config MFD_SM501_GPIO
bool "Export GPIO via GPIO layer"
depends on MFD_SM501 && GPIOLIB
---help---
This option uses the gpio library layer to export the 64 GPIO
lines on the SM501. The platform data is used to supply the
base number for the first GPIO line to register.
This option uses the gpio library layer to export the 64 GPIO
lines on the SM501. The platform data is used to supply the
base number for the first GPIO line to register.
config MFD_SKY81452
tristate "Skyworks Solutions SKY81452"
......@@ -1096,16 +1120,16 @@ config MFD_SKY81452
will be called sky81452.
config MFD_SMSC
bool "SMSC ECE1099 series chips"
depends on I2C=y
select MFD_CORE
select REGMAP_I2C
help
If you say yes here you get support for the
ece1099 chips from SMSC.
bool "SMSC ECE1099 series chips"
depends on I2C=y
select MFD_CORE
select REGMAP_I2C
help
If you say yes here you get support for the
ece1099 chips from SMSC.
To compile this driver as a module, choose M here: the
module will be called smsc.
To compile this driver as a module, choose M here: the
module will be called smsc.
config MFD_SC27XX_PMIC
tristate "Spreadtrum SC27xx PMICs"
......@@ -1171,12 +1195,12 @@ config AB8500_CORE
This chip embeds various other multimedia funtionalities as well.
config AB8500_DEBUG
bool "Enable debug info via debugfs"
depends on AB8500_GPADC && DEBUG_FS
default y if DEBUG_FS
help
Select this option if you want debug information using the debug
filesystem, debugfs.
bool "Enable debug info via debugfs"
depends on AB8500_GPADC && DEBUG_FS
default y if DEBUG_FS
help
Select this option if you want debug information using the debug
filesystem, debugfs.
config AB8500_GPADC
bool "ST-Ericsson AB8500 GPADC driver"
......@@ -1907,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
......
......@@ -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
......@@ -246,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
......@@ -2587,7 +2587,7 @@ static ssize_t ab8500_unsubscribe_write(struct file *file,
}
/*
* - several deubgfs nodes fops
* - several debugfs nodes fops
*/
static const struct file_operations ab8500_bank_fops = {
......
// 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");
......@@ -141,6 +141,7 @@ static const struct of_device_id atmel_hlcdc_match[] = {
{ .compatible = "atmel,sama5d2-hlcdc" },
{ .compatible = "atmel,sama5d3-hlcdc" },
{ .compatible = "atmel,sama5d4-hlcdc" },
{ .compatible = "microchip,sam9x60-hlcdc" },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, atmel_hlcdc_match);
......
......@@ -65,6 +65,7 @@ static const struct of_device_id axp20x_i2c_of_match[] = {
{ .compatible = "x-powers,axp202", .data = (void *)AXP202_ID },
{ .compatible = "x-powers,axp209", .data = (void *)AXP209_ID },
{ .compatible = "x-powers,axp221", .data = (void *)AXP221_ID },
{ .compatible = "x-powers,axp223", .data = (void *)AXP223_ID },
{ .compatible = "x-powers,axp806", .data = (void *)AXP806_ID },
{ },
};
......@@ -75,6 +76,7 @@ static const struct i2c_device_id axp20x_i2c_id[] = {
{ "axp202", 0 },
{ "axp209", 0 },
{ "axp221", 0 },
{ "axp223", 0 },
{ "axp806", 0 },
{ },
};
......
......@@ -198,6 +198,12 @@ static const struct resource axp22x_usb_power_supply_resources[] = {
DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_VBUS_REMOVAL, "VBUS_REMOVAL"),
};
/* AXP803 and AXP813/AXP818 share the same interrupts */
static const struct resource axp803_usb_power_supply_resources[] = {
DEFINE_RES_IRQ_NAMED(AXP803_IRQ_VBUS_PLUGIN, "VBUS_PLUGIN"),
DEFINE_RES_IRQ_NAMED(AXP803_IRQ_VBUS_REMOVAL, "VBUS_REMOVAL"),
};
static const struct resource axp22x_pek_resources[] = {
DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_PEK_RIS_EDGE, "PEK_DBR"),
DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_PEK_FAL_EDGE, "PEK_DBF"),
......@@ -741,6 +747,11 @@ static const struct mfd_cell axp803_cells[] = {
.of_compatible = "x-powers,axp813-ac-power-supply",
.num_resources = ARRAY_SIZE(axp20x_ac_power_supply_resources),
.resources = axp20x_ac_power_supply_resources,
}, {
.name = "axp20x-usb-power-supply",
.num_resources = ARRAY_SIZE(axp803_usb_power_supply_resources),
.resources = axp803_usb_power_supply_resources,
.of_compatible = "x-powers,axp813-usb-power-supply",
},
{ .name = "axp20x-regulator" },
};
......@@ -793,6 +804,11 @@ static const struct mfd_cell axp813_cells[] = {
.of_compatible = "x-powers,axp813-ac-power-supply",
.num_resources = ARRAY_SIZE(axp20x_ac_power_supply_resources),
.resources = axp20x_ac_power_supply_resources,
}, {
.name = "axp20x-usb-power-supply",
.num_resources = ARRAY_SIZE(axp803_usb_power_supply_resources),
.resources = axp803_usb_power_supply_resources,
.of_compatible = "x-powers,axp813-usb-power-supply",
},
};
......
......@@ -75,20 +75,49 @@ static irqreturn_t ec_irq_thread(int irq, void *data)
static int cros_ec_sleep_event(struct cros_ec_device *ec_dev, u8 sleep_event)
{
int ret;
struct {
struct cros_ec_command msg;
struct ec_params_host_sleep_event req;
union {
struct ec_params_host_sleep_event req0;
struct ec_params_host_sleep_event_v1 req1;
struct ec_response_host_sleep_event_v1 resp1;
} u;
} __packed buf;
memset(&buf, 0, sizeof(buf));
buf.req.sleep_event = sleep_event;
if (ec_dev->host_sleep_v1) {
buf.u.req1.sleep_event = sleep_event;
buf.u.req1.suspend_params.sleep_timeout_ms =
EC_HOST_SLEEP_TIMEOUT_DEFAULT;
buf.msg.outsize = sizeof(buf.u.req1);
if ((sleep_event == HOST_SLEEP_EVENT_S3_RESUME) ||
(sleep_event == HOST_SLEEP_EVENT_S0IX_RESUME))
buf.msg.insize = sizeof(buf.u.resp1);
buf.msg.version = 1;
} else {
buf.u.req0.sleep_event = sleep_event;
buf.msg.outsize = sizeof(buf.u.req0);
}
buf.msg.command = EC_CMD_HOST_SLEEP_EVENT;
buf.msg.version = 0;
buf.msg.outsize = sizeof(buf.req);
return cros_ec_cmd_xfer(ec_dev, &buf.msg);
ret = cros_ec_cmd_xfer(ec_dev, &buf.msg);
/* For now, report failure to transition to S0ix with a warning. */
if (ret >= 0 && ec_dev->host_sleep_v1 &&
(sleep_event == HOST_SLEEP_EVENT_S0IX_RESUME))
WARN_ONCE(buf.u.resp1.resume_response.sleep_transitions &
EC_HOST_RESUME_SLEEP_TIMEOUT,
"EC detected sleep transition timeout. Total slp_s0 transitions: %d",
buf.u.resp1.resume_response.sleep_transitions &
EC_HOST_RESUME_SLEEP_TRANSITIONS_MASK);
return ret;
}
int cros_ec_register(struct cros_ec_device *ec_dev)
......
......@@ -385,7 +385,8 @@ static const struct mfd_cell cros_ec_rtc_cells[] = {
};
static const struct mfd_cell cros_usbpd_charger_cells[] = {
{ .name = "cros-usbpd-charger" }
{ .name = "cros-usbpd-charger" },
{ .name = "cros-usbpd-logger" },
};
static const struct mfd_cell cros_ec_platform_cells[] = {
......@@ -418,6 +419,39 @@ static int ec_device_probe(struct platform_device *pdev)
device_initialize(&ec->class_dev);
cdev_init(&ec->cdev, &fops);
/* Check whether this is actually a Fingerprint MCU rather than an EC */
if (cros_ec_check_features(ec, EC_FEATURE_FINGERPRINT)) {
dev_info(dev, "CrOS Fingerprint MCU detected.\n");
/*
* Help userspace differentiating ECs from FP MCU,
* regardless of the probing order.
*/
ec_platform->ec_name = CROS_EC_DEV_FP_NAME;
}
/*
* Check whether this is actually an Integrated Sensor Hub (ISH)
* rather than an EC.
*/
if (cros_ec_check_features(ec, EC_FEATURE_ISH)) {
dev_info(dev, "CrOS ISH MCU detected.\n");
/*
* Help userspace differentiating ECs from ISH MCU,
* regardless of the probing order.
*/
ec_platform->ec_name = CROS_EC_DEV_ISH_NAME;
}
/* Check whether this is actually a Touchpad MCU rather than an EC */
if (cros_ec_check_features(ec, EC_FEATURE_TOUCHPAD)) {
dev_info(dev, "CrOS Touchpad MCU detected.\n");
/*
* Help userspace differentiating ECs from TP MCU,
* regardless of the probing order.
*/
ec_platform->ec_name = CROS_EC_DEV_TP_NAME;
}
/*
* Add the class device
* Link to the character device for creating the /dev entry
......
......@@ -178,6 +178,7 @@ static const struct reg_default cs47l35_reg_default[] = {
{ 0x00000448, 0x0a83 }, /* R1096 (0x448) - eDRE Enable */
{ 0x0000044a, 0x0000 }, /* R1098 (0x44a) - eDRE Manual */
{ 0x00000450, 0x0000 }, /* R1104 (0x450) - DAC AEC Control 1 */
{ 0x00000451, 0x0000 }, /* R1105 (0x451) - DAC AEC Control 2 */
{ 0x00000458, 0x0000 }, /* R1112 (0x458) - Noise Gate Control */
{ 0x00000490, 0x0069 }, /* R1168 (0x490) - PDM SPK1 CTRL 1 */
{ 0x00000491, 0x0000 }, /* R1169 (0x491) - PDM SPK1 CTRL 2 */
......@@ -970,6 +971,7 @@ static bool cs47l35_16bit_readable_register(struct device *dev,
case MADERA_EDRE_ENABLE:
case MADERA_EDRE_MANUAL:
case MADERA_DAC_AEC_CONTROL_1:
case MADERA_DAC_AEC_CONTROL_2:
case MADERA_NOISE_GATE_CONTROL:
case MADERA_PDM_SPK1_CTRL_1:
case MADERA_PDM_SPK1_CTRL_2:
......
......@@ -263,6 +263,7 @@ static const struct reg_default cs47l90_reg_default[] = {
{ 0x00000440, 0x003f }, /* R1088 (0x440) - DRE Enable */
{ 0x00000448, 0x003f }, /* R1096 (0x448) - eDRE Enable */
{ 0x00000450, 0x0000 }, /* R1104 (0x450) - DAC AEC Control 1 */
{ 0x00000451, 0x0000 }, /* R1104 (0x450) - DAC AEC Control 2 */
{ 0x00000458, 0x0000 }, /* R1112 (0x458) - Noise Gate Control */
{ 0x00000490, 0x0069 }, /* R1168 (0x490) - PDM SPK1 CTRL 1 */
{ 0x00000491, 0x0000 }, /* R1169 (0x491) - PDM SPK1 CTRL 2 */
......@@ -1692,6 +1693,7 @@ static bool cs47l90_16bit_readable_register(struct device *dev,
case MADERA_DRE_ENABLE:
case MADERA_EDRE_ENABLE:
case MADERA_DAC_AEC_CONTROL_1:
case MADERA_DAC_AEC_CONTROL_2:
case MADERA_NOISE_GATE_CONTROL:
case MADERA_PDM_SPK1_CTRL_1:
case MADERA_PDM_SPK1_CTRL_2:
......
// SPDX-License-Identifier: GPL-2.0+
/*
* da9063-core.c: Device access for Dialog DA9063 modules
* Device access for Dialog DA9063 modules
*
* Copyright 2012 Dialog Semiconductors Ltd.
* Copyright 2013 Philipp Zabel, Pengutronix
......@@ -7,11 +8,6 @@
* Author: Krystian Garbaciak, Dialog Semiconductor
* Author: Michal Hajduk, Dialog Semiconductor
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/kernel.h>
......@@ -26,7 +22,6 @@
#include <linux/regmap.h>
#include <linux/mfd/da9063/core.h>
#include <linux/mfd/da9063/pdata.h>
#include <linux/mfd/da9063/registers.h>
#include <linux/proc_fs.h>
......@@ -165,7 +160,6 @@ static int da9063_clear_fault_log(struct da9063 *da9063)
int da9063_device_init(struct da9063 *da9063, unsigned int irq)
{
struct da9063_pdata *pdata = da9063->dev->platform_data;
int model, variant_id, variant_code;
int ret;
......@@ -173,24 +167,10 @@ int da9063_device_init(struct da9063 *da9063, unsigned int irq)
if (ret < 0)
dev_err(da9063->dev, "Cannot clear fault log\n");
if (pdata) {
da9063->flags = pdata->flags;
da9063->irq_base = pdata->irq_base;
} else {
da9063->flags = 0;
da9063->irq_base = -1;
}
da9063->flags = 0;
da9063->irq_base = -1;
da9063->chip_irq = irq;
if (pdata && pdata->init != NULL) {
ret = pdata->init(da9063);
if (ret != 0) {
dev_err(da9063->dev,
"Platform initialization failed.\n");
return ret;
}
}
ret = regmap_read(da9063->regmap, DA9063_REG_CHIP_ID, &model);
if (ret < 0) {
dev_err(da9063->dev, "Cannot read chip model id.\n");
......
/* da9063-i2c.c: Interrupt support for Dialog DA9063
// SPDX-License-Identifier: GPL-2.0+
/* I2C support for Dialog DA9063
*
* Copyright 2012 Dialog Semiconductor Ltd.
* Copyright 2013 Philipp Zabel, Pengutronix
*
* Author: Krystian Garbaciak, Dialog Semiconductor
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/kernel.h>
......@@ -22,7 +17,6 @@
#include <linux/mfd/core.h>
#include <linux/mfd/da9063/core.h>
#include <linux/mfd/da9063/pdata.h>
#include <linux/mfd/da9063/registers.h>
#include <linux/of.h>
......
/* da9063-irq.c: Interrupts support for Dialog DA9063
// SPDX-License-Identifier: GPL-2.0+
/* Interrupt support for Dialog DA9063
*
* Copyright 2012 Dialog Semiconductor Ltd.
* Copyright 2013 Philipp Zabel, Pengutronix
*
* Author: Michal Hajduk, Dialog Semiconductor
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/kernel.h>
......@@ -19,7 +14,6 @@
#include <linux/interrupt.h>
#include <linux/regmap.h>
#include <linux/mfd/da9063/core.h>
#include <linux/mfd/da9063/pdata.h>
#define DA9063_REG_EVENT_A_OFFSET 0
#define DA9063_REG_EVENT_B_OFFSET 1
......
......@@ -129,6 +129,19 @@ static const struct intel_lpss_platform_info cnl_i2c_info = {
};
static const struct pci_device_id intel_lpss_pci_ids[] = {
/* CML */
{ PCI_VDEVICE(INTEL, 0x02a8), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x02a9), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x02aa), (kernel_ulong_t)&spt_info },
{ PCI_VDEVICE(INTEL, 0x02ab), (kernel_ulong_t)&spt_info },
{ PCI_VDEVICE(INTEL, 0x02c5), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x02c6), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x02c7), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x02e8), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x02e9), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x02ea), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x02eb), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x02fb), (kernel_ulong_t)&spt_info },
/* BXT A-Step */
{ PCI_VDEVICE(INTEL, 0x0aac), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x0aae), (kernel_ulong_t)&bxt_i2c_info },
......
......@@ -273,6 +273,9 @@ static void intel_lpss_init_dev(const struct intel_lpss *lpss)
{
u32 value = LPSS_PRIV_SSP_REG_DIS_DMA_FIN;
/* Set the device in reset state */
writel(0, lpss->priv + LPSS_PRIV_RESETS);
intel_lpss_deassert_reset(lpss);
intel_lpss_set_remap_addr(lpss);
......
......@@ -74,16 +74,6 @@ static const struct dmi_system_id dmi_platform_info[] = {
{
.matches = {
DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"),
DMI_EXACT_MATCH(DMI_BOARD_ASSET_TAG,
"6ES7647-0AA00-0YA2"),
},
.driver_data = (void *)400000,
},
{
.matches = {
DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"),
DMI_EXACT_MATCH(DMI_BOARD_ASSET_TAG,
"6ES7647-0AA00-1YA2"),
},
.driver_data = (void *)400000,
},
......
......@@ -60,6 +60,7 @@ static struct mfd_cell cht_wc_dev[] = {
.resources = cht_wc_ext_charger_resources,
},
{ .name = "cht_wcove_region", },
{ .name = "cht_wcove_leds", },
};
/*
......
......@@ -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 @@ static int max77620_i2c_resume(struct device *dev)
static const struct i2c_device_id max77620_id[] = {
{"max77620", MAX77620},
{"max20024", MAX20024},
{"max77663", MAX77663},
{},
};
......
// 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");
......@@ -269,6 +269,19 @@ static int mfd_add_device(struct device *parent, int id,
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,
......
......@@ -568,14 +568,6 @@ static int rk808_remove(struct i2c_client *client)
return 0;
}
static const struct i2c_device_id rk808_ids[] = {
{ "rk805" },
{ "rk808" },
{ "rk818" },
{ },
};
MODULE_DEVICE_TABLE(i2c, rk808_ids);
static struct i2c_driver rk808_i2c_driver = {
.driver = {
.name = "rk808",
......@@ -583,7 +575,6 @@ static struct i2c_driver rk808_i2c_driver = {
},
.probe = rk808_probe,
.remove = rk808_remove,
.id_table = rk808_ids,
};
module_i2c_driver(rk808_i2c_driver);
......
......@@ -28,45 +28,33 @@
#include <linux/regmap.h>
static const struct mfd_cell s5m8751_devs[] = {
{
.name = "s5m8751-pmic",
}, {
.name = "s5m-charger",
}, {
.name = "s5m8751-codec",
},
{ .name = "s5m8751-pmic", },
{ .name = "s5m-charger", },
{ .name = "s5m8751-codec", },
};
static const struct mfd_cell s5m8763_devs[] = {
{
.name = "s5m8763-pmic",
}, {
.name = "s5m-rtc",
}, {
.name = "s5m-charger",
},
{ .name = "s5m8763-pmic", },
{ .name = "s5m-rtc", },
{ .name = "s5m-charger", },
};
static const struct mfd_cell s5m8767_devs[] = {
{ .name = "s5m8767-pmic", },
{ .name = "s5m-rtc", },
{
.name = "s5m8767-pmic",
}, {
.name = "s5m-rtc",
}, {
.name = "s5m8767-clk",
.of_compatible = "samsung,s5m8767-clk",
}
},
};
static const struct mfd_cell s2mps11_devs[] = {
{ .name = "s2mps11-regulator", },
{ .name = "s2mps14-rtc", },
{
.name = "s2mps11-regulator",
}, {
.name = "s2mps14-rtc",
}, {
.name = "s2mps11-clk",
.of_compatible = "samsung,s2mps11-clk",
}
},
};
static const struct mfd_cell s2mps13_devs[] = {
......@@ -79,37 +67,30 @@ static const struct mfd_cell s2mps13_devs[] = {
};
static const struct mfd_cell s2mps14_devs[] = {
{ .name = "s2mps14-regulator", },
{ .name = "s2mps14-rtc", },
{
.name = "s2mps14-regulator",
}, {
.name = "s2mps14-rtc",
}, {
.name = "s2mps14-clk",
.of_compatible = "samsung,s2mps14-clk",
}
},
};
static const struct mfd_cell s2mps15_devs[] = {
{ .name = "s2mps15-regulator", },
{ .name = "s2mps15-rtc", },
{
.name = "s2mps15-regulator",
}, {
.name = "s2mps15-rtc",
}, {
.name = "s2mps13-clk",
.of_compatible = "samsung,s2mps13-clk",
},
};
static const struct mfd_cell s2mpa01_devs[] = {
{
.name = "s2mpa01-pmic",
},
{ .name = "s2mpa01-pmic", },
{ .name = "s2mps14-rtc", },
};
static const struct mfd_cell s2mpu02_devs[] = {
{
.name = "s2mpu02-regulator",
},
{ .name = "s2mpu02-regulator", },
};
#ifdef CONFIG_OF
......
......@@ -455,6 +455,9 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic)
case S5M8767X:
sec_irq_chip = &s5m8767_irq_chip;
break;
case S2MPA01:
sec_irq_chip = &s2mps14_irq_chip;
break;
case S2MPS11X:
sec_irq_chip = &s2mps11_irq_chip;
break;
......
......@@ -80,8 +80,6 @@ struct ssbi {
int (*write)(struct ssbi *, u16 addr, const u8 *buf, int len);
};
#define to_ssbi(dev) platform_get_drvdata(to_platform_device(dev))
static inline u32 ssbi_readl(struct ssbi *ssbi, u32 reg)
{
return readl(ssbi->base + reg);
......@@ -243,7 +241,7 @@ ssbi_pa_write_bytes(struct ssbi *ssbi, u16 addr, const u8 *buf, int len)
int ssbi_read(struct device *dev, u16 addr, u8 *buf, int len)
{
struct ssbi *ssbi = to_ssbi(dev);
struct ssbi *ssbi = dev_get_drvdata(dev);
unsigned long flags;
int ret;
......@@ -257,7 +255,7 @@ EXPORT_SYMBOL_GPL(ssbi_read);
int ssbi_write(struct device *dev, u16 addr, const u8 *buf, int len)
{
struct ssbi *ssbi = to_ssbi(dev);
struct ssbi *ssbi = dev_get_drvdata(dev);
unsigned long flags;
int ret;
......
// 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");
......@@ -148,13 +148,12 @@ static const struct of_device_id sun6i_prcm_dt_ids[] = {
static int sun6i_prcm_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *match;
const struct prcm_data *data;
struct resource *res;
int ret;
match = of_match_node(sun6i_prcm_dt_ids, np);
match = of_match_node(sun6i_prcm_dt_ids, pdev->dev.of_node);
if (!match)
return -EINVAL;
......
......@@ -12,6 +12,7 @@
* (at your option) any later version.
*/
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/hwspinlock.h>
#include <linux/io.h>
......@@ -45,6 +46,7 @@ static const struct regmap_config syscon_regmap_config = {
static struct syscon *of_syscon_register(struct device_node *np)
{
struct clk *clk;
struct syscon *syscon;
struct regmap *regmap;
void __iomem *base;
......@@ -119,6 +121,18 @@ static struct syscon *of_syscon_register(struct device_node *np)
goto err_regmap;
}
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
/* clock is optional */
if (ret != -ENOENT)
goto err_clk;
} else {
ret = regmap_mmio_attach_clk(regmap, clk);
if (ret)
goto err_attach;
}
syscon->regmap = regmap;
syscon->np = np;
......@@ -128,6 +142,11 @@ static struct syscon *of_syscon_register(struct device_node *np)
return syscon;
err_attach:
if (!IS_ERR(clk))
clk_put(clk);
err_clk:
regmap_exit(regmap);
err_regmap:
iounmap(base);
err_map:
......
......@@ -82,8 +82,7 @@ struct t7l66xb {
static int t7l66xb_mmc_enable(struct platform_device *mmc)
{
struct platform_device *dev = to_platform_device(mmc->dev.parent);
struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
struct t7l66xb *t7l66xb = dev_get_drvdata(mmc->dev.parent);
unsigned long flags;
u8 dev_ctl;
int ret;
......@@ -108,8 +107,7 @@ static int t7l66xb_mmc_enable(struct platform_device *mmc)
static int t7l66xb_mmc_disable(struct platform_device *mmc)
{
struct platform_device *dev = to_platform_device(mmc->dev.parent);
struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
struct t7l66xb *t7l66xb = dev_get_drvdata(mmc->dev.parent);
unsigned long flags;
u8 dev_ctl;
......@@ -128,16 +126,14 @@ static int t7l66xb_mmc_disable(struct platform_device *mmc)
static void t7l66xb_mmc_pwr(struct platform_device *mmc, int state)
{
struct platform_device *dev = to_platform_device(mmc->dev.parent);
struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
struct t7l66xb *t7l66xb = dev_get_drvdata(mmc->dev.parent);
tmio_core_mmc_pwr(t7l66xb->scr + 0x200, 0, state);
}
static void t7l66xb_mmc_clk_div(struct platform_device *mmc, int state)
{
struct platform_device *dev = to_platform_device(mmc->dev.parent);
struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
struct t7l66xb *t7l66xb = dev_get_drvdata(mmc->dev.parent);
tmio_core_mmc_clk_div(t7l66xb->scr + 0x200, 0, state);
}
......
......@@ -80,16 +80,14 @@ static int tc6387xb_resume(struct platform_device *dev)
static void tc6387xb_mmc_pwr(struct platform_device *mmc, int state)
{
struct platform_device *dev = to_platform_device(mmc->dev.parent);
struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
struct tc6387xb *tc6387xb = dev_get_drvdata(mmc->dev.parent);
tmio_core_mmc_pwr(tc6387xb->scr + 0x200, 0, state);
}
static void tc6387xb_mmc_clk_div(struct platform_device *mmc, int state)
{
struct platform_device *dev = to_platform_device(mmc->dev.parent);
struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
struct tc6387xb *tc6387xb = dev_get_drvdata(mmc->dev.parent);
tmio_core_mmc_clk_div(tc6387xb->scr + 0x200, 0, state);
}
......@@ -97,8 +95,7 @@ static void tc6387xb_mmc_clk_div(struct platform_device *mmc, int state)
static int tc6387xb_mmc_enable(struct platform_device *mmc)
{
struct platform_device *dev = to_platform_device(mmc->dev.parent);
struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
struct tc6387xb *tc6387xb = dev_get_drvdata(mmc->dev.parent);
clk_prepare_enable(tc6387xb->clk32k);
......@@ -110,8 +107,7 @@ static int tc6387xb_mmc_enable(struct platform_device *mmc)
static int tc6387xb_mmc_disable(struct platform_device *mmc)
{
struct platform_device *dev = to_platform_device(mmc->dev.parent);
struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
struct tc6387xb *tc6387xb = dev_get_drvdata(mmc->dev.parent);
clk_disable_unprepare(tc6387xb->clk32k);
......
......@@ -122,14 +122,13 @@ enum {
static int tc6393xb_nand_enable(struct platform_device *nand)
{
struct platform_device *dev = to_platform_device(nand->dev.parent);
struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
struct tc6393xb *tc6393xb = dev_get_drvdata(nand->dev.parent);
unsigned long flags;
raw_spin_lock_irqsave(&tc6393xb->lock, flags);
/* SMD buffer on */
dev_dbg(&dev->dev, "SMD buffer on\n");
dev_dbg(nand->dev.parent, "SMD buffer on\n");
tmio_iowrite8(0xff, tc6393xb->scr + SCR_GPI_BCR(1));
raw_spin_unlock_irqrestore(&tc6393xb->lock, flags);
......@@ -312,8 +311,7 @@ static int tc6393xb_fb_disable(struct platform_device *dev)
int tc6393xb_lcd_set_power(struct platform_device *fb, bool on)
{
struct platform_device *dev = to_platform_device(fb->dev.parent);
struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
struct tc6393xb *tc6393xb = dev_get_drvdata(fb->dev.parent);
u8 fer;
unsigned long flags;
......@@ -334,8 +332,7 @@ EXPORT_SYMBOL(tc6393xb_lcd_set_power);
int tc6393xb_lcd_mode(struct platform_device *fb,
const struct fb_videomode *mode) {
struct platform_device *dev = to_platform_device(fb->dev.parent);
struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
struct tc6393xb *tc6393xb = dev_get_drvdata(fb->dev.parent);
unsigned long flags;
raw_spin_lock_irqsave(&tc6393xb->lock, flags);
......@@ -351,8 +348,7 @@ EXPORT_SYMBOL(tc6393xb_lcd_mode);
static int tc6393xb_mmc_enable(struct platform_device *mmc)
{
struct platform_device *dev = to_platform_device(mmc->dev.parent);
struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
struct tc6393xb *tc6393xb = dev_get_drvdata(mmc->dev.parent);
tmio_core_mmc_enable(tc6393xb->scr + 0x200, 0,
tc6393xb_mmc_resources[0].start & 0xfffe);
......@@ -362,8 +358,7 @@ static int tc6393xb_mmc_enable(struct platform_device *mmc)
static int tc6393xb_mmc_resume(struct platform_device *mmc)
{
struct platform_device *dev = to_platform_device(mmc->dev.parent);
struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
struct tc6393xb *tc6393xb = dev_get_drvdata(mmc->dev.parent);
tmio_core_mmc_resume(tc6393xb->scr + 0x200, 0,
tc6393xb_mmc_resources[0].start & 0xfffe);
......@@ -373,16 +368,14 @@ static int tc6393xb_mmc_resume(struct platform_device *mmc)
static void tc6393xb_mmc_pwr(struct platform_device *mmc, int state)
{
struct platform_device *dev = to_platform_device(mmc->dev.parent);
struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
struct tc6393xb *tc6393xb = dev_get_drvdata(mmc->dev.parent);
tmio_core_mmc_pwr(tc6393xb->scr + 0x200, 0, state);
}
static void tc6393xb_mmc_clk_div(struct platform_device *mmc, int state)
{
struct platform_device *dev = to_platform_device(mmc->dev.parent);
struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
struct tc6393xb *tc6393xb = dev_get_drvdata(mmc->dev.parent);
tmio_core_mmc_clk_div(tc6393xb->scr + 0x200, 0, state);
}
......
......@@ -27,6 +27,7 @@ static const struct of_device_id tps65912_spi_of_match_table[] = {
{ .compatible = "ti,tps65912", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, tps65912_spi_of_match_table);
static int tps65912_spi_probe(struct spi_device *spi)
{
......
......@@ -322,8 +322,19 @@ int twl6040_power(struct twl6040 *twl6040, int on)
}
}
/*
* Register access can produce errors after power-up unless we
* wait at least 8ms based on measurements on duovero.
*/
usleep_range(10000, 12000);
/* Sync with the HW */
regcache_sync(twl6040->regmap);
ret = regcache_sync(twl6040->regmap);
if (ret) {
dev_err(twl6040->dev, "Failed to sync with the HW: %i\n",
ret);
goto out;
}
/* Default PLL configuration after power up */
twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL;
......
......@@ -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);
......
......@@ -273,6 +273,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
......
......@@ -41,6 +41,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
......
// 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");
......@@ -429,6 +429,12 @@ int cros_ec_query_all(struct cros_ec_device *ec_dev)
else
ec_dev->mkbp_event_supported = 1;
/* Probe if host sleep v1 is supported for S0ix failure detection. */
ret = cros_ec_get_host_command_version_mask(ec_dev,
EC_CMD_HOST_SLEEP_EVENT,
&ver_mask);
ec_dev->host_sleep_v1 = (ret >= 0 && (ver_mask & EC_VER_MASK(1)));
/*
* Get host event wake mask, assume all events are wake events
* if unavailable.
......
......@@ -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
......
......@@ -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
......
// 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, &reg);
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, &reg);
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, &reg);
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, &reg);
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");
/* 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__ */
......@@ -23,7 +23,10 @@
#include <linux/mutex.h>
#define CROS_EC_DEV_NAME "cros_ec"
#define CROS_EC_DEV_FP_NAME "cros_fp"
#define CROS_EC_DEV_PD_NAME "cros_pd"
#define CROS_EC_DEV_TP_NAME "cros_tp"
#define CROS_EC_DEV_ISH_NAME "cros_ish"
/*
* The EC is unresponsive for a time after a reboot command. Add a
......@@ -120,6 +123,7 @@ struct cros_ec_command {
* @pkt_xfer: Send packet to EC and get response.
* @lock: One transaction at a time.
* @mkbp_event_supported: True if this EC supports the MKBP event protocol.
* @host_sleep_v1: True if this EC supports the sleep v1 command.
* @event_notifier: Interrupt event notifier for transport devices.
* @event_data: Raw payload transferred with the MKBP event.
* @event_size: Size in bytes of the event data.
......@@ -153,6 +157,7 @@ struct cros_ec_device {
struct cros_ec_command *msg);
struct mutex lock;
bool mkbp_event_supported;
bool host_sleep_v1;
struct blocking_notifier_head event_notifier;
struct ec_response_get_next_event_v1 event_data;
......
......@@ -840,7 +840,7 @@ enum ec_feature_code {
* (Common Smart Battery System Interface Specification)
*/
EC_FEATURE_SMART_BATTERY = 18,
/* EC can dectect when the host hangs. */
/* EC can detect when the host hangs. */
EC_FEATURE_HANG_DETECT = 19,
/* Report power information, for pit only */
EC_FEATURE_PMU = 20,
......@@ -852,10 +852,42 @@ enum ec_feature_code {
EC_FEATURE_USB_MUX = 23,
/* Motion Sensor code has an internal software FIFO */
EC_FEATURE_MOTION_SENSE_FIFO = 24,
/* Support temporary secure vstore */
EC_FEATURE_VSTORE = 25,
/* EC decides on USB-C SS mux state, muxes configured by host */
EC_FEATURE_USBC_SS_MUX_VIRTUAL = 26,
/* EC has RTC feature that can be controlled by host commands */
EC_FEATURE_RTC = 27,
/* The MCU exposes a Fingerprint sensor */
EC_FEATURE_FINGERPRINT = 28,
/* The MCU exposes a Touchpad */
EC_FEATURE_TOUCHPAD = 29,
/* The MCU has RWSIG task enabled */
EC_FEATURE_RWSIG = 30,
/* EC has device events support */
EC_FEATURE_DEVICE_EVENT = 31,
/* EC supports the unified wake masks for LPC/eSPI systems */
EC_FEATURE_UNIFIED_WAKE_MASKS = 32,
/* EC supports 64-bit host events */
EC_FEATURE_HOST_EVENT64 = 33,
/* EC runs code in RAM (not in place, a.k.a. XIP) */
EC_FEATURE_EXEC_IN_RAM = 34,
/* EC supports CEC commands */
EC_FEATURE_CEC = 35,
/* EC supports tight sensor timestamping. */
EC_FEATURE_MOTION_SENSE_TIGHT_TIMESTAMPS = 36,
/*
* EC supports tablet mode detection aligned to Chrome and allows
* setting of threshold by host command using
* MOTIONSENSE_CMD_TABLET_MODE_LID_ANGLE.
*/
EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS = 37,
/* EC supports audio codec. */
EC_FEATURE_AUDIO_CODEC = 38,
/* EC Supports SCP. */
EC_FEATURE_SCP = 39,
/* The MCU is an Integrated Sensor Hub */
EC_FEATURE_ISH = 40,
};
#define EC_FEATURE_MASK_0(event_code) (1UL << (event_code % 32))
......@@ -2729,6 +2761,63 @@ struct ec_params_host_sleep_event {
uint8_t sleep_event;
} __packed;
/*
* Use a default timeout value (CONFIG_SLEEP_TIMEOUT_MS) for detecting sleep
* transition failures
*/
#define EC_HOST_SLEEP_TIMEOUT_DEFAULT 0
/* Disable timeout detection for this sleep transition */
#define EC_HOST_SLEEP_TIMEOUT_INFINITE 0xFFFF
struct ec_params_host_sleep_event_v1 {
/* The type of sleep being entered or exited. */
uint8_t sleep_event;
/* Padding */
uint8_t reserved;
union {
/* Parameters that apply for suspend messages. */
struct {
/*
* The timeout in milliseconds between when this message
* is received and when the EC will declare sleep
* transition failure if the sleep signal is not
* asserted.
*/
uint16_t sleep_timeout_ms;
} suspend_params;
/* No parameters for non-suspend messages. */
};
} __packed;
/* A timeout occurred when this bit is set */
#define EC_HOST_RESUME_SLEEP_TIMEOUT 0x80000000
/*
* The mask defining which bits correspond to the number of sleep transitions,
* as well as the maximum number of suspend line transitions that will be
* reported back to the host.
*/
#define EC_HOST_RESUME_SLEEP_TRANSITIONS_MASK 0x7FFFFFFF
struct ec_response_host_sleep_event_v1 {
union {
/* Response fields that apply for resume messages. */
struct {
/*
* The number of sleep power signal transitions that
* occurred since the suspend message. The high bit
* indicates a timeout occurred.
*/
uint32_t sleep_transitions;
} resume_response;
/* No response fields for non-resume messages. */
};
} __packed;
/*****************************************************************************/
/* Smart battery pass-through */
......
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Definitions for DA9063 MFD driver
*
......@@ -5,12 +6,6 @@
*
* Author: Michal Hajduk, Dialog Semiconductor
* Author: Krystian Garbaciak, Dialog Semiconductor
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef __MFD_DA9063_CORE_H__
......
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Registers definition for DA9063 modules
*
......@@ -5,12 +6,6 @@
*
* Author: Michal Hajduk, Dialog Semiconductor
* Author: Krystian Garbaciak, Dialog Semiconductor
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef _DA9063_REG_H
......@@ -215,9 +210,9 @@
/* DA9063 Configuration registers */
/* OTP */
#define DA9063_REG_OPT_COUNT 0x101
#define DA9063_REG_OPT_ADDR 0x102
#define DA9063_REG_OPT_DATA 0x103
#define DA9063_REG_OTP_CONT 0x101
#define DA9063_REG_OTP_ADDR 0x102
#define DA9063_REG_OTP_DATA 0x103
/* Customer Trim and Configuration */
#define DA9063_REG_T_OFFSET 0x104
......
......@@ -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 {
......
/* 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 */
/* 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
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2014 Atmel Corporation.
*
* Memory Controllers (MATRIX, EBI) - System peripherals registers.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef _LINUX_MFD_SYSCON_ATMEL_MATRIX_H
......
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2005 Ivan Kokshaysky
* Copyright (C) SAN People
......@@ -5,11 +6,6 @@
* Memory Controllers (MC, EBI, SMC, SDRAMC, BFC) - System peripherals
* registers.
* Based on AT91RM9200 datasheet revision E.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef _LINUX_MFD_SYSCON_ATMEL_MC_H_
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Atmel SMC (Static Memory Controller) register offsets and bit definitions.
*
......@@ -5,10 +6,6 @@
* Copyright (C) 2014 Free Electrons
*
* Author: Boris Brezillon <boris.brezillon@free-electrons.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _LINUX_MFD_SYSCON_ATMEL_SMC_H_
......
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2005 Ivan Kokshaysky
* Copyright (C) SAN People
*
* System Timer (ST) - System peripherals registers.
* Based on AT91RM9200 datasheet revision E.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef _LINUX_MFD_SYSCON_ATMEL_ST_H
......
......@@ -410,6 +410,15 @@
#define IMX6SX_GPR1_FEC_CLOCK_PAD_DIR_MASK (0x3 << 17)
#define IMX6SX_GPR1_FEC_CLOCK_MUX_SEL_EXT (0x3 << 13)
#define IMX6SX_GPR2_MQS_OVERSAMPLE_MASK (0x1 << 26)
#define IMX6SX_GPR2_MQS_OVERSAMPLE_SHIFT (26)
#define IMX6SX_GPR2_MQS_EN_MASK (0x1 << 25)
#define IMX6SX_GPR2_MQS_EN_SHIFT (25)
#define IMX6SX_GPR2_MQS_SW_RST_MASK (0x1 << 24)
#define IMX6SX_GPR2_MQS_SW_RST_SHIFT (24)
#define IMX6SX_GPR2_MQS_CLK_DIV_MASK (0xFF << 16)
#define IMX6SX_GPR2_MQS_CLK_DIV_SHIFT (16)
#define IMX6SX_GPR4_FEC_ENET1_STOP_REQ (0x1 << 3)
#define IMX6SX_GPR4_FEC_ENET2_STOP_REQ (0x1 << 4)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment