18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// MCP16502 PMIC driver 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries 68c2ecf20Sopenharmony_ci// 78c2ecf20Sopenharmony_ci// Author: Andrei Stefanescu <andrei.stefanescu@microchip.com> 88c2ecf20Sopenharmony_ci// 98c2ecf20Sopenharmony_ci// Inspired from tps65086-regulator.c 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/gpio.h> 128c2ecf20Sopenharmony_ci#include <linux/i2c.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci#include <linux/regmap.h> 188c2ecf20Sopenharmony_ci#include <linux/regulator/driver.h> 198c2ecf20Sopenharmony_ci#include <linux/suspend.h> 208c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define VDD_LOW_SEL 0x0D 238c2ecf20Sopenharmony_ci#define VDD_HIGH_SEL 0x3F 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define MCP16502_FLT BIT(7) 268c2ecf20Sopenharmony_ci#define MCP16502_ENS BIT(0) 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * The PMIC has four sets of registers corresponding to four power modes: 308c2ecf20Sopenharmony_ci * Performance, Active, Low-power, Hibernate. 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * Registers: 338c2ecf20Sopenharmony_ci * Each regulator has a register for each power mode. To access a register 348c2ecf20Sopenharmony_ci * for a specific regulator and mode BASE_* and OFFSET_* need to be added. 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * Operating modes: 378c2ecf20Sopenharmony_ci * In order for the PMIC to transition to operating modes it has to be 388c2ecf20Sopenharmony_ci * controlled via GPIO lines called LPM and HPM. 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * The registers are fully configurable such that you can put all regulators in 418c2ecf20Sopenharmony_ci * a low-power state while the PMIC is in Active mode. They are supposed to be 428c2ecf20Sopenharmony_ci * configured at startup and then simply transition to/from a global low-power 438c2ecf20Sopenharmony_ci * state by setting the GPIO lpm pin high/low. 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * This driver keeps the PMIC in Active mode, Low-power state is set for the 468c2ecf20Sopenharmony_ci * regulators by enabling/disabling operating mode (FPWM or Auto PFM). 478c2ecf20Sopenharmony_ci * 488c2ecf20Sopenharmony_ci * The PMIC's Low-power and Hibernate modes are used during standby/suspend. 498c2ecf20Sopenharmony_ci * To enter standby/suspend the PMIC will go to Low-power mode. From there, it 508c2ecf20Sopenharmony_ci * will transition to Hibernate when the PWRHLD line is set to low by the MPU. 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* 548c2ecf20Sopenharmony_ci * This function is useful for iterating over all regulators and accessing their 558c2ecf20Sopenharmony_ci * registers in a generic way or accessing a regulator device by its id. 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_ci#define MCP16502_BASE(i) (((i) + 1) << 4) 588c2ecf20Sopenharmony_ci#define MCP16502_STAT_BASE(i) ((i) + 5) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define MCP16502_OFFSET_MODE_A 0 618c2ecf20Sopenharmony_ci#define MCP16502_OFFSET_MODE_LPM 1 628c2ecf20Sopenharmony_ci#define MCP16502_OFFSET_MODE_HIB 2 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define MCP16502_OPMODE_ACTIVE REGULATOR_MODE_NORMAL 658c2ecf20Sopenharmony_ci#define MCP16502_OPMODE_LPM REGULATOR_MODE_IDLE 668c2ecf20Sopenharmony_ci#define MCP16502_OPMODE_HIB REGULATOR_MODE_STANDBY 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define MCP16502_MODE_AUTO_PFM 0 698c2ecf20Sopenharmony_ci#define MCP16502_MODE_FPWM BIT(6) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define MCP16502_VSEL 0x3F 728c2ecf20Sopenharmony_ci#define MCP16502_EN BIT(7) 738c2ecf20Sopenharmony_ci#define MCP16502_MODE BIT(6) 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define MCP16502_MIN_REG 0x0 768c2ecf20Sopenharmony_ci#define MCP16502_MAX_REG 0x65 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic unsigned int mcp16502_of_map_mode(unsigned int mode) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci if (mode == REGULATOR_MODE_NORMAL || mode == REGULATOR_MODE_IDLE) 818c2ecf20Sopenharmony_ci return mode; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return REGULATOR_MODE_INVALID; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define MCP16502_REGULATOR(_name, _id, _ranges, _ops) \ 878c2ecf20Sopenharmony_ci [_id] = { \ 888c2ecf20Sopenharmony_ci .name = _name, \ 898c2ecf20Sopenharmony_ci .regulators_node = of_match_ptr("regulators"), \ 908c2ecf20Sopenharmony_ci .id = _id, \ 918c2ecf20Sopenharmony_ci .ops = &(_ops), \ 928c2ecf20Sopenharmony_ci .type = REGULATOR_VOLTAGE, \ 938c2ecf20Sopenharmony_ci .owner = THIS_MODULE, \ 948c2ecf20Sopenharmony_ci .n_voltages = MCP16502_VSEL + 1, \ 958c2ecf20Sopenharmony_ci .linear_ranges = _ranges, \ 968c2ecf20Sopenharmony_ci .n_linear_ranges = ARRAY_SIZE(_ranges), \ 978c2ecf20Sopenharmony_ci .of_match = of_match_ptr(_name), \ 988c2ecf20Sopenharmony_ci .of_map_mode = mcp16502_of_map_mode, \ 998c2ecf20Sopenharmony_ci .vsel_reg = (((_id) + 1) << 4), \ 1008c2ecf20Sopenharmony_ci .vsel_mask = MCP16502_VSEL, \ 1018c2ecf20Sopenharmony_ci .enable_reg = (((_id) + 1) << 4), \ 1028c2ecf20Sopenharmony_ci .enable_mask = MCP16502_EN, \ 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cienum { 1068c2ecf20Sopenharmony_ci BUCK1 = 0, 1078c2ecf20Sopenharmony_ci BUCK2, 1088c2ecf20Sopenharmony_ci BUCK3, 1098c2ecf20Sopenharmony_ci BUCK4, 1108c2ecf20Sopenharmony_ci LDO1, 1118c2ecf20Sopenharmony_ci LDO2, 1128c2ecf20Sopenharmony_ci NUM_REGULATORS 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* 1168c2ecf20Sopenharmony_ci * struct mcp16502 - PMIC representation 1178c2ecf20Sopenharmony_ci * @rdev: the regulators belonging to this chip 1188c2ecf20Sopenharmony_ci * @rmap: regmap to be used for I2C communication 1198c2ecf20Sopenharmony_ci * @lpm: LPM GPIO descriptor 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_cistruct mcp16502 { 1228c2ecf20Sopenharmony_ci struct gpio_desc *lpm; 1238c2ecf20Sopenharmony_ci}; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* 1268c2ecf20Sopenharmony_ci * mcp16502_gpio_set_mode() - set the GPIO corresponding value 1278c2ecf20Sopenharmony_ci * 1288c2ecf20Sopenharmony_ci * Used to prepare transitioning into hibernate or resuming from it. 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_cistatic void mcp16502_gpio_set_mode(struct mcp16502 *mcp, int mode) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci switch (mode) { 1338c2ecf20Sopenharmony_ci case MCP16502_OPMODE_ACTIVE: 1348c2ecf20Sopenharmony_ci gpiod_set_value(mcp->lpm, 0); 1358c2ecf20Sopenharmony_ci break; 1368c2ecf20Sopenharmony_ci case MCP16502_OPMODE_LPM: 1378c2ecf20Sopenharmony_ci case MCP16502_OPMODE_HIB: 1388c2ecf20Sopenharmony_ci gpiod_set_value(mcp->lpm, 1); 1398c2ecf20Sopenharmony_ci break; 1408c2ecf20Sopenharmony_ci default: 1418c2ecf20Sopenharmony_ci pr_err("%s: %d invalid\n", __func__, mode); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/* 1468c2ecf20Sopenharmony_ci * mcp16502_get_reg() - get the PMIC's configuration register for opmode 1478c2ecf20Sopenharmony_ci * 1488c2ecf20Sopenharmony_ci * @rdev: the regulator whose register we are searching 1498c2ecf20Sopenharmony_ci * @opmode: the PMIC's operating mode ACTIVE, Low-power, Hibernate 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_cistatic int mcp16502_get_reg(struct regulator_dev *rdev, int opmode) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci int reg = MCP16502_BASE(rdev_get_id(rdev)); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci switch (opmode) { 1568c2ecf20Sopenharmony_ci case MCP16502_OPMODE_ACTIVE: 1578c2ecf20Sopenharmony_ci return reg + MCP16502_OFFSET_MODE_A; 1588c2ecf20Sopenharmony_ci case MCP16502_OPMODE_LPM: 1598c2ecf20Sopenharmony_ci return reg + MCP16502_OFFSET_MODE_LPM; 1608c2ecf20Sopenharmony_ci case MCP16502_OPMODE_HIB: 1618c2ecf20Sopenharmony_ci return reg + MCP16502_OFFSET_MODE_HIB; 1628c2ecf20Sopenharmony_ci default: 1638c2ecf20Sopenharmony_ci return -EINVAL; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/* 1688c2ecf20Sopenharmony_ci * mcp16502_get_mode() - return the current operating mode of a regulator 1698c2ecf20Sopenharmony_ci * 1708c2ecf20Sopenharmony_ci * Note: all functions that are not part of entering/exiting standby/suspend 1718c2ecf20Sopenharmony_ci * use the Active mode registers. 1728c2ecf20Sopenharmony_ci * 1738c2ecf20Sopenharmony_ci * Note: this is different from the PMIC's operatig mode, it is the 1748c2ecf20Sopenharmony_ci * MODE bit from the regulator's register. 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_cistatic unsigned int mcp16502_get_mode(struct regulator_dev *rdev) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci unsigned int val; 1798c2ecf20Sopenharmony_ci int ret, reg; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci reg = mcp16502_get_reg(rdev, MCP16502_OPMODE_ACTIVE); 1828c2ecf20Sopenharmony_ci if (reg < 0) 1838c2ecf20Sopenharmony_ci return reg; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci ret = regmap_read(rdev->regmap, reg, &val); 1868c2ecf20Sopenharmony_ci if (ret) 1878c2ecf20Sopenharmony_ci return ret; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci switch (val & MCP16502_MODE) { 1908c2ecf20Sopenharmony_ci case MCP16502_MODE_FPWM: 1918c2ecf20Sopenharmony_ci return REGULATOR_MODE_NORMAL; 1928c2ecf20Sopenharmony_ci case MCP16502_MODE_AUTO_PFM: 1938c2ecf20Sopenharmony_ci return REGULATOR_MODE_IDLE; 1948c2ecf20Sopenharmony_ci default: 1958c2ecf20Sopenharmony_ci return REGULATOR_MODE_INVALID; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/* 2008c2ecf20Sopenharmony_ci * _mcp16502_set_mode() - helper for set_mode and set_suspend_mode 2018c2ecf20Sopenharmony_ci * 2028c2ecf20Sopenharmony_ci * @rdev: the regulator for which we are setting the mode 2038c2ecf20Sopenharmony_ci * @mode: the regulator's mode (the one from MODE bit) 2048c2ecf20Sopenharmony_ci * @opmode: the PMIC's operating mode: Active/Low-power/Hibernate 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_cistatic int _mcp16502_set_mode(struct regulator_dev *rdev, unsigned int mode, 2078c2ecf20Sopenharmony_ci unsigned int op_mode) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci int val; 2108c2ecf20Sopenharmony_ci int reg; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci reg = mcp16502_get_reg(rdev, op_mode); 2138c2ecf20Sopenharmony_ci if (reg < 0) 2148c2ecf20Sopenharmony_ci return reg; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci switch (mode) { 2178c2ecf20Sopenharmony_ci case REGULATOR_MODE_NORMAL: 2188c2ecf20Sopenharmony_ci val = MCP16502_MODE_FPWM; 2198c2ecf20Sopenharmony_ci break; 2208c2ecf20Sopenharmony_ci case REGULATOR_MODE_IDLE: 2218c2ecf20Sopenharmony_ci val = MCP16502_MODE_AUTO_PFM; 2228c2ecf20Sopenharmony_ci break; 2238c2ecf20Sopenharmony_ci default: 2248c2ecf20Sopenharmony_ci return -EINVAL; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci reg = regmap_update_bits(rdev->regmap, reg, MCP16502_MODE, val); 2288c2ecf20Sopenharmony_ci return reg; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci/* 2328c2ecf20Sopenharmony_ci * mcp16502_set_mode() - regulator_ops set_mode 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_cistatic int mcp16502_set_mode(struct regulator_dev *rdev, unsigned int mode) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci return _mcp16502_set_mode(rdev, mode, MCP16502_OPMODE_ACTIVE); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci/* 2408c2ecf20Sopenharmony_ci * mcp16502_get_status() - regulator_ops get_status 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_cistatic int mcp16502_get_status(struct regulator_dev *rdev) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci int ret; 2458c2ecf20Sopenharmony_ci unsigned int val; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci ret = regmap_read(rdev->regmap, MCP16502_STAT_BASE(rdev_get_id(rdev)), 2488c2ecf20Sopenharmony_ci &val); 2498c2ecf20Sopenharmony_ci if (ret) 2508c2ecf20Sopenharmony_ci return ret; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (val & MCP16502_FLT) 2538c2ecf20Sopenharmony_ci return REGULATOR_STATUS_ERROR; 2548c2ecf20Sopenharmony_ci else if (val & MCP16502_ENS) 2558c2ecf20Sopenharmony_ci return REGULATOR_STATUS_ON; 2568c2ecf20Sopenharmony_ci else if (!(val & MCP16502_ENS)) 2578c2ecf20Sopenharmony_ci return REGULATOR_STATUS_OFF; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return REGULATOR_STATUS_UNDEFINED; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci#ifdef CONFIG_SUSPEND 2638c2ecf20Sopenharmony_ci/* 2648c2ecf20Sopenharmony_ci * mcp16502_suspend_get_target_reg() - get the reg of the target suspend PMIC 2658c2ecf20Sopenharmony_ci * mode 2668c2ecf20Sopenharmony_ci */ 2678c2ecf20Sopenharmony_cistatic int mcp16502_suspend_get_target_reg(struct regulator_dev *rdev) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci switch (pm_suspend_target_state) { 2708c2ecf20Sopenharmony_ci case PM_SUSPEND_STANDBY: 2718c2ecf20Sopenharmony_ci return mcp16502_get_reg(rdev, MCP16502_OPMODE_LPM); 2728c2ecf20Sopenharmony_ci case PM_SUSPEND_ON: 2738c2ecf20Sopenharmony_ci case PM_SUSPEND_MEM: 2748c2ecf20Sopenharmony_ci return mcp16502_get_reg(rdev, MCP16502_OPMODE_HIB); 2758c2ecf20Sopenharmony_ci default: 2768c2ecf20Sopenharmony_ci dev_err(&rdev->dev, "invalid suspend target: %d\n", 2778c2ecf20Sopenharmony_ci pm_suspend_target_state); 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return -EINVAL; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci/* 2848c2ecf20Sopenharmony_ci * mcp16502_set_suspend_voltage() - regulator_ops set_suspend_voltage 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_cistatic int mcp16502_set_suspend_voltage(struct regulator_dev *rdev, int uV) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci int sel = regulator_map_voltage_linear_range(rdev, uV, uV); 2898c2ecf20Sopenharmony_ci int reg = mcp16502_suspend_get_target_reg(rdev); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (sel < 0) 2928c2ecf20Sopenharmony_ci return sel; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (reg < 0) 2958c2ecf20Sopenharmony_ci return reg; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return regmap_update_bits(rdev->regmap, reg, MCP16502_VSEL, sel); 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci/* 3018c2ecf20Sopenharmony_ci * mcp16502_set_suspend_mode() - regulator_ops set_suspend_mode 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_cistatic int mcp16502_set_suspend_mode(struct regulator_dev *rdev, 3048c2ecf20Sopenharmony_ci unsigned int mode) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci switch (pm_suspend_target_state) { 3078c2ecf20Sopenharmony_ci case PM_SUSPEND_STANDBY: 3088c2ecf20Sopenharmony_ci return _mcp16502_set_mode(rdev, mode, MCP16502_OPMODE_LPM); 3098c2ecf20Sopenharmony_ci case PM_SUSPEND_ON: 3108c2ecf20Sopenharmony_ci case PM_SUSPEND_MEM: 3118c2ecf20Sopenharmony_ci return _mcp16502_set_mode(rdev, mode, MCP16502_OPMODE_HIB); 3128c2ecf20Sopenharmony_ci default: 3138c2ecf20Sopenharmony_ci dev_err(&rdev->dev, "invalid suspend target: %d\n", 3148c2ecf20Sopenharmony_ci pm_suspend_target_state); 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return -EINVAL; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci/* 3218c2ecf20Sopenharmony_ci * mcp16502_set_suspend_enable() - regulator_ops set_suspend_enable 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_cistatic int mcp16502_set_suspend_enable(struct regulator_dev *rdev) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci int reg = mcp16502_suspend_get_target_reg(rdev); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (reg < 0) 3288c2ecf20Sopenharmony_ci return reg; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci return regmap_update_bits(rdev->regmap, reg, MCP16502_EN, MCP16502_EN); 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci/* 3348c2ecf20Sopenharmony_ci * mcp16502_set_suspend_disable() - regulator_ops set_suspend_disable 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_cistatic int mcp16502_set_suspend_disable(struct regulator_dev *rdev) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci int reg = mcp16502_suspend_get_target_reg(rdev); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (reg < 0) 3418c2ecf20Sopenharmony_ci return reg; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci return regmap_update_bits(rdev->regmap, reg, MCP16502_EN, 0); 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci#endif /* CONFIG_SUSPEND */ 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic const struct regulator_ops mcp16502_buck_ops = { 3488c2ecf20Sopenharmony_ci .list_voltage = regulator_list_voltage_linear_range, 3498c2ecf20Sopenharmony_ci .map_voltage = regulator_map_voltage_linear_range, 3508c2ecf20Sopenharmony_ci .get_voltage_sel = regulator_get_voltage_sel_regmap, 3518c2ecf20Sopenharmony_ci .set_voltage_sel = regulator_set_voltage_sel_regmap, 3528c2ecf20Sopenharmony_ci .enable = regulator_enable_regmap, 3538c2ecf20Sopenharmony_ci .disable = regulator_disable_regmap, 3548c2ecf20Sopenharmony_ci .is_enabled = regulator_is_enabled_regmap, 3558c2ecf20Sopenharmony_ci .get_status = mcp16502_get_status, 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci .set_mode = mcp16502_set_mode, 3588c2ecf20Sopenharmony_ci .get_mode = mcp16502_get_mode, 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci#ifdef CONFIG_SUSPEND 3618c2ecf20Sopenharmony_ci .set_suspend_voltage = mcp16502_set_suspend_voltage, 3628c2ecf20Sopenharmony_ci .set_suspend_mode = mcp16502_set_suspend_mode, 3638c2ecf20Sopenharmony_ci .set_suspend_enable = mcp16502_set_suspend_enable, 3648c2ecf20Sopenharmony_ci .set_suspend_disable = mcp16502_set_suspend_disable, 3658c2ecf20Sopenharmony_ci#endif /* CONFIG_SUSPEND */ 3668c2ecf20Sopenharmony_ci}; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci/* 3698c2ecf20Sopenharmony_ci * LDOs cannot change operating modes. 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_cistatic const struct regulator_ops mcp16502_ldo_ops = { 3728c2ecf20Sopenharmony_ci .list_voltage = regulator_list_voltage_linear_range, 3738c2ecf20Sopenharmony_ci .map_voltage = regulator_map_voltage_linear_range, 3748c2ecf20Sopenharmony_ci .get_voltage_sel = regulator_get_voltage_sel_regmap, 3758c2ecf20Sopenharmony_ci .set_voltage_sel = regulator_set_voltage_sel_regmap, 3768c2ecf20Sopenharmony_ci .enable = regulator_enable_regmap, 3778c2ecf20Sopenharmony_ci .disable = regulator_disable_regmap, 3788c2ecf20Sopenharmony_ci .is_enabled = regulator_is_enabled_regmap, 3798c2ecf20Sopenharmony_ci .get_status = mcp16502_get_status, 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci#ifdef CONFIG_SUSPEND 3828c2ecf20Sopenharmony_ci .set_suspend_voltage = mcp16502_set_suspend_voltage, 3838c2ecf20Sopenharmony_ci .set_suspend_enable = mcp16502_set_suspend_enable, 3848c2ecf20Sopenharmony_ci .set_suspend_disable = mcp16502_set_suspend_disable, 3858c2ecf20Sopenharmony_ci#endif /* CONFIG_SUSPEND */ 3868c2ecf20Sopenharmony_ci}; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic const struct of_device_id mcp16502_ids[] = { 3898c2ecf20Sopenharmony_ci { .compatible = "microchip,mcp16502", }, 3908c2ecf20Sopenharmony_ci {} 3918c2ecf20Sopenharmony_ci}; 3928c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mcp16502_ids); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic const struct linear_range b1l12_ranges[] = { 3958c2ecf20Sopenharmony_ci REGULATOR_LINEAR_RANGE(1200000, VDD_LOW_SEL, VDD_HIGH_SEL, 50000), 3968c2ecf20Sopenharmony_ci}; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic const struct linear_range b234_ranges[] = { 3998c2ecf20Sopenharmony_ci REGULATOR_LINEAR_RANGE(600000, VDD_LOW_SEL, VDD_HIGH_SEL, 25000), 4008c2ecf20Sopenharmony_ci}; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic const struct regulator_desc mcp16502_desc[] = { 4038c2ecf20Sopenharmony_ci /* MCP16502_REGULATOR(_name, _id, ranges, regulator_ops) */ 4048c2ecf20Sopenharmony_ci MCP16502_REGULATOR("VDD_IO", BUCK1, b1l12_ranges, mcp16502_buck_ops), 4058c2ecf20Sopenharmony_ci MCP16502_REGULATOR("VDD_DDR", BUCK2, b234_ranges, mcp16502_buck_ops), 4068c2ecf20Sopenharmony_ci MCP16502_REGULATOR("VDD_CORE", BUCK3, b234_ranges, mcp16502_buck_ops), 4078c2ecf20Sopenharmony_ci MCP16502_REGULATOR("VDD_OTHER", BUCK4, b234_ranges, mcp16502_buck_ops), 4088c2ecf20Sopenharmony_ci MCP16502_REGULATOR("LDO1", LDO1, b1l12_ranges, mcp16502_ldo_ops), 4098c2ecf20Sopenharmony_ci MCP16502_REGULATOR("LDO2", LDO2, b1l12_ranges, mcp16502_ldo_ops) 4108c2ecf20Sopenharmony_ci}; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic const struct regmap_range mcp16502_ranges[] = { 4138c2ecf20Sopenharmony_ci regmap_reg_range(MCP16502_MIN_REG, MCP16502_MAX_REG) 4148c2ecf20Sopenharmony_ci}; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic const struct regmap_access_table mcp16502_yes_reg_table = { 4178c2ecf20Sopenharmony_ci .yes_ranges = mcp16502_ranges, 4188c2ecf20Sopenharmony_ci .n_yes_ranges = ARRAY_SIZE(mcp16502_ranges), 4198c2ecf20Sopenharmony_ci}; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic const struct regmap_config mcp16502_regmap_config = { 4228c2ecf20Sopenharmony_ci .reg_bits = 8, 4238c2ecf20Sopenharmony_ci .val_bits = 8, 4248c2ecf20Sopenharmony_ci .max_register = MCP16502_MAX_REG, 4258c2ecf20Sopenharmony_ci .cache_type = REGCACHE_NONE, 4268c2ecf20Sopenharmony_ci .rd_table = &mcp16502_yes_reg_table, 4278c2ecf20Sopenharmony_ci .wr_table = &mcp16502_yes_reg_table, 4288c2ecf20Sopenharmony_ci}; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic int mcp16502_probe(struct i2c_client *client, 4318c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct regulator_config config = { }; 4348c2ecf20Sopenharmony_ci struct regulator_dev *rdev; 4358c2ecf20Sopenharmony_ci struct device *dev; 4368c2ecf20Sopenharmony_ci struct mcp16502 *mcp; 4378c2ecf20Sopenharmony_ci struct regmap *rmap; 4388c2ecf20Sopenharmony_ci int i, ret; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci dev = &client->dev; 4418c2ecf20Sopenharmony_ci config.dev = dev; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci mcp = devm_kzalloc(dev, sizeof(*mcp), GFP_KERNEL); 4448c2ecf20Sopenharmony_ci if (!mcp) 4458c2ecf20Sopenharmony_ci return -ENOMEM; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci rmap = devm_regmap_init_i2c(client, &mcp16502_regmap_config); 4488c2ecf20Sopenharmony_ci if (IS_ERR(rmap)) { 4498c2ecf20Sopenharmony_ci ret = PTR_ERR(rmap); 4508c2ecf20Sopenharmony_ci dev_err(dev, "regmap init failed: %d\n", ret); 4518c2ecf20Sopenharmony_ci return ret; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci i2c_set_clientdata(client, mcp); 4558c2ecf20Sopenharmony_ci config.regmap = rmap; 4568c2ecf20Sopenharmony_ci config.driver_data = mcp; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci mcp->lpm = devm_gpiod_get(dev, "lpm", GPIOD_OUT_LOW); 4598c2ecf20Sopenharmony_ci if (IS_ERR(mcp->lpm)) { 4608c2ecf20Sopenharmony_ci dev_err(dev, "failed to get lpm pin: %ld\n", PTR_ERR(mcp->lpm)); 4618c2ecf20Sopenharmony_ci return PTR_ERR(mcp->lpm); 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci for (i = 0; i < NUM_REGULATORS; i++) { 4658c2ecf20Sopenharmony_ci rdev = devm_regulator_register(dev, &mcp16502_desc[i], &config); 4668c2ecf20Sopenharmony_ci if (IS_ERR(rdev)) { 4678c2ecf20Sopenharmony_ci dev_err(dev, 4688c2ecf20Sopenharmony_ci "failed to register %s regulator %ld\n", 4698c2ecf20Sopenharmony_ci mcp16502_desc[i].name, PTR_ERR(rdev)); 4708c2ecf20Sopenharmony_ci return PTR_ERR(rdev); 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci mcp16502_gpio_set_mode(mcp, MCP16502_OPMODE_ACTIVE); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci return 0; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4808c2ecf20Sopenharmony_cistatic int mcp16502_suspend_noirq(struct device *dev) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 4838c2ecf20Sopenharmony_ci struct mcp16502 *mcp = i2c_get_clientdata(client); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci mcp16502_gpio_set_mode(mcp, MCP16502_OPMODE_LPM); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci return 0; 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic int mcp16502_resume_noirq(struct device *dev) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 4938c2ecf20Sopenharmony_ci struct mcp16502 *mcp = i2c_get_clientdata(client); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci mcp16502_gpio_set_mode(mcp, MCP16502_OPMODE_ACTIVE); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return 0; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci#endif 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 5028c2ecf20Sopenharmony_cistatic const struct dev_pm_ops mcp16502_pm_ops = { 5038c2ecf20Sopenharmony_ci SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mcp16502_suspend_noirq, 5048c2ecf20Sopenharmony_ci mcp16502_resume_noirq) 5058c2ecf20Sopenharmony_ci}; 5068c2ecf20Sopenharmony_ci#endif 5078c2ecf20Sopenharmony_cistatic const struct i2c_device_id mcp16502_i2c_id[] = { 5088c2ecf20Sopenharmony_ci { "mcp16502", 0 }, 5098c2ecf20Sopenharmony_ci { } 5108c2ecf20Sopenharmony_ci}; 5118c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, mcp16502_i2c_id); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic struct i2c_driver mcp16502_drv = { 5148c2ecf20Sopenharmony_ci .probe = mcp16502_probe, 5158c2ecf20Sopenharmony_ci .driver = { 5168c2ecf20Sopenharmony_ci .name = "mcp16502-regulator", 5178c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(mcp16502_ids), 5188c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 5198c2ecf20Sopenharmony_ci .pm = &mcp16502_pm_ops, 5208c2ecf20Sopenharmony_ci#endif 5218c2ecf20Sopenharmony_ci }, 5228c2ecf20Sopenharmony_ci .id_table = mcp16502_i2c_id, 5238c2ecf20Sopenharmony_ci}; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cimodule_i2c_driver(mcp16502_drv); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 5288c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MCP16502 PMIC driver"); 5298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andrei Stefanescu andrei.stefanescu@microchip.com"); 530