From 254a1f3c99a65578044558234a01b4fbc180b3d6 Mon Sep 17 00:00:00 2001 From: Romuald JEANNE Date: Tue, 13 Nov 2018 12:22:30 +0100 Subject: [PATCH 08/52] ARM: stm32mp1-r0-rc1: MFD --- drivers/mfd/Kconfig | 31 ++ drivers/mfd/Makefile | 4 +- drivers/mfd/stm32-pwr.c | 287 ++++++++++++++++ drivers/mfd/stmfx.c | 626 +++++++++++++++++++++++++++++++++++ drivers/mfd/stpmic1.c | 413 +++++++++++++++++++++++ include/dt-bindings/mfd/st,stpmic1.h | 46 +++ include/linux/mfd/stmfx.h | 27 ++ include/linux/mfd/stpmic1.h | 213 ++++++++++++ 8 files changed, 1646 insertions(+), 1 deletion(-) create mode 100644 drivers/mfd/stm32-pwr.c create mode 100644 drivers/mfd/stmfx.c create mode 100644 drivers/mfd/stpmic1.c create mode 100644 include/dt-bindings/mfd/st,stpmic1.h create mode 100644 include/linux/mfd/stmfx.h create mode 100644 include/linux/mfd/stpmic1.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 11841f4..cc25a19 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1855,6 +1855,37 @@ config MFD_STM32_TIMERS for PWM and IIO Timer. This driver allow to share the registers between the others drivers. +config MFD_STM32MP1_PWR + bool "STM32MP1 wake-up pins" + depends on MACH_STM32MP157 + default y + +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. + +config MFD_STPMIC1 + tristate "Support for STPMIC1 PMIC" + depends on (I2C=y && OF) + select REGMAP_I2C + select REGMAP_IRQ + select MFD_CORE + help + Support for ST Microelectronics STPMIC1 PMIC. STPMIC1 MFD driver is + the core driver for STPMIC1 component that mainly handles interrupts. + + To compile this driver as a module, choose M here: the + module will be called stpmic1. + menu "Multimedia Capabilities Port drivers" depends on ARCH_SA1100 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 5856a94..d794a2d 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -232,12 +232,14 @@ 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_STPMIC1) += stpmic1.o obj-$(CONFIG_MFD_SUN4I_GPADC) += sun4i-gpadc.o obj-$(CONFIG_MFD_STM32_LPTIMER) += stm32-lptimer.o obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o +obj-$(CONFIG_MFD_STM32MP1_PWR) += stm32-pwr.o obj-$(CONFIG_MFD_MXS_LRADC) += mxs-lradc.o obj-$(CONFIG_MFD_SC27XX_PMIC) += sprd-sc27xx-spi.o obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.o obj-$(CONFIG_MFD_ROHM_BD718XX) += rohm-bd718x7.o - +obj-$(CONFIG_MFD_STMFX) += stmfx.o diff --git a/drivers/mfd/stm32-pwr.c b/drivers/mfd/stm32-pwr.c new file mode 100644 index 0000000..377e2f5 --- /dev/null +++ b/drivers/mfd/stm32-pwr.c @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) STMicroelectronics 2017 - All Rights Reserved + * Author: Pascal Paillet for STMicroelectronics. + */ + +#include +#include +#include +#include +#include + +#define NB_WAKEUPPINS 6 + +#define STM32_SVC_PWR 0x82001001 +#define STM32_WRITE 0x1 +#define STM32_SET_BITS 0x2 +#define STM32_CLEAR_BITS 0x3 + +#define SMC_PWR_BASE 0x50001000 +// PWR Registers +#define WKUPCR 0x20 +#define WKUPFR 0x24 +#define MPUWKUPENR 0x28 + +#define WKUP_FLAGS_MASK GENMASK(5, 0) + +// WKUPCR bits definition +#define WKUP_EDGE_SHIFT 8 +#define WKUP_PULL_SHIFT 16 +#define WKUP_PULL_MASK GENMASK(1, 0) + +enum wkup_pull_setting { + WKUP_NO_PULL = 0, + WKUP_PULL_UP, + WKUP_PULL_DOWN, + WKUP_PULL_RESERVED +}; + +#define SMC(class, op, offset, val) \ +{ \ + struct arm_smccc_res res; \ + arm_smccc_smc(class, op, SMC_PWR_BASE + offset, val, \ + 0, 0, 0, 0, &res); \ +} \ + +struct stm32_pwr_data { + struct device *dev; /* self device */ + void __iomem *base; /* IO Memory base address */ + struct irq_domain *domain; /* Domain for this controller */ + int irq; /* Parent interrupt */ +}; + +static void stm32_pwr_irq_ack(struct irq_data *d) +{ + struct stm32_pwr_data *priv = d->domain->host_data; + + dev_dbg(priv->dev, "irq:%lu\n", d->hwirq); + SMC(STM32_SVC_PWR, STM32_SET_BITS, WKUPCR, BIT(d->hwirq)); +} + +static void stm32_pwr_irq_mask(struct irq_data *d) +{ + struct stm32_pwr_data *priv = d->domain->host_data; + + dev_dbg(priv->dev, "irq:%lu\n", d->hwirq); + SMC(STM32_SVC_PWR, STM32_CLEAR_BITS, MPUWKUPENR, BIT(d->hwirq)); +} + +static void stm32_pwr_irq_unmask(struct irq_data *d) +{ + struct stm32_pwr_data *priv = d->domain->host_data; + + dev_dbg(priv->dev, "irq:%lu\n", d->hwirq); + SMC(STM32_SVC_PWR, STM32_SET_BITS, MPUWKUPENR, BIT(d->hwirq)); +} + +static int stm32_pwr_irq_set_wake(struct irq_data *d, unsigned int on) +{ + struct stm32_pwr_data *priv = d->domain->host_data; + + dev_dbg(priv->dev, "irq:%lu on:%d\n", d->hwirq, on); + if (on) + enable_irq_wake(priv->irq); + else + disable_irq_wake(priv->irq); + + return 0; +} + +static int stm32_pwr_irq_set_type(struct irq_data *d, unsigned int flow_type) +{ + struct stm32_pwr_data *priv = d->domain->host_data; + int pin_id = d->hwirq; + u32 wkupcr; + int en; + + dev_dbg(priv->dev, "irq:%lu\n", d->hwirq); + + en = readl_relaxed(priv->base + MPUWKUPENR) & BIT(pin_id); + /* reference manual request to disable the wakeup pin while + * changing the edge detection setting + */ + if (en) + stm32_pwr_irq_mask(d); + + wkupcr = readl_relaxed(priv->base + WKUPCR); + switch (flow_type & IRQ_TYPE_SENSE_MASK) { + case IRQF_TRIGGER_FALLING: + wkupcr |= (1 << (WKUP_EDGE_SHIFT + pin_id)); + break; + case IRQF_TRIGGER_RISING: + wkupcr &= ~(1 << (WKUP_EDGE_SHIFT + pin_id)); + break; + default: + return -EINVAL; + } + SMC(STM32_SVC_PWR, STM32_WRITE, WKUPCR, wkupcr); + + if (en) + stm32_pwr_irq_unmask(d); + + return 0; +} + +static struct irq_chip stm32_pwr_irq_chip = { + .name = "stm32_pwr-irq", + .irq_ack = stm32_pwr_irq_ack, + .irq_mask = stm32_pwr_irq_mask, + .irq_unmask = stm32_pwr_irq_unmask, + .irq_set_type = stm32_pwr_irq_set_type, + .irq_set_wake = stm32_pwr_irq_set_wake, +}; + +static int stm32_pwr_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_and_handler(virq, &stm32_pwr_irq_chip, handle_edge_irq); + return 0; +} + +static int stm32_pwr_irq_set_pull_config(struct irq_domain *d, int pin_id, + enum wkup_pull_setting config) +{ + struct stm32_pwr_data *priv = d->host_data; + u32 wkupcr; + + dev_dbg(priv->dev, "irq:%d pull config:0x%x\n", pin_id, config); + + if (config >= WKUP_PULL_RESERVED) { + dev_err(priv->dev, "%s: bad irq pull config\n", __func__); + return -EINVAL; + } + + wkupcr = readl_relaxed(priv->base + WKUPCR); + wkupcr &= ~((WKUP_PULL_MASK) << (WKUP_PULL_SHIFT + pin_id * 2)); + wkupcr |= (config & WKUP_PULL_MASK) << (WKUP_PULL_SHIFT + pin_id * 2); + SMC(STM32_SVC_PWR, STM32_WRITE, WKUPCR, wkupcr); + + return 0; +} + +static int stm32_pwr_xlate(struct irq_domain *d, struct device_node *ctrlr, + const u32 *intspec, unsigned int intsize, + irq_hw_number_t *out_hwirq, unsigned int *out_type) +{ + struct stm32_pwr_data *priv = d->host_data; + + if (WARN_ON(intsize < 3)) { + dev_err(priv->dev, "%s: bad irq config parameters\n", __func__); + return -EINVAL; + } + + *out_hwirq = intspec[0]; + *out_type = intspec[1] & (IRQ_TYPE_SENSE_MASK); + + return stm32_pwr_irq_set_pull_config(d, intspec[0], intspec[2]); +} + +static const struct irq_domain_ops stm32_pwr_irq_domain_ops = { + .map = stm32_pwr_irq_map, + .xlate = stm32_pwr_xlate, +}; + +/* + * Handler for the cascaded IRQ. + */ +static void stm32_pwr_handle_irq(struct irq_desc *desc) +{ + struct stm32_pwr_data *priv = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + u32 wkupfr, wkupenr, i; + + chained_irq_enter(chip, desc); + + wkupfr = readl_relaxed(priv->base + WKUPFR); + wkupenr = readl_relaxed(priv->base + MPUWKUPENR); + for (i = 0; i < NB_WAKEUPPINS; i++) { + if ((wkupfr & BIT(i)) && (wkupenr & BIT(i))) { + dev_dbg(priv->dev, "handle wkup irq:%d\n", i); + generic_handle_irq(irq_find_mapping(priv->domain, i)); + } + } + + chained_irq_exit(chip, desc); +} + +static int stm32_pwr_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = pdev->dev.of_node; + struct stm32_pwr_data *priv; + + struct resource *res; + int ret; + + priv = devm_kzalloc(dev, sizeof(struct stm32_pwr_data), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + priv->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) { + dev_err(dev, "%s: Unable to map IO memory\n", __func__); + return PTR_ERR(priv->base); + } + + /* Disable all wake-up pins */ + SMC(STM32_SVC_PWR, STM32_WRITE, MPUWKUPENR, 0); + /* Clear all interrupts flags */ + SMC(STM32_SVC_PWR, STM32_SET_BITS, WKUPCR, WKUP_FLAGS_MASK); + + priv->domain = irq_domain_add_linear(np, NB_WAKEUPPINS, + &stm32_pwr_irq_domain_ops, priv); + if (!priv->domain) { + dev_err(dev, "%s: Unable to add irq domain!\n", __func__); + goto out; + } + + ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(dev, "failed to get PWR IRQ\n"); + goto err; + } + priv->irq = ret; + + irq_set_chained_handler_and_data(priv->irq, + stm32_pwr_handle_irq, priv); + +out: + return 0; +err: + irq_domain_remove(priv->domain); + return ret; +} + +static int stm32_pwr_remove(struct platform_device *pdev) +{ + struct stm32_pwr_data *priv = platform_get_drvdata(pdev); + + irq_domain_remove(priv->domain); + return 0; +} + +static const struct of_device_id stm32_pwr_match[] = { + { .compatible = "st,stm32mp1-pwr" }, + {}, +}; + +static struct platform_driver stm32_pwr_driver = { + .probe = stm32_pwr_probe, + .remove = stm32_pwr_remove, + .driver = { + .name = "stm32-pwr", + .owner = THIS_MODULE, + .of_match_table = stm32_pwr_match, + }, +}; + +static int __init stm32_pwr_init(void) +{ + return platform_driver_register(&stm32_pwr_driver); +} +arch_initcall(stm32_pwr_init); diff --git a/drivers/mfd/stmfx.c b/drivers/mfd/stmfx.c new file mode 100644 index 0000000..cfd4fca --- /dev/null +++ b/drivers/mfd/stmfx.c @@ -0,0 +1,626 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for STMicroelectronics Multi-Function eXpander (STMFX) core + * + * Copyright (C) 2018 STMicroelectronics + * Author(s): Amelie Delaunay . + */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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_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, +}; + +/** + * struct stmfx_ddata - STMFX MFD private structure + * @stmfx: state holder with device for logs and 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_ddata { + struct stmfx *stmfx; + 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 +}; + +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_ddata *ddata = irq_data_get_irq_chip_data(data); + + mutex_lock(&ddata->lock); +} + +static void stmfx_irq_bus_sync_unlock(struct irq_data *data) +{ + struct stmfx_ddata *ddata = irq_data_get_irq_chip_data(data); + + regmap_write(ddata->stmfx->map, STMFX_REG_IRQ_SRC_EN, ddata->irq_src); + + mutex_unlock(&ddata->lock); +} + +static void stmfx_irq_mask(struct irq_data *data) +{ + struct stmfx_ddata *ddata = irq_data_get_irq_chip_data(data); + + ddata->irq_src &= ~BIT(data->hwirq % 8); +} + +static void stmfx_irq_unmask(struct irq_data *data) +{ + struct stmfx_ddata *ddata = irq_data_get_irq_chip_data(data); + + ddata->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_ddata *ddata = data; + unsigned long n, pending; + u32 ack; + int ret; + + ret = regmap_read(ddata->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(ddata->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(ddata->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 stmfx_ddata *ddata) +{ + int hwirq; + + for (hwirq = 0; hwirq < STMFX_REG_IRQ_SRC_MAX; hwirq++) + irq_dispose_mapping(irq_find_mapping(ddata->irq_domain, hwirq)); + irq_domain_remove(ddata->irq_domain); +} + +static int stmfx_irq_init(struct stmfx_ddata *ddata, int irq) +{ + struct device *dev = ddata->stmfx->dev; + u32 irqoutpin = 0, irqtrigger; + int ret; + + ddata->irq_domain = irq_domain_add_simple(dev->of_node, + STMFX_REG_IRQ_SRC_MAX, 0, + &stmfx_irq_ops, ddata); + if (!ddata->irq_domain) { + dev_err(dev, "failed to create irq domain\n"); + return -EINVAL; + } + + if (!of_property_read_bool(dev->of_node, "drive-open-drain")) + irqoutpin |= STMFX_REG_IRQ_OUT_PIN_TYPE; + + irqtrigger = irq_get_trigger_type(irq); + if ((irqtrigger & IRQ_TYPE_EDGE_RISING) || + (irqtrigger & IRQ_TYPE_LEVEL_HIGH)) + irqoutpin |= STMFX_REG_IRQ_OUT_PIN_POL; + + ret = regmap_write(ddata->stmfx->map, STMFX_REG_IRQ_OUT_PIN, irqoutpin); + if (ret) + return ret; + + ret = devm_request_threaded_irq(dev, irq, NULL, stmfx_irq_handler, + irqtrigger | IRQF_ONESHOT, + "stmfx", ddata); + if (ret) + stmfx_irq_exit(ddata); + + return ret; +} + +static int stmfx_chip_reset(struct stmfx_ddata *ddata) +{ + int ret; + + ret = regmap_write(ddata->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 stmfx_ddata *ddata, struct i2c_client *client) +{ + u32 id; + u8 version[2]; + int ret; + + ddata->vdd = devm_regulator_get_optional(&client->dev, "vdd"); + if (IS_ERR(ddata->vdd)) { + ret = PTR_ERR(ddata->vdd); + if (ret != -ENODEV) { + if (ret != -EPROBE_DEFER) + dev_err(&client->dev, + "no vdd regulator found:%d\n", ret); + return ret; + } + } + + if (!IS_ERR(ddata->vdd)) { + ret = regulator_enable(ddata->vdd); + if (ret) { + dev_err(&client->dev, "vdd enable failed: %d\n", ret); + return ret; + } + } + + ret = regmap_read(ddata->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(ddata->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]); + + return stmfx_chip_reset(ddata); + +err: + if (!IS_ERR(ddata->vdd)) + return regulator_disable(ddata->vdd); + + return ret; +} + +static int stmfx_chip_exit(struct stmfx_ddata *ddata) +{ + regmap_write(ddata->stmfx->map, STMFX_REG_IRQ_SRC_EN, 0); + regmap_write(ddata->stmfx->map, STMFX_REG_SYS_CTRL, 0); + + if (!IS_ERR(ddata->vdd)) + return regulator_disable(ddata->vdd); + + return 0; +} + +static const struct resource stmfx_pinctrl_resources[] = { + DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_GPIO), +}; + +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), + } +}; + +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 int stmfx_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct stmfx_ddata *ddata; + int i, ret; + + ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + /* State holder */ + ddata->stmfx = devm_kzalloc(dev, sizeof(*ddata->stmfx), GFP_KERNEL); + if (!ddata->stmfx) + return -ENOMEM; + + i2c_set_clientdata(client, ddata); + + ddata->stmfx->dev = dev; + + ddata->stmfx->map = devm_regmap_init_i2c(client, &stmfx_regmap_config); + if (IS_ERR(ddata->stmfx->map)) { + ret = PTR_ERR(ddata->stmfx->map); + dev_err(dev, "failed to allocate register map: %d\n", ret); + return ret; + } + + mutex_init(&ddata->lock); + + ret = stmfx_chip_init(ddata, 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(ddata, client->irq); + if (ret) + goto err_chip_exit; + + for (i = 0; i < ARRAY_SIZE(stmfx_cells); i++) { + stmfx_cells[i].platform_data = ddata->stmfx; + stmfx_cells[i].pdata_size = sizeof(struct stmfx); + } + + ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, + stmfx_cells, ARRAY_SIZE(stmfx_cells), NULL, + 0, ddata->irq_domain); + if (ret) + goto err_irq_exit; + + return 0; + +err_irq_exit: + stmfx_irq_exit(ddata); +err_chip_exit: + stmfx_chip_exit(ddata); + + return ret; +} + +static int stmfx_remove(struct i2c_client *client) +{ + struct stmfx_ddata *ddata = i2c_get_clientdata(client); + + stmfx_irq_exit(ddata); + + return stmfx_chip_exit(ddata); +} + +#ifdef CONFIG_PM_SLEEP +static int stmfx_backup_regs(struct stmfx_ddata *ddata) +{ + int ret; + + ret = regmap_raw_read(ddata->stmfx->map, STMFX_REG_SYS_CTRL, + &ddata->bkp_sysctrl, sizeof(ddata->bkp_sysctrl)); + if (ret) + return ret; + ret = regmap_raw_read(ddata->stmfx->map, STMFX_REG_IRQ_OUT_PIN, + &ddata->bkp_irqoutpin, + sizeof(ddata->bkp_irqoutpin)); + if (ret) + return ret; + + return 0; +} + +static int stmfx_restore_regs(struct stmfx_ddata *ddata) +{ + int ret; + + ret = regmap_raw_write(ddata->stmfx->map, STMFX_REG_SYS_CTRL, + &ddata->bkp_sysctrl, sizeof(ddata->bkp_sysctrl)); + if (ret) + return ret; + ret = regmap_raw_write(ddata->stmfx->map, STMFX_REG_IRQ_OUT_PIN, + &ddata->bkp_irqoutpin, + sizeof(ddata->bkp_irqoutpin)); + if (ret) + return ret; + ret = regmap_raw_write(ddata->stmfx->map, STMFX_REG_IRQ_SRC_EN, + &ddata->irq_src, sizeof(ddata->irq_src)); + if (ret) + return ret; + + return 0; +} + +static int stmfx_suspend(struct device *dev) +{ + struct stmfx_ddata *ddata = dev_get_drvdata(dev); + int ret; + + ret = stmfx_backup_regs(ddata); + if (ret) { + dev_err(ddata->stmfx->dev, "registers backup failure\n"); + return ret; + } + + if (!IS_ERR(ddata->vdd)) { + ret = regulator_disable(ddata->vdd); + if (ret) + return ret; + } + + return 0; +} + +static int stmfx_resume(struct device *dev) +{ + struct stmfx_ddata *ddata = dev_get_drvdata(dev); + int ret; + + if (!IS_ERR(ddata->vdd)) { + ret = regulator_enable(ddata->vdd); + if (ret) { + dev_err(ddata->stmfx->dev, + "vdd enable failed: %d\n", ret); + return ret; + } + } + + ret = stmfx_restore_regs(ddata); + if (ret) { + dev_err(ddata->stmfx->dev, "registers restoration failure\n"); + 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 "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/stpmic1.c b/drivers/mfd/stpmic1.c new file mode 100644 index 0000000..5bf6328 --- /dev/null +++ b/drivers/mfd/stpmic1.c @@ -0,0 +1,413 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) STMicroelectronics 2018 +// Author: Pascal Paillet + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define STPMIC1_MAIN_IRQ 0 +#define STPMIC1_WAKEUP_IRQ 1 + +static bool stpmic1_reg_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TURN_ON_SR: + case TURN_OFF_SR: + case ICC_LDO_TURN_OFF_SR: + case ICC_BUCK_TURN_OFF_SR: + case RREQ_STATE_SR: + case VERSION_SR: + case SWOFF_PWRCTRL_CR: + case PADS_PULL_CR: + case BUCKS_PD_CR: + case LDO14_PD_CR: + case LDO56_VREF_PD_CR: + case VBUS_DET_VIN_CR: + case PKEY_TURNOFF_CR: + case BUCKS_MASK_RANK_CR: + case BUCKS_MASK_RESET_CR: + case LDOS_MASK_RANK_CR: + case LDOS_MASK_RESET_CR: + case WCHDG_CR: + case WCHDG_TIMER_CR: + case BUCKS_ICCTO_CR: + case LDOS_ICCTO_CR: + case BUCK1_ACTIVE_CR: + case BUCK2_ACTIVE_CR: + case BUCK3_ACTIVE_CR: + case BUCK4_ACTIVE_CR: + case VREF_DDR_ACTIVE_CR: + case LDO1_ACTIVE_CR: + case LDO2_ACTIVE_CR: + case LDO3_ACTIVE_CR: + case LDO4_ACTIVE_CR: + case LDO5_ACTIVE_CR: + case LDO6_ACTIVE_CR: + case BUCK1_STDBY_CR: + case BUCK2_STDBY_CR: + case BUCK3_STDBY_CR: + case BUCK4_STDBY_CR: + case VREF_DDR_STDBY_CR: + case LDO1_STDBY_CR: + case LDO2_STDBY_CR: + case LDO3_STDBY_CR: + case LDO4_STDBY_CR: + case LDO5_STDBY_CR: + case LDO6_STDBY_CR: + case BST_SW_CR: + case INT_PENDING_R1: + case INT_PENDING_R2: + case INT_PENDING_R3: + case INT_PENDING_R4: + case INT_DBG_LATCH_R1: + case INT_DBG_LATCH_R2: + case INT_DBG_LATCH_R3: + case INT_DBG_LATCH_R4: + case INT_CLEAR_R1: + case INT_CLEAR_R2: + case INT_CLEAR_R3: + case INT_CLEAR_R4: + case INT_MASK_R1: + case INT_MASK_R2: + case INT_MASK_R3: + case INT_MASK_R4: + case INT_SET_MASK_R1: + case INT_SET_MASK_R2: + case INT_SET_MASK_R3: + case INT_SET_MASK_R4: + case INT_CLEAR_MASK_R1: + case INT_CLEAR_MASK_R2: + case INT_CLEAR_MASK_R3: + case INT_CLEAR_MASK_R4: + case INT_SRC_R1: + case INT_SRC_R2: + case INT_SRC_R3: + case INT_SRC_R4: + return true; + default: + return false; + } +} + +static bool stpmic1_reg_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SWOFF_PWRCTRL_CR: + case PADS_PULL_CR: + case BUCKS_PD_CR: + case LDO14_PD_CR: + case LDO56_VREF_PD_CR: + case VBUS_DET_VIN_CR: + case PKEY_TURNOFF_CR: + case BUCKS_MASK_RANK_CR: + case BUCKS_MASK_RESET_CR: + case LDOS_MASK_RANK_CR: + case LDOS_MASK_RESET_CR: + case WCHDG_CR: + case WCHDG_TIMER_CR: + case BUCKS_ICCTO_CR: + case LDOS_ICCTO_CR: + case BUCK1_ACTIVE_CR: + case BUCK2_ACTIVE_CR: + case BUCK3_ACTIVE_CR: + case BUCK4_ACTIVE_CR: + case VREF_DDR_ACTIVE_CR: + case LDO1_ACTIVE_CR: + case LDO2_ACTIVE_CR: + case LDO3_ACTIVE_CR: + case LDO4_ACTIVE_CR: + case LDO5_ACTIVE_CR: + case LDO6_ACTIVE_CR: + case BUCK1_STDBY_CR: + case BUCK2_STDBY_CR: + case BUCK3_STDBY_CR: + case BUCK4_STDBY_CR: + case VREF_DDR_STDBY_CR: + case LDO1_STDBY_CR: + case LDO2_STDBY_CR: + case LDO3_STDBY_CR: + case LDO4_STDBY_CR: + case LDO5_STDBY_CR: + case LDO6_STDBY_CR: + case BST_SW_CR: + case INT_DBG_LATCH_R1: + case INT_DBG_LATCH_R2: + case INT_DBG_LATCH_R3: + case INT_DBG_LATCH_R4: + case INT_CLEAR_R1: + case INT_CLEAR_R2: + case INT_CLEAR_R3: + case INT_CLEAR_R4: + case INT_SET_MASK_R1: + case INT_SET_MASK_R2: + case INT_SET_MASK_R3: + case INT_SET_MASK_R4: + case INT_CLEAR_MASK_R1: + case INT_CLEAR_MASK_R2: + case INT_CLEAR_MASK_R3: + case INT_CLEAR_MASK_R4: + return true; + default: + return false; + } +} + +static bool stpmic1_reg_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TURN_ON_SR: + case TURN_OFF_SR: + case ICC_LDO_TURN_OFF_SR: + case ICC_BUCK_TURN_OFF_SR: + case RREQ_STATE_SR: + case INT_PENDING_R1: + case INT_PENDING_R2: + case INT_PENDING_R3: + case INT_PENDING_R4: + case INT_SRC_R1: + case INT_SRC_R2: + case INT_SRC_R3: + case INT_SRC_R4: + case WCHDG_CR: + return true; + default: + return false; + } +} + +const struct regmap_config stpmic1_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .max_register = PMIC_MAX_REGISTER_ADDRESS, + .readable_reg = stpmic1_reg_readable, + .writeable_reg = stpmic1_reg_writeable, + .volatile_reg = stpmic1_reg_volatile, +}; + +static const struct regmap_irq stpmic1_irqs[] = { + [IT_PONKEY_F] = { .mask = 0x01 }, + [IT_PONKEY_R] = { .mask = 0x02 }, + [IT_WAKEUP_F] = { .mask = 0x04 }, + [IT_WAKEUP_R] = { .mask = 0x08 }, + [IT_VBUS_OTG_F] = { .mask = 0x10 }, + [IT_VBUS_OTG_R] = { .mask = 0x20 }, + [IT_SWOUT_F] = { .mask = 0x40 }, + [IT_SWOUT_R] = { .mask = 0x80 }, + + [IT_CURLIM_BUCK1] = { .reg_offset = 1, .mask = 0x01 }, + [IT_CURLIM_BUCK2] = { .reg_offset = 1, .mask = 0x02 }, + [IT_CURLIM_BUCK3] = { .reg_offset = 1, .mask = 0x04 }, + [IT_CURLIM_BUCK4] = { .reg_offset = 1, .mask = 0x08 }, + [IT_OCP_OTG] = { .reg_offset = 1, .mask = 0x10 }, + [IT_OCP_SWOUT] = { .reg_offset = 1, .mask = 0x20 }, + [IT_OCP_BOOST] = { .reg_offset = 1, .mask = 0x40 }, + [IT_OVP_BOOST] = { .reg_offset = 1, .mask = 0x80 }, + + [IT_CURLIM_LDO1] = { .reg_offset = 2, .mask = 0x01 }, + [IT_CURLIM_LDO2] = { .reg_offset = 2, .mask = 0x02 }, + [IT_CURLIM_LDO3] = { .reg_offset = 2, .mask = 0x04 }, + [IT_CURLIM_LDO4] = { .reg_offset = 2, .mask = 0x08 }, + [IT_CURLIM_LDO5] = { .reg_offset = 2, .mask = 0x10 }, + [IT_CURLIM_LDO6] = { .reg_offset = 2, .mask = 0x20 }, + [IT_SHORT_SWOTG] = { .reg_offset = 2, .mask = 0x40 }, + [IT_SHORT_SWOUT] = { .reg_offset = 2, .mask = 0x80 }, + + [IT_TWARN_F] = { .reg_offset = 3, .mask = 0x01 }, + [IT_TWARN_R] = { .reg_offset = 3, .mask = 0x02 }, + [IT_VINLOW_F] = { .reg_offset = 3, .mask = 0x04 }, + [IT_VINLOW_R] = { .reg_offset = 3, .mask = 0x08 }, + [IT_SWIN_F] = { .reg_offset = 3, .mask = 0x40 }, + [IT_SWIN_R] = { .reg_offset = 3, .mask = 0x80 }, +}; + +static const struct regmap_irq_chip stpmic1_regmap_irq_chip = { + .name = "pmic_irq", + .status_base = INT_PENDING_R1, + .mask_base = INT_CLEAR_MASK_R1, + .unmask_base = INT_SET_MASK_R1, + .ack_base = INT_CLEAR_R1, + .num_regs = STPMIC1_PMIC_NUM_IRQ_REGS, + .irqs = stpmic1_irqs, + .num_irqs = ARRAY_SIZE(stpmic1_irqs), +}; + +static int stpmic1_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct stpmic1 *ddata; + struct device *dev = &i2c->dev; + int ret; + struct device_node *np = dev->of_node; + u32 reg; + + ddata = devm_kzalloc(dev, sizeof(struct stpmic1), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + i2c_set_clientdata(i2c, ddata); + ddata->dev = dev; + + ddata->regmap = devm_regmap_init_i2c(i2c, &stpmic1_regmap_config); + if (IS_ERR(ddata->regmap)) + return PTR_ERR(ddata->regmap); + + ddata->irq = of_irq_get(np, STPMIC1_MAIN_IRQ); + if (ddata->irq < 0) { + dev_err(dev, "Failed to get main IRQ: %d\n", ddata->irq); + return ddata->irq; + } + + ddata->irq_wake = of_irq_get(np, STPMIC1_WAKEUP_IRQ); + if (ddata->irq_wake > 0) { + device_init_wakeup(dev, true); + ret = dev_pm_set_dedicated_wake_irq(dev, ddata->irq_wake); + if (ret) + dev_warn(dev, "failed to set up wakeup irq"); + } + + if (!of_property_read_u32(np, "st,main-control-register", ®)) { + ret = regmap_update_bits(ddata->regmap, + SWOFF_PWRCTRL_CR, + PWRCTRL_POLARITY_HIGH | + PWRCTRL_PIN_VALID | + RESTART_REQUEST_ENABLED, + reg); + if (ret) { + dev_err(dev, + "Failed to update main control register: %d\n", + ret); + return ret; + } + } + + /* Read Version ID */ + ret = regmap_read(ddata->regmap, VERSION_SR, ®); + if (ret) { + dev_err(dev, "Unable to read pmic version\n"); + return ret; + } + dev_info(dev, "PMIC Chip Version: 0x%x\n", reg); + + if (!of_property_read_u32(np, "st,pads-pull-register", ®)) { + ret = regmap_update_bits(ddata->regmap, + PADS_PULL_CR, + WAKEUP_DETECTOR_DISABLED | + PWRCTRL_PD_ACTIVE | + PWRCTRL_PU_ACTIVE | + WAKEUP_PD_ACTIVE, + reg); + if (ret) { + dev_err(dev, + "Failed to update pads control register: %d\n", + ret); + return ret; + } + } + + if (!of_property_read_u32(np, "st,vin-control-register", ®)) { + ret = regmap_update_bits(ddata->regmap, + VBUS_DET_VIN_CR, + VINLOW_CTRL_REG_MASK, + reg); + if (ret) { + dev_err(dev, + "Failed to update vin control register: %d\n", + ret); + return ret; + } + } + + if (!of_property_read_u32(np, "st,usb-control-register", ®)) { + ret = regmap_update_bits(ddata->regmap, BST_SW_CR, + BOOST_OVP_DISABLED | + VBUS_OTG_DETECTION_DISABLED | + SW_OUT_DISCHARGE | + VBUS_OTG_DISCHARGE | + OCP_LIMIT_HIGH, + reg); + if (ret) { + dev_err(dev, + "Failed to update usb control register: %d\n", + ret); + return ret; + } + } + + /* Initialize PMIC IRQ Chip & IRQ domains associated */ + ret = devm_regmap_add_irq_chip(dev, ddata->regmap, ddata->irq, + IRQF_ONESHOT | IRQF_SHARED, + 0, &stpmic1_regmap_irq_chip, + &ddata->irq_data); + if (ret) { + dev_err(dev, "IRQ Chip registration failed: %d\n", ret); + return ret; + } + + return devm_of_platform_populate(dev); +} + +static const struct i2c_device_id stpmic1_id[] = { + { "stpmic1"}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, stpmic1_id); + +#ifdef CONFIG_PM_SLEEP +static int stpmic1_suspend(struct device *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + struct stpmic1 *pmic_dev = i2c_get_clientdata(i2c); + + disable_irq(pmic_dev->irq); + if ((pmic_dev->irq_wake > 0) && device_may_wakeup(dev)) + enable_irq_wake(pmic_dev->irq_wake); + + return 0; +} + +static int stpmic1_resume(struct device *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + struct stpmic1 *pmic_dev = i2c_get_clientdata(i2c); + int ret; + + ret = regcache_sync(pmic_dev->regmap); + if (ret) + return ret; + + enable_irq(pmic_dev->irq); + if ((pmic_dev->irq_wake > 0) && device_may_wakeup(dev)) + disable_irq_wake(pmic_dev->irq_wake); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(stpmic1_pm, stpmic1_suspend, stpmic1_resume); + +static struct i2c_driver stpmic1_driver = { + .driver = { + .name = "stpmic1", + .pm = &stpmic1_pm, + }, + .probe = stpmic1_probe, + .id_table = stpmic1_id, +}; + +module_i2c_driver(stpmic1_driver); + +MODULE_DESCRIPTION("STPMIC1 PMIC Driver"); +MODULE_AUTHOR("Pascal Paillet "); +MODULE_LICENSE("GPL v2"); diff --git a/include/dt-bindings/mfd/st,stpmic1.h b/include/dt-bindings/mfd/st,stpmic1.h new file mode 100644 index 0000000..b2d6c83 --- /dev/null +++ b/include/dt-bindings/mfd/st,stpmic1.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved + * Author: Philippe Peurichard , + * Pascal Paillet for STMicroelectronics. + */ + +#ifndef __DT_BINDINGS_STPMIC1_H__ +#define __DT_BINDINGS_STPMIC1_H__ + +/* IRQ definitions */ +#define IT_PONKEY_F 0 +#define IT_PONKEY_R 1 +#define IT_WAKEUP_F 2 +#define IT_WAKEUP_R 3 +#define IT_VBUS_OTG_F 4 +#define IT_VBUS_OTG_R 5 +#define IT_SWOUT_F 6 +#define IT_SWOUT_R 7 + +#define IT_CURLIM_BUCK1 8 +#define IT_CURLIM_BUCK2 9 +#define IT_CURLIM_BUCK3 10 +#define IT_CURLIM_BUCK4 11 +#define IT_OCP_OTG 12 +#define IT_OCP_SWOUT 13 +#define IT_OCP_BOOST 14 +#define IT_OVP_BOOST 15 + +#define IT_CURLIM_LDO1 16 +#define IT_CURLIM_LDO2 17 +#define IT_CURLIM_LDO3 18 +#define IT_CURLIM_LDO4 19 +#define IT_CURLIM_LDO5 20 +#define IT_CURLIM_LDO6 21 +#define IT_SHORT_SWOTG 22 +#define IT_SHORT_SWOUT 23 + +#define IT_TWARN_F 24 +#define IT_TWARN_R 25 +#define IT_VINLOW_F 26 +#define IT_VINLOW_R 27 +#define IT_SWIN_F 30 +#define IT_SWIN_R 31 + +#endif /* __DT_BINDINGS_STPMIC1_H__ */ diff --git a/include/linux/mfd/stmfx.h b/include/linux/mfd/stmfx.h new file mode 100644 index 0000000..35c3d42 --- /dev/null +++ b/include/linux/mfd/stmfx.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 STMicroelectronics + * Author(s): Amelie Delaunay . + */ + +#ifndef MFD_STMFX_H +#define MFX_STMFX_H + +#include + +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 { + struct device *dev; + struct regmap *map; +}; + +int stmfx_function_enable(struct stmfx *stmfx, u32 func); +int stmfx_function_disable(struct stmfx *stmfx, u32 func); +#endif diff --git a/include/linux/mfd/stpmic1.h b/include/linux/mfd/stpmic1.h new file mode 100644 index 0000000..4abe5f1 --- /dev/null +++ b/include/linux/mfd/stpmic1.h @@ -0,0 +1,213 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved + * Author: Philippe Peurichard , + * Pascal Paillet for STMicroelectronics. + */ + +#ifndef __LINUX_MFD_STPMIC1_H +#define __LINUX_MFD_STPMIC1_H + +#define TURN_ON_SR 0x1 +#define TURN_OFF_SR 0x2 +#define ICC_LDO_TURN_OFF_SR 0x3 +#define ICC_BUCK_TURN_OFF_SR 0x4 +#define RREQ_STATE_SR 0x5 +#define VERSION_SR 0x6 + +#define SWOFF_PWRCTRL_CR 0x10 +#define PADS_PULL_CR 0x11 +#define BUCKS_PD_CR 0x12 +#define LDO14_PD_CR 0x13 +#define LDO56_VREF_PD_CR 0x14 +#define VBUS_DET_VIN_CR 0x15 +#define PKEY_TURNOFF_CR 0x16 +#define BUCKS_MASK_RANK_CR 0x17 +#define BUCKS_MASK_RESET_CR 0x18 +#define LDOS_MASK_RANK_CR 0x19 +#define LDOS_MASK_RESET_CR 0x1A +#define WCHDG_CR 0x1B +#define WCHDG_TIMER_CR 0x1C +#define BUCKS_ICCTO_CR 0x1D +#define LDOS_ICCTO_CR 0x1E + +#define BUCK1_ACTIVE_CR 0x20 +#define BUCK2_ACTIVE_CR 0x21 +#define BUCK3_ACTIVE_CR 0x22 +#define BUCK4_ACTIVE_CR 0x23 +#define VREF_DDR_ACTIVE_CR 0x24 +#define LDO1_ACTIVE_CR 0x25 +#define LDO2_ACTIVE_CR 0x26 +#define LDO3_ACTIVE_CR 0x27 +#define LDO4_ACTIVE_CR 0x28 +#define LDO5_ACTIVE_CR 0x29 +#define LDO6_ACTIVE_CR 0x2A + +#define BUCK1_STDBY_CR 0x30 +#define BUCK2_STDBY_CR 0x31 +#define BUCK3_STDBY_CR 0x32 +#define BUCK4_STDBY_CR 0x33 +#define VREF_DDR_STDBY_CR 0x34 +#define LDO1_STDBY_CR 0x35 +#define LDO2_STDBY_CR 0x36 +#define LDO3_STDBY_CR 0x37 +#define LDO4_STDBY_CR 0x38 +#define LDO5_STDBY_CR 0x39 +#define LDO6_STDBY_CR 0x3A + +#define BST_SW_CR 0x40 + +#define INT_PENDING_R1 0x50 +#define INT_PENDING_R2 0x51 +#define INT_PENDING_R3 0x52 +#define INT_PENDING_R4 0x53 + +#define INT_DBG_LATCH_R1 0x60 +#define INT_DBG_LATCH_R2 0x61 +#define INT_DBG_LATCH_R3 0x62 +#define INT_DBG_LATCH_R4 0x63 + +#define INT_CLEAR_R1 0x70 +#define INT_CLEAR_R2 0x71 +#define INT_CLEAR_R3 0x72 +#define INT_CLEAR_R4 0x73 + +#define INT_MASK_R1 0x80 +#define INT_MASK_R2 0x81 +#define INT_MASK_R3 0x82 +#define INT_MASK_R4 0x83 + +#define INT_SET_MASK_R1 0x90 +#define INT_SET_MASK_R2 0x91 +#define INT_SET_MASK_R3 0x92 +#define INT_SET_MASK_R4 0x93 + +#define INT_CLEAR_MASK_R1 0xA0 +#define INT_CLEAR_MASK_R2 0xA1 +#define INT_CLEAR_MASK_R3 0xA2 +#define INT_CLEAR_MASK_R4 0xA3 + +#define INT_SRC_R1 0xB0 +#define INT_SRC_R2 0xB1 +#define INT_SRC_R3 0xB2 +#define INT_SRC_R4 0xB3 + +#define PMIC_MAX_REGISTER_ADDRESS INT_SRC_R4 + +#define STPMIC1_PMIC_NUM_IRQ_REGS 4 + +#define TURN_OFF_SR_ICC_EVENT 0x08 + +#define LDO_VOLTAGE_MASK GENMASK(6, 2) +#define BUCK_VOLTAGE_MASK GENMASK(7, 2) +#define LDO_BUCK_VOLTAGE_SHIFT 2 + +#define LDO_ENABLE_MASK BIT(0) +#define BUCK_ENABLE_MASK BIT(0) + +#define BUCK_HPLP_ENABLE_MASK BIT(1) +#define BUCK_HPLP_SHIFT 1 + +#define STDBY_ENABLE_MASK BIT(0) + +#define BUCKS_PD_CR_REG_MASK GENMASK(7, 0) +#define BUCK_MASK_RANK_REGISTER_MASK GENMASK(3, 0) +#define BUCK_MASK_RESET_REGISTER_MASK GENMASK(3, 0) +#define LDO1234_PULL_DOWN_REGISTER_MASK GENMASK(7, 0) +#define LDO56_VREF_PD_CR_REG_MASK GENMASK(5, 0) +#define LDO_MASK_RANK_REGISTER_MASK GENMASK(5, 0) +#define LDO_MASK_RESET_REGISTER_MASK GENMASK(5, 0) + +#define BUCK1_PULL_DOWN_REG BUCKS_PD_CR +#define BUCK1_PULL_DOWN_MASK BIT(0) +#define BUCK2_PULL_DOWN_REG BUCKS_PD_CR +#define BUCK2_PULL_DOWN_MASK BIT(2) +#define BUCK3_PULL_DOWN_REG BUCKS_PD_CR +#define BUCK3_PULL_DOWN_MASK BIT(4) +#define BUCK4_PULL_DOWN_REG BUCKS_PD_CR +#define BUCK4_PULL_DOWN_MASK BIT(6) + +#define LDO1_PULL_DOWN_REG LDO14_PD_CR +#define LDO1_PULL_DOWN_MASK BIT(0) +#define LDO2_PULL_DOWN_REG LDO14_PD_CR +#define LDO2_PULL_DOWN_MASK BIT(2) +#define LDO3_PULL_DOWN_REG LDO14_PD_CR +#define LDO3_PULL_DOWN_MASK BIT(4) +#define LDO4_PULL_DOWN_REG LDO14_PD_CR +#define LDO4_PULL_DOWN_MASK BIT(6) +#define LDO5_PULL_DOWN_REG LDO56_VREF_PD_CR +#define LDO5_PULL_DOWN_MASK BIT(0) +#define LDO6_PULL_DOWN_REG LDO56_VREF_PD_CR +#define LDO6_PULL_DOWN_MASK BIT(2) +#define VREF_DDR_PULL_DOWN_REG LDO56_VREF_PD_CR +#define VREF_DDR_PULL_DOWN_MASK BIT(4) + +#define BUCKS_ICCTO_CR_REG_MASK GENMASK(6, 0) +#define LDOS_ICCTO_CR_REG_MASK GENMASK(5, 0) + +#define LDO_BYPASS_MASK BIT(7) + +/* Main PMIC Control Register + * SWOFF_PWRCTRL_CR + * Address : 0x10 + */ +#define ICC_EVENT_ENABLED BIT(4) +#define PWRCTRL_POLARITY_HIGH BIT(3) +#define PWRCTRL_PIN_VALID BIT(2) +#define RESTART_REQUEST_ENABLED BIT(1) +#define SOFTWARE_SWITCH_OFF_ENABLED BIT(0) + +/* Main PMIC PADS Control Register + * PADS_PULL_CR + * Address : 0x11 + */ +#define WAKEUP_DETECTOR_DISABLED BIT(4) +#define PWRCTRL_PD_ACTIVE BIT(3) +#define PWRCTRL_PU_ACTIVE BIT(2) +#define WAKEUP_PD_ACTIVE BIT(1) +#define PONKEY_PU_INACTIVE BIT(0) + +/* Main PMIC VINLOW Control Register + * VBUS_DET_VIN_CRC DMSC + * Address : 0x15 + */ +#define SWIN_DETECTOR_ENABLED BIT(7) +#define SWOUT_DETECTOR_ENABLED BIT(6) +#define VINLOW_ENABLED BIT(0) +#define VINLOW_CTRL_REG_MASK GENMASK(7, 0) + +/* USB Control Register + * Address : 0x40 + */ +#define BOOST_OVP_DISABLED BIT(7) +#define VBUS_OTG_DETECTION_DISABLED BIT(6) +#define SW_OUT_DISCHARGE BIT(5) +#define VBUS_OTG_DISCHARGE BIT(4) +#define OCP_LIMIT_HIGH BIT(3) +#define SWIN_SWOUT_ENABLED BIT(2) +#define USBSW_OTG_SWITCH_ENABLED BIT(1) +#define BOOST_ENABLED BIT(0) + +/* PKEY_TURNOFF_CR + * Address : 0x16 + */ +#define PONKEY_PWR_OFF BIT(7) +#define PONKEY_CC_FLAG_CLEAR BIT(6) +#define PONKEY_TURNOFF_TIMER_MASK GENMASK(3, 0) +#define PONKEY_TURNOFF_MASK GENMASK(7, 0) + +/* + * struct stpmic1 - stpmic1 master device for sub-drivers + * @dev: master device of the chip (can be used to access platform data) + * @irq: main IRQ number + * @regmap_irq_chip_data: irq chip data + */ +struct stpmic1 { + struct device *dev; + struct regmap *regmap; + int irq; + int irq_wake; + struct regmap_irq_chip_data *irq_data; +}; + +#endif /* __LINUX_MFD_STPMIC1_H */ -- 2.7.4