106252adeSAmelie Delaunay // SPDX-License-Identifier: GPL-2.0 206252adeSAmelie Delaunay /* 306252adeSAmelie Delaunay * Driver for STMicroelectronics Multi-Function eXpander (STMFX) core 406252adeSAmelie Delaunay * 506252adeSAmelie Delaunay * Copyright (C) 2019 STMicroelectronics 606252adeSAmelie Delaunay * Author(s): Amelie Delaunay <amelie.delaunay@st.com>. 706252adeSAmelie Delaunay */ 806252adeSAmelie Delaunay #include <linux/bitfield.h> 906252adeSAmelie Delaunay #include <linux/i2c.h> 1006252adeSAmelie Delaunay #include <linux/interrupt.h> 1106252adeSAmelie Delaunay #include <linux/irq.h> 1206252adeSAmelie Delaunay #include <linux/mfd/core.h> 1306252adeSAmelie Delaunay #include <linux/mfd/stmfx.h> 1406252adeSAmelie Delaunay #include <linux/module.h> 1506252adeSAmelie Delaunay #include <linux/regulator/consumer.h> 1606252adeSAmelie Delaunay 1706252adeSAmelie Delaunay static bool stmfx_reg_volatile(struct device *dev, unsigned int reg) 1806252adeSAmelie Delaunay { 1906252adeSAmelie Delaunay switch (reg) { 2006252adeSAmelie Delaunay case STMFX_REG_SYS_CTRL: 2106252adeSAmelie Delaunay case STMFX_REG_IRQ_SRC_EN: 2206252adeSAmelie Delaunay case STMFX_REG_IRQ_PENDING: 2306252adeSAmelie Delaunay case STMFX_REG_IRQ_GPI_PENDING1: 2406252adeSAmelie Delaunay case STMFX_REG_IRQ_GPI_PENDING2: 2506252adeSAmelie Delaunay case STMFX_REG_IRQ_GPI_PENDING3: 2606252adeSAmelie Delaunay case STMFX_REG_GPIO_STATE1: 2706252adeSAmelie Delaunay case STMFX_REG_GPIO_STATE2: 2806252adeSAmelie Delaunay case STMFX_REG_GPIO_STATE3: 2906252adeSAmelie Delaunay case STMFX_REG_IRQ_GPI_SRC1: 3006252adeSAmelie Delaunay case STMFX_REG_IRQ_GPI_SRC2: 3106252adeSAmelie Delaunay case STMFX_REG_IRQ_GPI_SRC3: 3206252adeSAmelie Delaunay case STMFX_REG_GPO_SET1: 3306252adeSAmelie Delaunay case STMFX_REG_GPO_SET2: 3406252adeSAmelie Delaunay case STMFX_REG_GPO_SET3: 3506252adeSAmelie Delaunay case STMFX_REG_GPO_CLR1: 3606252adeSAmelie Delaunay case STMFX_REG_GPO_CLR2: 3706252adeSAmelie Delaunay case STMFX_REG_GPO_CLR3: 3806252adeSAmelie Delaunay return true; 3906252adeSAmelie Delaunay default: 4006252adeSAmelie Delaunay return false; 4106252adeSAmelie Delaunay } 4206252adeSAmelie Delaunay } 4306252adeSAmelie Delaunay 4406252adeSAmelie Delaunay static bool stmfx_reg_writeable(struct device *dev, unsigned int reg) 4506252adeSAmelie Delaunay { 4606252adeSAmelie Delaunay return (reg >= STMFX_REG_SYS_CTRL); 4706252adeSAmelie Delaunay } 4806252adeSAmelie Delaunay 4906252adeSAmelie Delaunay static const struct regmap_config stmfx_regmap_config = { 5006252adeSAmelie Delaunay .reg_bits = 8, 5106252adeSAmelie Delaunay .reg_stride = 1, 5206252adeSAmelie Delaunay .val_bits = 8, 5306252adeSAmelie Delaunay .max_register = STMFX_REG_MAX, 5406252adeSAmelie Delaunay .volatile_reg = stmfx_reg_volatile, 5506252adeSAmelie Delaunay .writeable_reg = stmfx_reg_writeable, 5606252adeSAmelie Delaunay .cache_type = REGCACHE_RBTREE, 5706252adeSAmelie Delaunay }; 5806252adeSAmelie Delaunay 5906252adeSAmelie Delaunay static const struct resource stmfx_pinctrl_resources[] = { 6006252adeSAmelie Delaunay DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_GPIO), 6106252adeSAmelie Delaunay }; 6206252adeSAmelie Delaunay 6306252adeSAmelie Delaunay static const struct resource stmfx_idd_resources[] = { 6406252adeSAmelie Delaunay DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_IDD), 6506252adeSAmelie Delaunay DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_ERROR), 6606252adeSAmelie Delaunay }; 6706252adeSAmelie Delaunay 6806252adeSAmelie Delaunay static const struct resource stmfx_ts_resources[] = { 6906252adeSAmelie Delaunay DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_TS_DET), 7006252adeSAmelie Delaunay DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_TS_NE), 7106252adeSAmelie Delaunay DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_TS_TH), 7206252adeSAmelie Delaunay DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_TS_FULL), 7306252adeSAmelie Delaunay DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_TS_OVF), 7406252adeSAmelie Delaunay }; 7506252adeSAmelie Delaunay 7606252adeSAmelie Delaunay static struct mfd_cell stmfx_cells[] = { 7706252adeSAmelie Delaunay { 7806252adeSAmelie Delaunay .of_compatible = "st,stmfx-0300-pinctrl", 7906252adeSAmelie Delaunay .name = "stmfx-pinctrl", 8006252adeSAmelie Delaunay .resources = stmfx_pinctrl_resources, 8106252adeSAmelie Delaunay .num_resources = ARRAY_SIZE(stmfx_pinctrl_resources), 8206252adeSAmelie Delaunay }, 8306252adeSAmelie Delaunay { 8406252adeSAmelie Delaunay .of_compatible = "st,stmfx-0300-idd", 8506252adeSAmelie Delaunay .name = "stmfx-idd", 8606252adeSAmelie Delaunay .resources = stmfx_idd_resources, 8706252adeSAmelie Delaunay .num_resources = ARRAY_SIZE(stmfx_idd_resources), 8806252adeSAmelie Delaunay }, 8906252adeSAmelie Delaunay { 9006252adeSAmelie Delaunay .of_compatible = "st,stmfx-0300-ts", 9106252adeSAmelie Delaunay .name = "stmfx-ts", 9206252adeSAmelie Delaunay .resources = stmfx_ts_resources, 9306252adeSAmelie Delaunay .num_resources = ARRAY_SIZE(stmfx_ts_resources), 9406252adeSAmelie Delaunay }, 9506252adeSAmelie Delaunay }; 9606252adeSAmelie Delaunay 9706252adeSAmelie Delaunay static u8 stmfx_func_to_mask(u32 func) 9806252adeSAmelie Delaunay { 9906252adeSAmelie Delaunay u8 mask = 0; 10006252adeSAmelie Delaunay 10106252adeSAmelie Delaunay if (func & STMFX_FUNC_GPIO) 10206252adeSAmelie Delaunay mask |= STMFX_REG_SYS_CTRL_GPIO_EN; 10306252adeSAmelie Delaunay 10406252adeSAmelie Delaunay if ((func & STMFX_FUNC_ALTGPIO_LOW) || (func & STMFX_FUNC_ALTGPIO_HIGH)) 10506252adeSAmelie Delaunay mask |= STMFX_REG_SYS_CTRL_ALTGPIO_EN; 10606252adeSAmelie Delaunay 10706252adeSAmelie Delaunay if (func & STMFX_FUNC_TS) 10806252adeSAmelie Delaunay mask |= STMFX_REG_SYS_CTRL_TS_EN; 10906252adeSAmelie Delaunay 11006252adeSAmelie Delaunay if (func & STMFX_FUNC_IDD) 11106252adeSAmelie Delaunay mask |= STMFX_REG_SYS_CTRL_IDD_EN; 11206252adeSAmelie Delaunay 11306252adeSAmelie Delaunay return mask; 11406252adeSAmelie Delaunay } 11506252adeSAmelie Delaunay 11606252adeSAmelie Delaunay int stmfx_function_enable(struct stmfx *stmfx, u32 func) 11706252adeSAmelie Delaunay { 11806252adeSAmelie Delaunay u32 sys_ctrl; 11906252adeSAmelie Delaunay u8 mask; 12006252adeSAmelie Delaunay int ret; 12106252adeSAmelie Delaunay 12206252adeSAmelie Delaunay ret = regmap_read(stmfx->map, STMFX_REG_SYS_CTRL, &sys_ctrl); 12306252adeSAmelie Delaunay if (ret) 12406252adeSAmelie Delaunay return ret; 12506252adeSAmelie Delaunay 12606252adeSAmelie Delaunay /* 12706252adeSAmelie Delaunay * IDD and TS have priority in STMFX FW, so if IDD and TS are enabled, 12806252adeSAmelie Delaunay * ALTGPIO function is disabled by STMFX FW. If IDD or TS is enabled, 12906252adeSAmelie Delaunay * the number of aGPIO available decreases. To avoid GPIO management 13006252adeSAmelie Delaunay * disturbance, abort IDD or TS function enable in this case. 13106252adeSAmelie Delaunay */ 13206252adeSAmelie Delaunay if (((func & STMFX_FUNC_IDD) || (func & STMFX_FUNC_TS)) && 13306252adeSAmelie Delaunay (sys_ctrl & STMFX_REG_SYS_CTRL_ALTGPIO_EN)) { 13406252adeSAmelie Delaunay dev_err(stmfx->dev, "ALTGPIO function already enabled\n"); 13506252adeSAmelie Delaunay return -EBUSY; 13606252adeSAmelie Delaunay } 13706252adeSAmelie Delaunay 13806252adeSAmelie Delaunay /* If TS is enabled, aGPIO[3:0] cannot be used */ 13906252adeSAmelie Delaunay if ((func & STMFX_FUNC_ALTGPIO_LOW) && 14006252adeSAmelie Delaunay (sys_ctrl & STMFX_REG_SYS_CTRL_TS_EN)) { 14106252adeSAmelie Delaunay dev_err(stmfx->dev, "TS in use, aGPIO[3:0] unavailable\n"); 14206252adeSAmelie Delaunay return -EBUSY; 14306252adeSAmelie Delaunay } 14406252adeSAmelie Delaunay 14506252adeSAmelie Delaunay /* If IDD is enabled, aGPIO[7:4] cannot be used */ 14606252adeSAmelie Delaunay if ((func & STMFX_FUNC_ALTGPIO_HIGH) && 14706252adeSAmelie Delaunay (sys_ctrl & STMFX_REG_SYS_CTRL_IDD_EN)) { 14806252adeSAmelie Delaunay dev_err(stmfx->dev, "IDD in use, aGPIO[7:4] unavailable\n"); 14906252adeSAmelie Delaunay return -EBUSY; 15006252adeSAmelie Delaunay } 15106252adeSAmelie Delaunay 15206252adeSAmelie Delaunay mask = stmfx_func_to_mask(func); 15306252adeSAmelie Delaunay 15406252adeSAmelie Delaunay return regmap_update_bits(stmfx->map, STMFX_REG_SYS_CTRL, mask, mask); 15506252adeSAmelie Delaunay } 15606252adeSAmelie Delaunay EXPORT_SYMBOL_GPL(stmfx_function_enable); 15706252adeSAmelie Delaunay 15806252adeSAmelie Delaunay int stmfx_function_disable(struct stmfx *stmfx, u32 func) 15906252adeSAmelie Delaunay { 16006252adeSAmelie Delaunay u8 mask = stmfx_func_to_mask(func); 16106252adeSAmelie Delaunay 16206252adeSAmelie Delaunay return regmap_update_bits(stmfx->map, STMFX_REG_SYS_CTRL, mask, 0); 16306252adeSAmelie Delaunay } 16406252adeSAmelie Delaunay EXPORT_SYMBOL_GPL(stmfx_function_disable); 16506252adeSAmelie Delaunay 16606252adeSAmelie Delaunay static void stmfx_irq_bus_lock(struct irq_data *data) 16706252adeSAmelie Delaunay { 16806252adeSAmelie Delaunay struct stmfx *stmfx = irq_data_get_irq_chip_data(data); 16906252adeSAmelie Delaunay 17006252adeSAmelie Delaunay mutex_lock(&stmfx->lock); 17106252adeSAmelie Delaunay } 17206252adeSAmelie Delaunay 17306252adeSAmelie Delaunay static void stmfx_irq_bus_sync_unlock(struct irq_data *data) 17406252adeSAmelie Delaunay { 17506252adeSAmelie Delaunay struct stmfx *stmfx = irq_data_get_irq_chip_data(data); 17606252adeSAmelie Delaunay 17706252adeSAmelie Delaunay regmap_write(stmfx->map, STMFX_REG_IRQ_SRC_EN, stmfx->irq_src); 17806252adeSAmelie Delaunay 17906252adeSAmelie Delaunay mutex_unlock(&stmfx->lock); 18006252adeSAmelie Delaunay } 18106252adeSAmelie Delaunay 18206252adeSAmelie Delaunay static void stmfx_irq_mask(struct irq_data *data) 18306252adeSAmelie Delaunay { 18406252adeSAmelie Delaunay struct stmfx *stmfx = irq_data_get_irq_chip_data(data); 18506252adeSAmelie Delaunay 18606252adeSAmelie Delaunay stmfx->irq_src &= ~BIT(data->hwirq % 8); 18706252adeSAmelie Delaunay } 18806252adeSAmelie Delaunay 18906252adeSAmelie Delaunay static void stmfx_irq_unmask(struct irq_data *data) 19006252adeSAmelie Delaunay { 19106252adeSAmelie Delaunay struct stmfx *stmfx = irq_data_get_irq_chip_data(data); 19206252adeSAmelie Delaunay 19306252adeSAmelie Delaunay stmfx->irq_src |= BIT(data->hwirq % 8); 19406252adeSAmelie Delaunay } 19506252adeSAmelie Delaunay 19606252adeSAmelie Delaunay static struct irq_chip stmfx_irq_chip = { 19706252adeSAmelie Delaunay .name = "stmfx-core", 19806252adeSAmelie Delaunay .irq_bus_lock = stmfx_irq_bus_lock, 19906252adeSAmelie Delaunay .irq_bus_sync_unlock = stmfx_irq_bus_sync_unlock, 20006252adeSAmelie Delaunay .irq_mask = stmfx_irq_mask, 20106252adeSAmelie Delaunay .irq_unmask = stmfx_irq_unmask, 20206252adeSAmelie Delaunay }; 20306252adeSAmelie Delaunay 20406252adeSAmelie Delaunay static irqreturn_t stmfx_irq_handler(int irq, void *data) 20506252adeSAmelie Delaunay { 20606252adeSAmelie Delaunay struct stmfx *stmfx = data; 20763b2de12SDan Carpenter unsigned long bits; 208cd49b84dSDan Carpenter u32 pending, ack; 209cd49b84dSDan Carpenter int n, ret; 21006252adeSAmelie Delaunay 211cd49b84dSDan Carpenter ret = regmap_read(stmfx->map, STMFX_REG_IRQ_PENDING, &pending); 21206252adeSAmelie Delaunay if (ret) 21306252adeSAmelie Delaunay return IRQ_NONE; 21406252adeSAmelie Delaunay 21506252adeSAmelie Delaunay /* 21606252adeSAmelie Delaunay * There is no ACK for GPIO, MFX_REG_IRQ_PENDING_GPIO is a logical OR 21706252adeSAmelie Delaunay * of MFX_REG_IRQ_GPI _PENDING1/_PENDING2/_PENDING3 21806252adeSAmelie Delaunay */ 21906252adeSAmelie Delaunay ack = pending & ~BIT(STMFX_REG_IRQ_SRC_EN_GPIO); 22006252adeSAmelie Delaunay if (ack) { 22106252adeSAmelie Delaunay ret = regmap_write(stmfx->map, STMFX_REG_IRQ_ACK, ack); 22206252adeSAmelie Delaunay if (ret) 22306252adeSAmelie Delaunay return IRQ_NONE; 22406252adeSAmelie Delaunay } 22506252adeSAmelie Delaunay 22663b2de12SDan Carpenter bits = pending; 22763b2de12SDan Carpenter for_each_set_bit(n, &bits, STMFX_REG_IRQ_SRC_MAX) 22806252adeSAmelie Delaunay handle_nested_irq(irq_find_mapping(stmfx->irq_domain, n)); 22906252adeSAmelie Delaunay 23006252adeSAmelie Delaunay return IRQ_HANDLED; 23106252adeSAmelie Delaunay } 23206252adeSAmelie Delaunay 23306252adeSAmelie Delaunay static int stmfx_irq_map(struct irq_domain *d, unsigned int virq, 23406252adeSAmelie Delaunay irq_hw_number_t hwirq) 23506252adeSAmelie Delaunay { 23606252adeSAmelie Delaunay irq_set_chip_data(virq, d->host_data); 23706252adeSAmelie Delaunay irq_set_chip_and_handler(virq, &stmfx_irq_chip, handle_simple_irq); 23806252adeSAmelie Delaunay irq_set_nested_thread(virq, 1); 23906252adeSAmelie Delaunay irq_set_noprobe(virq); 24006252adeSAmelie Delaunay 24106252adeSAmelie Delaunay return 0; 24206252adeSAmelie Delaunay } 24306252adeSAmelie Delaunay 24406252adeSAmelie Delaunay static void stmfx_irq_unmap(struct irq_domain *d, unsigned int virq) 24506252adeSAmelie Delaunay { 24606252adeSAmelie Delaunay irq_set_chip_and_handler(virq, NULL, NULL); 24706252adeSAmelie Delaunay irq_set_chip_data(virq, NULL); 24806252adeSAmelie Delaunay } 24906252adeSAmelie Delaunay 25006252adeSAmelie Delaunay static const struct irq_domain_ops stmfx_irq_ops = { 25106252adeSAmelie Delaunay .map = stmfx_irq_map, 25206252adeSAmelie Delaunay .unmap = stmfx_irq_unmap, 25306252adeSAmelie Delaunay }; 25406252adeSAmelie Delaunay 25506252adeSAmelie Delaunay static void stmfx_irq_exit(struct i2c_client *client) 25606252adeSAmelie Delaunay { 25706252adeSAmelie Delaunay struct stmfx *stmfx = i2c_get_clientdata(client); 25806252adeSAmelie Delaunay int hwirq; 25906252adeSAmelie Delaunay 26006252adeSAmelie Delaunay for (hwirq = 0; hwirq < STMFX_REG_IRQ_SRC_MAX; hwirq++) 26106252adeSAmelie Delaunay irq_dispose_mapping(irq_find_mapping(stmfx->irq_domain, hwirq)); 26206252adeSAmelie Delaunay 26306252adeSAmelie Delaunay irq_domain_remove(stmfx->irq_domain); 26406252adeSAmelie Delaunay } 26506252adeSAmelie Delaunay 26606252adeSAmelie Delaunay static int stmfx_irq_init(struct i2c_client *client) 26706252adeSAmelie Delaunay { 26806252adeSAmelie Delaunay struct stmfx *stmfx = i2c_get_clientdata(client); 26906252adeSAmelie Delaunay u32 irqoutpin = 0, irqtrigger; 27006252adeSAmelie Delaunay int ret; 27106252adeSAmelie Delaunay 27206252adeSAmelie Delaunay stmfx->irq_domain = irq_domain_add_simple(stmfx->dev->of_node, 27306252adeSAmelie Delaunay STMFX_REG_IRQ_SRC_MAX, 0, 27406252adeSAmelie Delaunay &stmfx_irq_ops, stmfx); 27506252adeSAmelie Delaunay if (!stmfx->irq_domain) { 27606252adeSAmelie Delaunay dev_err(stmfx->dev, "Failed to create IRQ domain\n"); 27706252adeSAmelie Delaunay return -EINVAL; 27806252adeSAmelie Delaunay } 27906252adeSAmelie Delaunay 28006252adeSAmelie Delaunay if (!of_property_read_bool(stmfx->dev->of_node, "drive-open-drain")) 28106252adeSAmelie Delaunay irqoutpin |= STMFX_REG_IRQ_OUT_PIN_TYPE; 28206252adeSAmelie Delaunay 28306252adeSAmelie Delaunay irqtrigger = irq_get_trigger_type(client->irq); 28406252adeSAmelie Delaunay if ((irqtrigger & IRQ_TYPE_EDGE_RISING) || 28506252adeSAmelie Delaunay (irqtrigger & IRQ_TYPE_LEVEL_HIGH)) 28606252adeSAmelie Delaunay irqoutpin |= STMFX_REG_IRQ_OUT_PIN_POL; 28706252adeSAmelie Delaunay 28806252adeSAmelie Delaunay ret = regmap_write(stmfx->map, STMFX_REG_IRQ_OUT_PIN, irqoutpin); 28906252adeSAmelie Delaunay if (ret) 29060c2c4bcSAmelie Delaunay goto irq_exit; 29106252adeSAmelie Delaunay 29206252adeSAmelie Delaunay ret = devm_request_threaded_irq(stmfx->dev, client->irq, 29306252adeSAmelie Delaunay NULL, stmfx_irq_handler, 29406252adeSAmelie Delaunay irqtrigger | IRQF_ONESHOT, 29506252adeSAmelie Delaunay "stmfx", stmfx); 29606252adeSAmelie Delaunay if (ret) 29760c2c4bcSAmelie Delaunay goto irq_exit; 29860c2c4bcSAmelie Delaunay 29997eda5dcSAmelie Delaunay stmfx->irq = client->irq; 30097eda5dcSAmelie Delaunay 30160c2c4bcSAmelie Delaunay return 0; 30260c2c4bcSAmelie Delaunay 30360c2c4bcSAmelie Delaunay irq_exit: 30406252adeSAmelie Delaunay stmfx_irq_exit(client); 30506252adeSAmelie Delaunay 30606252adeSAmelie Delaunay return ret; 30706252adeSAmelie Delaunay } 30806252adeSAmelie Delaunay 30906252adeSAmelie Delaunay static int stmfx_chip_reset(struct stmfx *stmfx) 31006252adeSAmelie Delaunay { 31106252adeSAmelie Delaunay int ret; 31206252adeSAmelie Delaunay 31306252adeSAmelie Delaunay ret = regmap_write(stmfx->map, STMFX_REG_SYS_CTRL, 31406252adeSAmelie Delaunay STMFX_REG_SYS_CTRL_SWRST); 31506252adeSAmelie Delaunay if (ret) 31606252adeSAmelie Delaunay return ret; 31706252adeSAmelie Delaunay 31806252adeSAmelie Delaunay msleep(STMFX_BOOT_TIME_MS); 31906252adeSAmelie Delaunay 32006252adeSAmelie Delaunay return ret; 32106252adeSAmelie Delaunay } 32206252adeSAmelie Delaunay 32306252adeSAmelie Delaunay static int stmfx_chip_init(struct i2c_client *client) 32406252adeSAmelie Delaunay { 32506252adeSAmelie Delaunay struct stmfx *stmfx = i2c_get_clientdata(client); 32606252adeSAmelie Delaunay u32 id; 32706252adeSAmelie Delaunay u8 version[2]; 32806252adeSAmelie Delaunay int ret; 32906252adeSAmelie Delaunay 33006252adeSAmelie Delaunay stmfx->vdd = devm_regulator_get_optional(&client->dev, "vdd"); 33106252adeSAmelie Delaunay ret = PTR_ERR_OR_ZERO(stmfx->vdd); 332d75846edSAmelie Delaunay if (ret) { 333d75846edSAmelie Delaunay if (ret == -ENODEV) 33406252adeSAmelie Delaunay stmfx->vdd = NULL; 335d75846edSAmelie Delaunay else 336d75846edSAmelie Delaunay return dev_err_probe(&client->dev, ret, "Failed to get VDD regulator\n"); 33706252adeSAmelie Delaunay } 33806252adeSAmelie Delaunay 33906252adeSAmelie Delaunay if (stmfx->vdd) { 34006252adeSAmelie Delaunay ret = regulator_enable(stmfx->vdd); 34106252adeSAmelie Delaunay if (ret) { 34206252adeSAmelie Delaunay dev_err(&client->dev, "VDD enable failed: %d\n", ret); 34306252adeSAmelie Delaunay return ret; 34406252adeSAmelie Delaunay } 34506252adeSAmelie Delaunay } 34606252adeSAmelie Delaunay 34706252adeSAmelie Delaunay ret = regmap_read(stmfx->map, STMFX_REG_CHIP_ID, &id); 34806252adeSAmelie Delaunay if (ret) { 34906252adeSAmelie Delaunay dev_err(&client->dev, "Error reading chip ID: %d\n", ret); 35006252adeSAmelie Delaunay goto err; 35106252adeSAmelie Delaunay } 35206252adeSAmelie Delaunay 35306252adeSAmelie Delaunay /* 35406252adeSAmelie Delaunay * Check that ID is the complement of the I2C address: 35506252adeSAmelie Delaunay * STMFX I2C address follows the 7-bit format (MSB), that's why 35606252adeSAmelie Delaunay * client->addr is shifted. 35706252adeSAmelie Delaunay * 35806252adeSAmelie Delaunay * STMFX_I2C_ADDR| STMFX | Linux 35906252adeSAmelie Delaunay * input pin | I2C device address | I2C device address 36006252adeSAmelie Delaunay *--------------------------------------------------------- 36106252adeSAmelie Delaunay * 0 | b: 1000 010x h:0x84 | 0x42 36206252adeSAmelie Delaunay * 1 | b: 1000 011x h:0x86 | 0x43 36306252adeSAmelie Delaunay */ 36406252adeSAmelie Delaunay if (FIELD_GET(STMFX_REG_CHIP_ID_MASK, ~id) != (client->addr << 1)) { 36506252adeSAmelie Delaunay dev_err(&client->dev, "Unknown chip ID: %#x\n", id); 36606252adeSAmelie Delaunay ret = -EINVAL; 36706252adeSAmelie Delaunay goto err; 36806252adeSAmelie Delaunay } 36906252adeSAmelie Delaunay 37006252adeSAmelie Delaunay ret = regmap_bulk_read(stmfx->map, STMFX_REG_FW_VERSION_MSB, 37106252adeSAmelie Delaunay version, ARRAY_SIZE(version)); 37206252adeSAmelie Delaunay if (ret) { 37306252adeSAmelie Delaunay dev_err(&client->dev, "Error reading FW version: %d\n", ret); 37406252adeSAmelie Delaunay goto err; 37506252adeSAmelie Delaunay } 37606252adeSAmelie Delaunay 37706252adeSAmelie Delaunay dev_info(&client->dev, "STMFX id: %#x, fw version: %x.%02x\n", 37806252adeSAmelie Delaunay id, version[0], version[1]); 37906252adeSAmelie Delaunay 38006252adeSAmelie Delaunay ret = stmfx_chip_reset(stmfx); 38106252adeSAmelie Delaunay if (ret) { 38206252adeSAmelie Delaunay dev_err(&client->dev, "Failed to reset chip: %d\n", ret); 38306252adeSAmelie Delaunay goto err; 38406252adeSAmelie Delaunay } 38506252adeSAmelie Delaunay 38606252adeSAmelie Delaunay return 0; 38706252adeSAmelie Delaunay 38806252adeSAmelie Delaunay err: 38906252adeSAmelie Delaunay if (stmfx->vdd) 39006252adeSAmelie Delaunay return regulator_disable(stmfx->vdd); 39106252adeSAmelie Delaunay 39206252adeSAmelie Delaunay return ret; 39306252adeSAmelie Delaunay } 39406252adeSAmelie Delaunay 395c788f6e6SUwe Kleine-König static void stmfx_chip_exit(struct i2c_client *client) 39606252adeSAmelie Delaunay { 39706252adeSAmelie Delaunay struct stmfx *stmfx = i2c_get_clientdata(client); 39806252adeSAmelie Delaunay 39906252adeSAmelie Delaunay regmap_write(stmfx->map, STMFX_REG_IRQ_SRC_EN, 0); 40006252adeSAmelie Delaunay regmap_write(stmfx->map, STMFX_REG_SYS_CTRL, 0); 40106252adeSAmelie Delaunay 402c788f6e6SUwe Kleine-König if (stmfx->vdd) { 403c788f6e6SUwe Kleine-König int ret; 40406252adeSAmelie Delaunay 405c788f6e6SUwe Kleine-König ret = regulator_disable(stmfx->vdd); 406c788f6e6SUwe Kleine-König if (ret) 407c788f6e6SUwe Kleine-König dev_err(&client->dev, 408c788f6e6SUwe Kleine-König "Failed to disable vdd regulator: %pe\n", 409c788f6e6SUwe Kleine-König ERR_PTR(ret)); 410c788f6e6SUwe Kleine-König } 41106252adeSAmelie Delaunay } 41206252adeSAmelie Delaunay 413*05b62f4cSUwe Kleine-König static int stmfx_probe(struct i2c_client *client) 41406252adeSAmelie Delaunay { 41506252adeSAmelie Delaunay struct device *dev = &client->dev; 41606252adeSAmelie Delaunay struct stmfx *stmfx; 41706252adeSAmelie Delaunay int ret; 41806252adeSAmelie Delaunay 41906252adeSAmelie Delaunay stmfx = devm_kzalloc(dev, sizeof(*stmfx), GFP_KERNEL); 42006252adeSAmelie Delaunay if (!stmfx) 42106252adeSAmelie Delaunay return -ENOMEM; 42206252adeSAmelie Delaunay 42306252adeSAmelie Delaunay i2c_set_clientdata(client, stmfx); 42406252adeSAmelie Delaunay 42506252adeSAmelie Delaunay stmfx->dev = dev; 42606252adeSAmelie Delaunay 42706252adeSAmelie Delaunay stmfx->map = devm_regmap_init_i2c(client, &stmfx_regmap_config); 42806252adeSAmelie Delaunay if (IS_ERR(stmfx->map)) { 42906252adeSAmelie Delaunay ret = PTR_ERR(stmfx->map); 43006252adeSAmelie Delaunay dev_err(dev, "Failed to allocate register map: %d\n", ret); 43106252adeSAmelie Delaunay return ret; 43206252adeSAmelie Delaunay } 43306252adeSAmelie Delaunay 43406252adeSAmelie Delaunay mutex_init(&stmfx->lock); 43506252adeSAmelie Delaunay 43606252adeSAmelie Delaunay ret = stmfx_chip_init(client); 43706252adeSAmelie Delaunay if (ret) { 43806252adeSAmelie Delaunay if (ret == -ETIMEDOUT) 43906252adeSAmelie Delaunay return -EPROBE_DEFER; 44006252adeSAmelie Delaunay return ret; 44106252adeSAmelie Delaunay } 44206252adeSAmelie Delaunay 44306252adeSAmelie Delaunay if (client->irq < 0) { 44406252adeSAmelie Delaunay dev_err(dev, "Failed to get IRQ: %d\n", client->irq); 44506252adeSAmelie Delaunay ret = client->irq; 44606252adeSAmelie Delaunay goto err_chip_exit; 44706252adeSAmelie Delaunay } 44806252adeSAmelie Delaunay 44906252adeSAmelie Delaunay ret = stmfx_irq_init(client); 45006252adeSAmelie Delaunay if (ret) 45106252adeSAmelie Delaunay goto err_chip_exit; 45206252adeSAmelie Delaunay 45306252adeSAmelie Delaunay ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 45406252adeSAmelie Delaunay stmfx_cells, ARRAY_SIZE(stmfx_cells), NULL, 45506252adeSAmelie Delaunay 0, stmfx->irq_domain); 45606252adeSAmelie Delaunay if (ret) 45706252adeSAmelie Delaunay goto err_irq_exit; 45806252adeSAmelie Delaunay 45906252adeSAmelie Delaunay return 0; 46006252adeSAmelie Delaunay 46106252adeSAmelie Delaunay err_irq_exit: 46206252adeSAmelie Delaunay stmfx_irq_exit(client); 46306252adeSAmelie Delaunay err_chip_exit: 46406252adeSAmelie Delaunay stmfx_chip_exit(client); 46506252adeSAmelie Delaunay 46606252adeSAmelie Delaunay return ret; 46706252adeSAmelie Delaunay } 46806252adeSAmelie Delaunay 469ed5c2f5fSUwe Kleine-König static void stmfx_remove(struct i2c_client *client) 47006252adeSAmelie Delaunay { 47106252adeSAmelie Delaunay stmfx_irq_exit(client); 47206252adeSAmelie Delaunay 473c788f6e6SUwe Kleine-König stmfx_chip_exit(client); 47406252adeSAmelie Delaunay } 47506252adeSAmelie Delaunay 47606252adeSAmelie Delaunay #ifdef CONFIG_PM_SLEEP 47706252adeSAmelie Delaunay static int stmfx_suspend(struct device *dev) 47806252adeSAmelie Delaunay { 47906252adeSAmelie Delaunay struct stmfx *stmfx = dev_get_drvdata(dev); 48006252adeSAmelie Delaunay int ret; 48106252adeSAmelie Delaunay 48206252adeSAmelie Delaunay ret = regmap_raw_read(stmfx->map, STMFX_REG_SYS_CTRL, 48306252adeSAmelie Delaunay &stmfx->bkp_sysctrl, sizeof(stmfx->bkp_sysctrl)); 48406252adeSAmelie Delaunay if (ret) 48506252adeSAmelie Delaunay return ret; 48606252adeSAmelie Delaunay 48706252adeSAmelie Delaunay ret = regmap_raw_read(stmfx->map, STMFX_REG_IRQ_OUT_PIN, 48806252adeSAmelie Delaunay &stmfx->bkp_irqoutpin, 48906252adeSAmelie Delaunay sizeof(stmfx->bkp_irqoutpin)); 49006252adeSAmelie Delaunay if (ret) 49106252adeSAmelie Delaunay return ret; 49206252adeSAmelie Delaunay 49397eda5dcSAmelie Delaunay disable_irq(stmfx->irq); 49497eda5dcSAmelie Delaunay 49506252adeSAmelie Delaunay if (stmfx->vdd) 49606252adeSAmelie Delaunay return regulator_disable(stmfx->vdd); 49706252adeSAmelie Delaunay 49806252adeSAmelie Delaunay return 0; 49906252adeSAmelie Delaunay } 50006252adeSAmelie Delaunay 50106252adeSAmelie Delaunay static int stmfx_resume(struct device *dev) 50206252adeSAmelie Delaunay { 50306252adeSAmelie Delaunay struct stmfx *stmfx = dev_get_drvdata(dev); 50406252adeSAmelie Delaunay int ret; 50506252adeSAmelie Delaunay 50606252adeSAmelie Delaunay if (stmfx->vdd) { 50706252adeSAmelie Delaunay ret = regulator_enable(stmfx->vdd); 50806252adeSAmelie Delaunay if (ret) { 50906252adeSAmelie Delaunay dev_err(stmfx->dev, 51006252adeSAmelie Delaunay "VDD enable failed: %d\n", ret); 51106252adeSAmelie Delaunay return ret; 51206252adeSAmelie Delaunay } 51306252adeSAmelie Delaunay } 51406252adeSAmelie Delaunay 515e583649dSAmelie Delaunay /* Reset STMFX - supply has been stopped during suspend */ 516e583649dSAmelie Delaunay ret = stmfx_chip_reset(stmfx); 517e583649dSAmelie Delaunay if (ret) { 518e583649dSAmelie Delaunay dev_err(stmfx->dev, "Failed to reset chip: %d\n", ret); 519e583649dSAmelie Delaunay return ret; 520e583649dSAmelie Delaunay } 521e583649dSAmelie Delaunay 52206252adeSAmelie Delaunay ret = regmap_raw_write(stmfx->map, STMFX_REG_SYS_CTRL, 52306252adeSAmelie Delaunay &stmfx->bkp_sysctrl, sizeof(stmfx->bkp_sysctrl)); 52406252adeSAmelie Delaunay if (ret) 52506252adeSAmelie Delaunay return ret; 52606252adeSAmelie Delaunay 52706252adeSAmelie Delaunay ret = regmap_raw_write(stmfx->map, STMFX_REG_IRQ_OUT_PIN, 52806252adeSAmelie Delaunay &stmfx->bkp_irqoutpin, 52906252adeSAmelie Delaunay sizeof(stmfx->bkp_irqoutpin)); 53006252adeSAmelie Delaunay if (ret) 53106252adeSAmelie Delaunay return ret; 53206252adeSAmelie Delaunay 53306252adeSAmelie Delaunay ret = regmap_raw_write(stmfx->map, STMFX_REG_IRQ_SRC_EN, 53406252adeSAmelie Delaunay &stmfx->irq_src, sizeof(stmfx->irq_src)); 53506252adeSAmelie Delaunay if (ret) 53606252adeSAmelie Delaunay return ret; 53706252adeSAmelie Delaunay 53897eda5dcSAmelie Delaunay enable_irq(stmfx->irq); 53997eda5dcSAmelie Delaunay 54006252adeSAmelie Delaunay return 0; 54106252adeSAmelie Delaunay } 54206252adeSAmelie Delaunay #endif 54306252adeSAmelie Delaunay 54406252adeSAmelie Delaunay static SIMPLE_DEV_PM_OPS(stmfx_dev_pm_ops, stmfx_suspend, stmfx_resume); 54506252adeSAmelie Delaunay 54606252adeSAmelie Delaunay static const struct of_device_id stmfx_of_match[] = { 54706252adeSAmelie Delaunay { .compatible = "st,stmfx-0300", }, 54806252adeSAmelie Delaunay {}, 54906252adeSAmelie Delaunay }; 55006252adeSAmelie Delaunay MODULE_DEVICE_TABLE(of, stmfx_of_match); 55106252adeSAmelie Delaunay 55206252adeSAmelie Delaunay static struct i2c_driver stmfx_driver = { 55306252adeSAmelie Delaunay .driver = { 55406252adeSAmelie Delaunay .name = "stmfx-core", 555a06d0dc4SKrzysztof Kozlowski .of_match_table = stmfx_of_match, 55606252adeSAmelie Delaunay .pm = &stmfx_dev_pm_ops, 55706252adeSAmelie Delaunay }, 558*05b62f4cSUwe Kleine-König .probe_new = stmfx_probe, 55906252adeSAmelie Delaunay .remove = stmfx_remove, 56006252adeSAmelie Delaunay }; 56106252adeSAmelie Delaunay module_i2c_driver(stmfx_driver); 56206252adeSAmelie Delaunay 56306252adeSAmelie Delaunay MODULE_DESCRIPTION("STMFX core driver"); 56406252adeSAmelie Delaunay MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>"); 56506252adeSAmelie Delaunay MODULE_LICENSE("GPL v2"); 566