18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for STMicroelectronics Multi-Function eXpander (STMFX) core 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2019 STMicroelectronics 68c2ecf20Sopenharmony_ci * Author(s): Amelie Delaunay <amelie.delaunay@st.com>. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 98c2ecf20Sopenharmony_ci#include <linux/i2c.h> 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/irq.h> 128c2ecf20Sopenharmony_ci#include <linux/mfd/core.h> 138c2ecf20Sopenharmony_ci#include <linux/mfd/stmfx.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic bool stmfx_reg_volatile(struct device *dev, unsigned int reg) 188c2ecf20Sopenharmony_ci{ 198c2ecf20Sopenharmony_ci switch (reg) { 208c2ecf20Sopenharmony_ci case STMFX_REG_SYS_CTRL: 218c2ecf20Sopenharmony_ci case STMFX_REG_IRQ_SRC_EN: 228c2ecf20Sopenharmony_ci case STMFX_REG_IRQ_PENDING: 238c2ecf20Sopenharmony_ci case STMFX_REG_IRQ_GPI_PENDING1: 248c2ecf20Sopenharmony_ci case STMFX_REG_IRQ_GPI_PENDING2: 258c2ecf20Sopenharmony_ci case STMFX_REG_IRQ_GPI_PENDING3: 268c2ecf20Sopenharmony_ci case STMFX_REG_GPIO_STATE1: 278c2ecf20Sopenharmony_ci case STMFX_REG_GPIO_STATE2: 288c2ecf20Sopenharmony_ci case STMFX_REG_GPIO_STATE3: 298c2ecf20Sopenharmony_ci case STMFX_REG_IRQ_GPI_SRC1: 308c2ecf20Sopenharmony_ci case STMFX_REG_IRQ_GPI_SRC2: 318c2ecf20Sopenharmony_ci case STMFX_REG_IRQ_GPI_SRC3: 328c2ecf20Sopenharmony_ci case STMFX_REG_GPO_SET1: 338c2ecf20Sopenharmony_ci case STMFX_REG_GPO_SET2: 348c2ecf20Sopenharmony_ci case STMFX_REG_GPO_SET3: 358c2ecf20Sopenharmony_ci case STMFX_REG_GPO_CLR1: 368c2ecf20Sopenharmony_ci case STMFX_REG_GPO_CLR2: 378c2ecf20Sopenharmony_ci case STMFX_REG_GPO_CLR3: 388c2ecf20Sopenharmony_ci return true; 398c2ecf20Sopenharmony_ci default: 408c2ecf20Sopenharmony_ci return false; 418c2ecf20Sopenharmony_ci } 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic bool stmfx_reg_writeable(struct device *dev, unsigned int reg) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci return (reg >= STMFX_REG_SYS_CTRL); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic const struct regmap_config stmfx_regmap_config = { 508c2ecf20Sopenharmony_ci .reg_bits = 8, 518c2ecf20Sopenharmony_ci .reg_stride = 1, 528c2ecf20Sopenharmony_ci .val_bits = 8, 538c2ecf20Sopenharmony_ci .max_register = STMFX_REG_MAX, 548c2ecf20Sopenharmony_ci .volatile_reg = stmfx_reg_volatile, 558c2ecf20Sopenharmony_ci .writeable_reg = stmfx_reg_writeable, 568c2ecf20Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic const struct resource stmfx_pinctrl_resources[] = { 608c2ecf20Sopenharmony_ci DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_GPIO), 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic const struct resource stmfx_idd_resources[] = { 648c2ecf20Sopenharmony_ci DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_IDD), 658c2ecf20Sopenharmony_ci DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_ERROR), 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic const struct resource stmfx_ts_resources[] = { 698c2ecf20Sopenharmony_ci DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_TS_DET), 708c2ecf20Sopenharmony_ci DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_TS_NE), 718c2ecf20Sopenharmony_ci DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_TS_TH), 728c2ecf20Sopenharmony_ci DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_TS_FULL), 738c2ecf20Sopenharmony_ci DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_TS_OVF), 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic struct mfd_cell stmfx_cells[] = { 778c2ecf20Sopenharmony_ci { 788c2ecf20Sopenharmony_ci .of_compatible = "st,stmfx-0300-pinctrl", 798c2ecf20Sopenharmony_ci .name = "stmfx-pinctrl", 808c2ecf20Sopenharmony_ci .resources = stmfx_pinctrl_resources, 818c2ecf20Sopenharmony_ci .num_resources = ARRAY_SIZE(stmfx_pinctrl_resources), 828c2ecf20Sopenharmony_ci }, 838c2ecf20Sopenharmony_ci { 848c2ecf20Sopenharmony_ci .of_compatible = "st,stmfx-0300-idd", 858c2ecf20Sopenharmony_ci .name = "stmfx-idd", 868c2ecf20Sopenharmony_ci .resources = stmfx_idd_resources, 878c2ecf20Sopenharmony_ci .num_resources = ARRAY_SIZE(stmfx_idd_resources), 888c2ecf20Sopenharmony_ci }, 898c2ecf20Sopenharmony_ci { 908c2ecf20Sopenharmony_ci .of_compatible = "st,stmfx-0300-ts", 918c2ecf20Sopenharmony_ci .name = "stmfx-ts", 928c2ecf20Sopenharmony_ci .resources = stmfx_ts_resources, 938c2ecf20Sopenharmony_ci .num_resources = ARRAY_SIZE(stmfx_ts_resources), 948c2ecf20Sopenharmony_ci }, 958c2ecf20Sopenharmony_ci}; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic u8 stmfx_func_to_mask(u32 func) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci u8 mask = 0; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (func & STMFX_FUNC_GPIO) 1028c2ecf20Sopenharmony_ci mask |= STMFX_REG_SYS_CTRL_GPIO_EN; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if ((func & STMFX_FUNC_ALTGPIO_LOW) || (func & STMFX_FUNC_ALTGPIO_HIGH)) 1058c2ecf20Sopenharmony_ci mask |= STMFX_REG_SYS_CTRL_ALTGPIO_EN; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (func & STMFX_FUNC_TS) 1088c2ecf20Sopenharmony_ci mask |= STMFX_REG_SYS_CTRL_TS_EN; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (func & STMFX_FUNC_IDD) 1118c2ecf20Sopenharmony_ci mask |= STMFX_REG_SYS_CTRL_IDD_EN; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return mask; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ciint stmfx_function_enable(struct stmfx *stmfx, u32 func) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci u32 sys_ctrl; 1198c2ecf20Sopenharmony_ci u8 mask; 1208c2ecf20Sopenharmony_ci int ret; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci ret = regmap_read(stmfx->map, STMFX_REG_SYS_CTRL, &sys_ctrl); 1238c2ecf20Sopenharmony_ci if (ret) 1248c2ecf20Sopenharmony_ci return ret; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* 1278c2ecf20Sopenharmony_ci * IDD and TS have priority in STMFX FW, so if IDD and TS are enabled, 1288c2ecf20Sopenharmony_ci * ALTGPIO function is disabled by STMFX FW. If IDD or TS is enabled, 1298c2ecf20Sopenharmony_ci * the number of aGPIO available decreases. To avoid GPIO management 1308c2ecf20Sopenharmony_ci * disturbance, abort IDD or TS function enable in this case. 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_ci if (((func & STMFX_FUNC_IDD) || (func & STMFX_FUNC_TS)) && 1338c2ecf20Sopenharmony_ci (sys_ctrl & STMFX_REG_SYS_CTRL_ALTGPIO_EN)) { 1348c2ecf20Sopenharmony_ci dev_err(stmfx->dev, "ALTGPIO function already enabled\n"); 1358c2ecf20Sopenharmony_ci return -EBUSY; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* If TS is enabled, aGPIO[3:0] cannot be used */ 1398c2ecf20Sopenharmony_ci if ((func & STMFX_FUNC_ALTGPIO_LOW) && 1408c2ecf20Sopenharmony_ci (sys_ctrl & STMFX_REG_SYS_CTRL_TS_EN)) { 1418c2ecf20Sopenharmony_ci dev_err(stmfx->dev, "TS in use, aGPIO[3:0] unavailable\n"); 1428c2ecf20Sopenharmony_ci return -EBUSY; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* If IDD is enabled, aGPIO[7:4] cannot be used */ 1468c2ecf20Sopenharmony_ci if ((func & STMFX_FUNC_ALTGPIO_HIGH) && 1478c2ecf20Sopenharmony_ci (sys_ctrl & STMFX_REG_SYS_CTRL_IDD_EN)) { 1488c2ecf20Sopenharmony_ci dev_err(stmfx->dev, "IDD in use, aGPIO[7:4] unavailable\n"); 1498c2ecf20Sopenharmony_ci return -EBUSY; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci mask = stmfx_func_to_mask(func); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return regmap_update_bits(stmfx->map, STMFX_REG_SYS_CTRL, mask, mask); 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(stmfx_function_enable); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ciint stmfx_function_disable(struct stmfx *stmfx, u32 func) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci u8 mask = stmfx_func_to_mask(func); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return regmap_update_bits(stmfx->map, STMFX_REG_SYS_CTRL, mask, 0); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(stmfx_function_disable); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic void stmfx_irq_bus_lock(struct irq_data *data) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct stmfx *stmfx = irq_data_get_irq_chip_data(data); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci mutex_lock(&stmfx->lock); 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic void stmfx_irq_bus_sync_unlock(struct irq_data *data) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct stmfx *stmfx = irq_data_get_irq_chip_data(data); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci regmap_write(stmfx->map, STMFX_REG_IRQ_SRC_EN, stmfx->irq_src); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci mutex_unlock(&stmfx->lock); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic void stmfx_irq_mask(struct irq_data *data) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct stmfx *stmfx = irq_data_get_irq_chip_data(data); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci stmfx->irq_src &= ~BIT(data->hwirq % 8); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic void stmfx_irq_unmask(struct irq_data *data) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct stmfx *stmfx = irq_data_get_irq_chip_data(data); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci stmfx->irq_src |= BIT(data->hwirq % 8); 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic struct irq_chip stmfx_irq_chip = { 1978c2ecf20Sopenharmony_ci .name = "stmfx-core", 1988c2ecf20Sopenharmony_ci .irq_bus_lock = stmfx_irq_bus_lock, 1998c2ecf20Sopenharmony_ci .irq_bus_sync_unlock = stmfx_irq_bus_sync_unlock, 2008c2ecf20Sopenharmony_ci .irq_mask = stmfx_irq_mask, 2018c2ecf20Sopenharmony_ci .irq_unmask = stmfx_irq_unmask, 2028c2ecf20Sopenharmony_ci}; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic irqreturn_t stmfx_irq_handler(int irq, void *data) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct stmfx *stmfx = data; 2078c2ecf20Sopenharmony_ci unsigned long bits; 2088c2ecf20Sopenharmony_ci u32 pending, ack; 2098c2ecf20Sopenharmony_ci int n, ret; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci ret = regmap_read(stmfx->map, STMFX_REG_IRQ_PENDING, &pending); 2128c2ecf20Sopenharmony_ci if (ret) 2138c2ecf20Sopenharmony_ci return IRQ_NONE; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* 2168c2ecf20Sopenharmony_ci * There is no ACK for GPIO, MFX_REG_IRQ_PENDING_GPIO is a logical OR 2178c2ecf20Sopenharmony_ci * of MFX_REG_IRQ_GPI _PENDING1/_PENDING2/_PENDING3 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_ci ack = pending & ~BIT(STMFX_REG_IRQ_SRC_EN_GPIO); 2208c2ecf20Sopenharmony_ci if (ack) { 2218c2ecf20Sopenharmony_ci ret = regmap_write(stmfx->map, STMFX_REG_IRQ_ACK, ack); 2228c2ecf20Sopenharmony_ci if (ret) 2238c2ecf20Sopenharmony_ci return IRQ_NONE; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci bits = pending; 2278c2ecf20Sopenharmony_ci for_each_set_bit(n, &bits, STMFX_REG_IRQ_SRC_MAX) 2288c2ecf20Sopenharmony_ci handle_nested_irq(irq_find_mapping(stmfx->irq_domain, n)); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic int stmfx_irq_map(struct irq_domain *d, unsigned int virq, 2348c2ecf20Sopenharmony_ci irq_hw_number_t hwirq) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci irq_set_chip_data(virq, d->host_data); 2378c2ecf20Sopenharmony_ci irq_set_chip_and_handler(virq, &stmfx_irq_chip, handle_simple_irq); 2388c2ecf20Sopenharmony_ci irq_set_nested_thread(virq, 1); 2398c2ecf20Sopenharmony_ci irq_set_noprobe(virq); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic void stmfx_irq_unmap(struct irq_domain *d, unsigned int virq) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci irq_set_chip_and_handler(virq, NULL, NULL); 2478c2ecf20Sopenharmony_ci irq_set_chip_data(virq, NULL); 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic const struct irq_domain_ops stmfx_irq_ops = { 2518c2ecf20Sopenharmony_ci .map = stmfx_irq_map, 2528c2ecf20Sopenharmony_ci .unmap = stmfx_irq_unmap, 2538c2ecf20Sopenharmony_ci}; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic void stmfx_irq_exit(struct i2c_client *client) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct stmfx *stmfx = i2c_get_clientdata(client); 2588c2ecf20Sopenharmony_ci int hwirq; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci for (hwirq = 0; hwirq < STMFX_REG_IRQ_SRC_MAX; hwirq++) 2618c2ecf20Sopenharmony_ci irq_dispose_mapping(irq_find_mapping(stmfx->irq_domain, hwirq)); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci irq_domain_remove(stmfx->irq_domain); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic int stmfx_irq_init(struct i2c_client *client) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci struct stmfx *stmfx = i2c_get_clientdata(client); 2698c2ecf20Sopenharmony_ci u32 irqoutpin = 0, irqtrigger; 2708c2ecf20Sopenharmony_ci int ret; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci stmfx->irq_domain = irq_domain_add_simple(stmfx->dev->of_node, 2738c2ecf20Sopenharmony_ci STMFX_REG_IRQ_SRC_MAX, 0, 2748c2ecf20Sopenharmony_ci &stmfx_irq_ops, stmfx); 2758c2ecf20Sopenharmony_ci if (!stmfx->irq_domain) { 2768c2ecf20Sopenharmony_ci dev_err(stmfx->dev, "Failed to create IRQ domain\n"); 2778c2ecf20Sopenharmony_ci return -EINVAL; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (!of_property_read_bool(stmfx->dev->of_node, "drive-open-drain")) 2818c2ecf20Sopenharmony_ci irqoutpin |= STMFX_REG_IRQ_OUT_PIN_TYPE; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci irqtrigger = irq_get_trigger_type(client->irq); 2848c2ecf20Sopenharmony_ci if ((irqtrigger & IRQ_TYPE_EDGE_RISING) || 2858c2ecf20Sopenharmony_ci (irqtrigger & IRQ_TYPE_LEVEL_HIGH)) 2868c2ecf20Sopenharmony_ci irqoutpin |= STMFX_REG_IRQ_OUT_PIN_POL; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ret = regmap_write(stmfx->map, STMFX_REG_IRQ_OUT_PIN, irqoutpin); 2898c2ecf20Sopenharmony_ci if (ret) 2908c2ecf20Sopenharmony_ci goto irq_exit; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(stmfx->dev, client->irq, 2938c2ecf20Sopenharmony_ci NULL, stmfx_irq_handler, 2948c2ecf20Sopenharmony_ci irqtrigger | IRQF_ONESHOT, 2958c2ecf20Sopenharmony_ci "stmfx", stmfx); 2968c2ecf20Sopenharmony_ci if (ret) 2978c2ecf20Sopenharmony_ci goto irq_exit; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci stmfx->irq = client->irq; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return 0; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ciirq_exit: 3048c2ecf20Sopenharmony_ci stmfx_irq_exit(client); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return ret; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic int stmfx_chip_reset(struct stmfx *stmfx) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci int ret; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci ret = regmap_write(stmfx->map, STMFX_REG_SYS_CTRL, 3148c2ecf20Sopenharmony_ci STMFX_REG_SYS_CTRL_SWRST); 3158c2ecf20Sopenharmony_ci if (ret) 3168c2ecf20Sopenharmony_ci return ret; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci msleep(STMFX_BOOT_TIME_MS); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci return ret; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic int stmfx_chip_init(struct i2c_client *client) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci struct stmfx *stmfx = i2c_get_clientdata(client); 3268c2ecf20Sopenharmony_ci u32 id; 3278c2ecf20Sopenharmony_ci u8 version[2]; 3288c2ecf20Sopenharmony_ci int ret; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci stmfx->vdd = devm_regulator_get_optional(&client->dev, "vdd"); 3318c2ecf20Sopenharmony_ci ret = PTR_ERR_OR_ZERO(stmfx->vdd); 3328c2ecf20Sopenharmony_ci if (ret) { 3338c2ecf20Sopenharmony_ci stmfx->vdd = NULL; 3348c2ecf20Sopenharmony_ci if (ret != -ENODEV) 3358c2ecf20Sopenharmony_ci return dev_err_probe(&client->dev, ret, "Failed to get VDD regulator\n"); 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (stmfx->vdd) { 3398c2ecf20Sopenharmony_ci ret = regulator_enable(stmfx->vdd); 3408c2ecf20Sopenharmony_ci if (ret) { 3418c2ecf20Sopenharmony_ci dev_err(&client->dev, "VDD enable failed: %d\n", ret); 3428c2ecf20Sopenharmony_ci return ret; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci ret = regmap_read(stmfx->map, STMFX_REG_CHIP_ID, &id); 3478c2ecf20Sopenharmony_ci if (ret) { 3488c2ecf20Sopenharmony_ci dev_err(&client->dev, "Error reading chip ID: %d\n", ret); 3498c2ecf20Sopenharmony_ci goto err; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* 3538c2ecf20Sopenharmony_ci * Check that ID is the complement of the I2C address: 3548c2ecf20Sopenharmony_ci * STMFX I2C address follows the 7-bit format (MSB), that's why 3558c2ecf20Sopenharmony_ci * client->addr is shifted. 3568c2ecf20Sopenharmony_ci * 3578c2ecf20Sopenharmony_ci * STMFX_I2C_ADDR| STMFX | Linux 3588c2ecf20Sopenharmony_ci * input pin | I2C device address | I2C device address 3598c2ecf20Sopenharmony_ci *--------------------------------------------------------- 3608c2ecf20Sopenharmony_ci * 0 | b: 1000 010x h:0x84 | 0x42 3618c2ecf20Sopenharmony_ci * 1 | b: 1000 011x h:0x86 | 0x43 3628c2ecf20Sopenharmony_ci */ 3638c2ecf20Sopenharmony_ci if (FIELD_GET(STMFX_REG_CHIP_ID_MASK, ~id) != (client->addr << 1)) { 3648c2ecf20Sopenharmony_ci dev_err(&client->dev, "Unknown chip ID: %#x\n", id); 3658c2ecf20Sopenharmony_ci ret = -EINVAL; 3668c2ecf20Sopenharmony_ci goto err; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci ret = regmap_bulk_read(stmfx->map, STMFX_REG_FW_VERSION_MSB, 3708c2ecf20Sopenharmony_ci version, ARRAY_SIZE(version)); 3718c2ecf20Sopenharmony_ci if (ret) { 3728c2ecf20Sopenharmony_ci dev_err(&client->dev, "Error reading FW version: %d\n", ret); 3738c2ecf20Sopenharmony_ci goto err; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci dev_info(&client->dev, "STMFX id: %#x, fw version: %x.%02x\n", 3778c2ecf20Sopenharmony_ci id, version[0], version[1]); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci ret = stmfx_chip_reset(stmfx); 3808c2ecf20Sopenharmony_ci if (ret) { 3818c2ecf20Sopenharmony_ci dev_err(&client->dev, "Failed to reset chip: %d\n", ret); 3828c2ecf20Sopenharmony_ci goto err; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci return 0; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cierr: 3888c2ecf20Sopenharmony_ci if (stmfx->vdd) 3898c2ecf20Sopenharmony_ci regulator_disable(stmfx->vdd); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci return ret; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic int stmfx_chip_exit(struct i2c_client *client) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct stmfx *stmfx = i2c_get_clientdata(client); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci regmap_write(stmfx->map, STMFX_REG_IRQ_SRC_EN, 0); 3998c2ecf20Sopenharmony_ci regmap_write(stmfx->map, STMFX_REG_SYS_CTRL, 0); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (stmfx->vdd) 4028c2ecf20Sopenharmony_ci return regulator_disable(stmfx->vdd); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci return 0; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int stmfx_probe(struct i2c_client *client, 4088c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 4118c2ecf20Sopenharmony_ci struct stmfx *stmfx; 4128c2ecf20Sopenharmony_ci int ret; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci stmfx = devm_kzalloc(dev, sizeof(*stmfx), GFP_KERNEL); 4158c2ecf20Sopenharmony_ci if (!stmfx) 4168c2ecf20Sopenharmony_ci return -ENOMEM; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci i2c_set_clientdata(client, stmfx); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci stmfx->dev = dev; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci stmfx->map = devm_regmap_init_i2c(client, &stmfx_regmap_config); 4238c2ecf20Sopenharmony_ci if (IS_ERR(stmfx->map)) { 4248c2ecf20Sopenharmony_ci ret = PTR_ERR(stmfx->map); 4258c2ecf20Sopenharmony_ci dev_err(dev, "Failed to allocate register map: %d\n", ret); 4268c2ecf20Sopenharmony_ci return ret; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci mutex_init(&stmfx->lock); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci ret = stmfx_chip_init(client); 4328c2ecf20Sopenharmony_ci if (ret) { 4338c2ecf20Sopenharmony_ci if (ret == -ETIMEDOUT) 4348c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 4358c2ecf20Sopenharmony_ci return ret; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (client->irq < 0) { 4398c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get IRQ: %d\n", client->irq); 4408c2ecf20Sopenharmony_ci ret = client->irq; 4418c2ecf20Sopenharmony_ci goto err_chip_exit; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci ret = stmfx_irq_init(client); 4458c2ecf20Sopenharmony_ci if (ret) 4468c2ecf20Sopenharmony_ci goto err_chip_exit; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 4498c2ecf20Sopenharmony_ci stmfx_cells, ARRAY_SIZE(stmfx_cells), NULL, 4508c2ecf20Sopenharmony_ci 0, stmfx->irq_domain); 4518c2ecf20Sopenharmony_ci if (ret) 4528c2ecf20Sopenharmony_ci goto err_irq_exit; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci return 0; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cierr_irq_exit: 4578c2ecf20Sopenharmony_ci stmfx_irq_exit(client); 4588c2ecf20Sopenharmony_cierr_chip_exit: 4598c2ecf20Sopenharmony_ci stmfx_chip_exit(client); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci return ret; 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic int stmfx_remove(struct i2c_client *client) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci stmfx_irq_exit(client); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci return stmfx_chip_exit(client); 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4728c2ecf20Sopenharmony_cistatic int stmfx_suspend(struct device *dev) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci struct stmfx *stmfx = dev_get_drvdata(dev); 4758c2ecf20Sopenharmony_ci int ret; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci ret = regmap_raw_read(stmfx->map, STMFX_REG_SYS_CTRL, 4788c2ecf20Sopenharmony_ci &stmfx->bkp_sysctrl, sizeof(stmfx->bkp_sysctrl)); 4798c2ecf20Sopenharmony_ci if (ret) 4808c2ecf20Sopenharmony_ci return ret; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci ret = regmap_raw_read(stmfx->map, STMFX_REG_IRQ_OUT_PIN, 4838c2ecf20Sopenharmony_ci &stmfx->bkp_irqoutpin, 4848c2ecf20Sopenharmony_ci sizeof(stmfx->bkp_irqoutpin)); 4858c2ecf20Sopenharmony_ci if (ret) 4868c2ecf20Sopenharmony_ci return ret; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci disable_irq(stmfx->irq); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (stmfx->vdd) 4918c2ecf20Sopenharmony_ci return regulator_disable(stmfx->vdd); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci return 0; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic int stmfx_resume(struct device *dev) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci struct stmfx *stmfx = dev_get_drvdata(dev); 4998c2ecf20Sopenharmony_ci int ret; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (stmfx->vdd) { 5028c2ecf20Sopenharmony_ci ret = regulator_enable(stmfx->vdd); 5038c2ecf20Sopenharmony_ci if (ret) { 5048c2ecf20Sopenharmony_ci dev_err(stmfx->dev, 5058c2ecf20Sopenharmony_ci "VDD enable failed: %d\n", ret); 5068c2ecf20Sopenharmony_ci return ret; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* Reset STMFX - supply has been stopped during suspend */ 5118c2ecf20Sopenharmony_ci ret = stmfx_chip_reset(stmfx); 5128c2ecf20Sopenharmony_ci if (ret) { 5138c2ecf20Sopenharmony_ci dev_err(stmfx->dev, "Failed to reset chip: %d\n", ret); 5148c2ecf20Sopenharmony_ci return ret; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci ret = regmap_raw_write(stmfx->map, STMFX_REG_SYS_CTRL, 5188c2ecf20Sopenharmony_ci &stmfx->bkp_sysctrl, sizeof(stmfx->bkp_sysctrl)); 5198c2ecf20Sopenharmony_ci if (ret) 5208c2ecf20Sopenharmony_ci return ret; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci ret = regmap_raw_write(stmfx->map, STMFX_REG_IRQ_OUT_PIN, 5238c2ecf20Sopenharmony_ci &stmfx->bkp_irqoutpin, 5248c2ecf20Sopenharmony_ci sizeof(stmfx->bkp_irqoutpin)); 5258c2ecf20Sopenharmony_ci if (ret) 5268c2ecf20Sopenharmony_ci return ret; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci ret = regmap_raw_write(stmfx->map, STMFX_REG_IRQ_SRC_EN, 5298c2ecf20Sopenharmony_ci &stmfx->irq_src, sizeof(stmfx->irq_src)); 5308c2ecf20Sopenharmony_ci if (ret) 5318c2ecf20Sopenharmony_ci return ret; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci enable_irq(stmfx->irq); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci return 0; 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci#endif 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(stmfx_dev_pm_ops, stmfx_suspend, stmfx_resume); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic const struct of_device_id stmfx_of_match[] = { 5428c2ecf20Sopenharmony_ci { .compatible = "st,stmfx-0300", }, 5438c2ecf20Sopenharmony_ci {}, 5448c2ecf20Sopenharmony_ci}; 5458c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, stmfx_of_match); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic struct i2c_driver stmfx_driver = { 5488c2ecf20Sopenharmony_ci .driver = { 5498c2ecf20Sopenharmony_ci .name = "stmfx-core", 5508c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(stmfx_of_match), 5518c2ecf20Sopenharmony_ci .pm = &stmfx_dev_pm_ops, 5528c2ecf20Sopenharmony_ci }, 5538c2ecf20Sopenharmony_ci .probe = stmfx_probe, 5548c2ecf20Sopenharmony_ci .remove = stmfx_remove, 5558c2ecf20Sopenharmony_ci}; 5568c2ecf20Sopenharmony_cimodule_i2c_driver(stmfx_driver); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("STMFX core driver"); 5598c2ecf20Sopenharmony_ciMODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>"); 5608c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 561