18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * max8660.c -- Voltage regulation for the Maxim 8660/8661 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * based on max1586.c and wm8400-regulator.c 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2009 Wolfram Sang, Pengutronix e.K. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Some info: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX8660-MAX8661.pdf 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This chip is a bit nasty because it is a write-only device. Thus, the driver 148c2ecf20Sopenharmony_ci * uses shadow registers to keep track of its values. The main problem appears 158c2ecf20Sopenharmony_ci * to be the initialization: When Linux boots up, we cannot know if the chip is 168c2ecf20Sopenharmony_ci * in the default state or not, so we would have to pass such information in 178c2ecf20Sopenharmony_ci * platform_data. As this adds a bit of complexity to the driver, this is left 188c2ecf20Sopenharmony_ci * out for now until it is really needed. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * [A|S|M]DTV1 registers are currently not used, but [A|S|M]DTV2. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * If the driver is feature complete, it might be worth to check if one set of 238c2ecf20Sopenharmony_ci * functions for V3-V7 is sufficient. For maximum flexibility during 248c2ecf20Sopenharmony_ci * development, they are separated for now. 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <linux/module.h> 288c2ecf20Sopenharmony_ci#include <linux/err.h> 298c2ecf20Sopenharmony_ci#include <linux/i2c.h> 308c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 318c2ecf20Sopenharmony_ci#include <linux/regulator/driver.h> 328c2ecf20Sopenharmony_ci#include <linux/slab.h> 338c2ecf20Sopenharmony_ci#include <linux/regulator/max8660.h> 348c2ecf20Sopenharmony_ci#include <linux/of.h> 358c2ecf20Sopenharmony_ci#include <linux/of_device.h> 368c2ecf20Sopenharmony_ci#include <linux/regulator/of_regulator.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define MAX8660_DCDC_MIN_UV 725000 398c2ecf20Sopenharmony_ci#define MAX8660_DCDC_MAX_UV 1800000 408c2ecf20Sopenharmony_ci#define MAX8660_DCDC_STEP 25000 418c2ecf20Sopenharmony_ci#define MAX8660_DCDC_MAX_SEL 0x2b 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define MAX8660_LDO5_MIN_UV 1700000 448c2ecf20Sopenharmony_ci#define MAX8660_LDO5_MAX_UV 2000000 458c2ecf20Sopenharmony_ci#define MAX8660_LDO5_STEP 25000 468c2ecf20Sopenharmony_ci#define MAX8660_LDO5_MAX_SEL 0x0c 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define MAX8660_LDO67_MIN_UV 1800000 498c2ecf20Sopenharmony_ci#define MAX8660_LDO67_MAX_UV 3300000 508c2ecf20Sopenharmony_ci#define MAX8660_LDO67_STEP 100000 518c2ecf20Sopenharmony_ci#define MAX8660_LDO67_MAX_SEL 0x0f 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cienum { 548c2ecf20Sopenharmony_ci MAX8660_OVER1, 558c2ecf20Sopenharmony_ci MAX8660_OVER2, 568c2ecf20Sopenharmony_ci MAX8660_VCC1, 578c2ecf20Sopenharmony_ci MAX8660_ADTV1, 588c2ecf20Sopenharmony_ci MAX8660_ADTV2, 598c2ecf20Sopenharmony_ci MAX8660_SDTV1, 608c2ecf20Sopenharmony_ci MAX8660_SDTV2, 618c2ecf20Sopenharmony_ci MAX8660_MDTV1, 628c2ecf20Sopenharmony_ci MAX8660_MDTV2, 638c2ecf20Sopenharmony_ci MAX8660_L12VCR, 648c2ecf20Sopenharmony_ci MAX8660_FPWM, 658c2ecf20Sopenharmony_ci MAX8660_N_REGS, /* not a real register */ 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistruct max8660 { 698c2ecf20Sopenharmony_ci struct i2c_client *client; 708c2ecf20Sopenharmony_ci u8 shadow_regs[MAX8660_N_REGS]; /* as chip is write only */ 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int max8660_write(struct max8660 *max8660, u8 reg, u8 mask, u8 val) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci static const u8 max8660_addresses[MAX8660_N_REGS] = { 768c2ecf20Sopenharmony_ci 0x10, 0x12, 0x20, 0x23, 0x24, 0x29, 0x2a, 0x32, 0x33, 0x39, 0x80 778c2ecf20Sopenharmony_ci }; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci int ret; 808c2ecf20Sopenharmony_ci u8 reg_val = (max8660->shadow_regs[reg] & mask) | val; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci dev_vdbg(&max8660->client->dev, "Writing reg %02x with %02x\n", 838c2ecf20Sopenharmony_ci max8660_addresses[reg], reg_val); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(max8660->client, 868c2ecf20Sopenharmony_ci max8660_addresses[reg], reg_val); 878c2ecf20Sopenharmony_ci if (ret == 0) 888c2ecf20Sopenharmony_ci max8660->shadow_regs[reg] = reg_val; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return ret; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* 958c2ecf20Sopenharmony_ci * DCDC functions 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int max8660_dcdc_is_enabled(struct regulator_dev *rdev) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct max8660 *max8660 = rdev_get_drvdata(rdev); 1018c2ecf20Sopenharmony_ci u8 val = max8660->shadow_regs[MAX8660_OVER1]; 1028c2ecf20Sopenharmony_ci u8 mask = (rdev_get_id(rdev) == MAX8660_V3) ? 1 : 4; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return !!(val & mask); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int max8660_dcdc_enable(struct regulator_dev *rdev) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct max8660 *max8660 = rdev_get_drvdata(rdev); 1108c2ecf20Sopenharmony_ci u8 bit = (rdev_get_id(rdev) == MAX8660_V3) ? 1 : 4; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return max8660_write(max8660, MAX8660_OVER1, 0xff, bit); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int max8660_dcdc_disable(struct regulator_dev *rdev) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct max8660 *max8660 = rdev_get_drvdata(rdev); 1188c2ecf20Sopenharmony_ci u8 mask = (rdev_get_id(rdev) == MAX8660_V3) ? ~1 : ~4; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return max8660_write(max8660, MAX8660_OVER1, mask, 0); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int max8660_dcdc_get_voltage_sel(struct regulator_dev *rdev) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct max8660 *max8660 = rdev_get_drvdata(rdev); 1268c2ecf20Sopenharmony_ci u8 reg = (rdev_get_id(rdev) == MAX8660_V3) ? MAX8660_ADTV2 : MAX8660_SDTV2; 1278c2ecf20Sopenharmony_ci u8 selector = max8660->shadow_regs[reg]; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return selector; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic int max8660_dcdc_set_voltage_sel(struct regulator_dev *rdev, 1338c2ecf20Sopenharmony_ci unsigned int selector) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct max8660 *max8660 = rdev_get_drvdata(rdev); 1368c2ecf20Sopenharmony_ci u8 reg, bits; 1378c2ecf20Sopenharmony_ci int ret; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci reg = (rdev_get_id(rdev) == MAX8660_V3) ? MAX8660_ADTV2 : MAX8660_SDTV2; 1408c2ecf20Sopenharmony_ci ret = max8660_write(max8660, reg, 0, selector); 1418c2ecf20Sopenharmony_ci if (ret) 1428c2ecf20Sopenharmony_ci return ret; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* Select target voltage register and activate regulation */ 1458c2ecf20Sopenharmony_ci bits = (rdev_get_id(rdev) == MAX8660_V3) ? 0x03 : 0x30; 1468c2ecf20Sopenharmony_ci return max8660_write(max8660, MAX8660_VCC1, 0xff, bits); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic struct regulator_ops max8660_dcdc_ops = { 1508c2ecf20Sopenharmony_ci .is_enabled = max8660_dcdc_is_enabled, 1518c2ecf20Sopenharmony_ci .list_voltage = regulator_list_voltage_linear, 1528c2ecf20Sopenharmony_ci .map_voltage = regulator_map_voltage_linear, 1538c2ecf20Sopenharmony_ci .set_voltage_sel = max8660_dcdc_set_voltage_sel, 1548c2ecf20Sopenharmony_ci .get_voltage_sel = max8660_dcdc_get_voltage_sel, 1558c2ecf20Sopenharmony_ci}; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/* 1598c2ecf20Sopenharmony_ci * LDO5 functions 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic int max8660_ldo5_get_voltage_sel(struct regulator_dev *rdev) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci struct max8660 *max8660 = rdev_get_drvdata(rdev); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci u8 selector = max8660->shadow_regs[MAX8660_MDTV2]; 1678c2ecf20Sopenharmony_ci return selector; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic int max8660_ldo5_set_voltage_sel(struct regulator_dev *rdev, 1718c2ecf20Sopenharmony_ci unsigned int selector) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct max8660 *max8660 = rdev_get_drvdata(rdev); 1748c2ecf20Sopenharmony_ci int ret; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci ret = max8660_write(max8660, MAX8660_MDTV2, 0, selector); 1778c2ecf20Sopenharmony_ci if (ret) 1788c2ecf20Sopenharmony_ci return ret; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* Select target voltage register and activate regulation */ 1818c2ecf20Sopenharmony_ci return max8660_write(max8660, MAX8660_VCC1, 0xff, 0xc0); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic const struct regulator_ops max8660_ldo5_ops = { 1858c2ecf20Sopenharmony_ci .list_voltage = regulator_list_voltage_linear, 1868c2ecf20Sopenharmony_ci .map_voltage = regulator_map_voltage_linear, 1878c2ecf20Sopenharmony_ci .set_voltage_sel = max8660_ldo5_set_voltage_sel, 1888c2ecf20Sopenharmony_ci .get_voltage_sel = max8660_ldo5_get_voltage_sel, 1898c2ecf20Sopenharmony_ci}; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci/* 1938c2ecf20Sopenharmony_ci * LDO67 functions 1948c2ecf20Sopenharmony_ci */ 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int max8660_ldo67_is_enabled(struct regulator_dev *rdev) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct max8660 *max8660 = rdev_get_drvdata(rdev); 1998c2ecf20Sopenharmony_ci u8 val = max8660->shadow_regs[MAX8660_OVER2]; 2008c2ecf20Sopenharmony_ci u8 mask = (rdev_get_id(rdev) == MAX8660_V6) ? 2 : 4; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci return !!(val & mask); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int max8660_ldo67_enable(struct regulator_dev *rdev) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct max8660 *max8660 = rdev_get_drvdata(rdev); 2088c2ecf20Sopenharmony_ci u8 bit = (rdev_get_id(rdev) == MAX8660_V6) ? 2 : 4; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return max8660_write(max8660, MAX8660_OVER2, 0xff, bit); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic int max8660_ldo67_disable(struct regulator_dev *rdev) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct max8660 *max8660 = rdev_get_drvdata(rdev); 2168c2ecf20Sopenharmony_ci u8 mask = (rdev_get_id(rdev) == MAX8660_V6) ? ~2 : ~4; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci return max8660_write(max8660, MAX8660_OVER2, mask, 0); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic int max8660_ldo67_get_voltage_sel(struct regulator_dev *rdev) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct max8660 *max8660 = rdev_get_drvdata(rdev); 2248c2ecf20Sopenharmony_ci u8 shift = (rdev_get_id(rdev) == MAX8660_V6) ? 0 : 4; 2258c2ecf20Sopenharmony_ci u8 selector = (max8660->shadow_regs[MAX8660_L12VCR] >> shift) & 0xf; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci return selector; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic int max8660_ldo67_set_voltage_sel(struct regulator_dev *rdev, 2318c2ecf20Sopenharmony_ci unsigned int selector) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct max8660 *max8660 = rdev_get_drvdata(rdev); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (rdev_get_id(rdev) == MAX8660_V6) 2368c2ecf20Sopenharmony_ci return max8660_write(max8660, MAX8660_L12VCR, 0xf0, selector); 2378c2ecf20Sopenharmony_ci else 2388c2ecf20Sopenharmony_ci return max8660_write(max8660, MAX8660_L12VCR, 0x0f, 2398c2ecf20Sopenharmony_ci selector << 4); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic const struct regulator_ops max8660_ldo67_ops = { 2438c2ecf20Sopenharmony_ci .is_enabled = max8660_ldo67_is_enabled, 2448c2ecf20Sopenharmony_ci .enable = max8660_ldo67_enable, 2458c2ecf20Sopenharmony_ci .disable = max8660_ldo67_disable, 2468c2ecf20Sopenharmony_ci .list_voltage = regulator_list_voltage_linear, 2478c2ecf20Sopenharmony_ci .map_voltage = regulator_map_voltage_linear, 2488c2ecf20Sopenharmony_ci .get_voltage_sel = max8660_ldo67_get_voltage_sel, 2498c2ecf20Sopenharmony_ci .set_voltage_sel = max8660_ldo67_set_voltage_sel, 2508c2ecf20Sopenharmony_ci}; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic const struct regulator_desc max8660_reg[] = { 2538c2ecf20Sopenharmony_ci { 2548c2ecf20Sopenharmony_ci .name = "V3(DCDC)", 2558c2ecf20Sopenharmony_ci .id = MAX8660_V3, 2568c2ecf20Sopenharmony_ci .ops = &max8660_dcdc_ops, 2578c2ecf20Sopenharmony_ci .type = REGULATOR_VOLTAGE, 2588c2ecf20Sopenharmony_ci .n_voltages = MAX8660_DCDC_MAX_SEL + 1, 2598c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2608c2ecf20Sopenharmony_ci .min_uV = MAX8660_DCDC_MIN_UV, 2618c2ecf20Sopenharmony_ci .uV_step = MAX8660_DCDC_STEP, 2628c2ecf20Sopenharmony_ci }, 2638c2ecf20Sopenharmony_ci { 2648c2ecf20Sopenharmony_ci .name = "V4(DCDC)", 2658c2ecf20Sopenharmony_ci .id = MAX8660_V4, 2668c2ecf20Sopenharmony_ci .ops = &max8660_dcdc_ops, 2678c2ecf20Sopenharmony_ci .type = REGULATOR_VOLTAGE, 2688c2ecf20Sopenharmony_ci .n_voltages = MAX8660_DCDC_MAX_SEL + 1, 2698c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2708c2ecf20Sopenharmony_ci .min_uV = MAX8660_DCDC_MIN_UV, 2718c2ecf20Sopenharmony_ci .uV_step = MAX8660_DCDC_STEP, 2728c2ecf20Sopenharmony_ci }, 2738c2ecf20Sopenharmony_ci { 2748c2ecf20Sopenharmony_ci .name = "V5(LDO)", 2758c2ecf20Sopenharmony_ci .id = MAX8660_V5, 2768c2ecf20Sopenharmony_ci .ops = &max8660_ldo5_ops, 2778c2ecf20Sopenharmony_ci .type = REGULATOR_VOLTAGE, 2788c2ecf20Sopenharmony_ci .n_voltages = MAX8660_LDO5_MAX_SEL + 1, 2798c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2808c2ecf20Sopenharmony_ci .min_uV = MAX8660_LDO5_MIN_UV, 2818c2ecf20Sopenharmony_ci .uV_step = MAX8660_LDO5_STEP, 2828c2ecf20Sopenharmony_ci }, 2838c2ecf20Sopenharmony_ci { 2848c2ecf20Sopenharmony_ci .name = "V6(LDO)", 2858c2ecf20Sopenharmony_ci .id = MAX8660_V6, 2868c2ecf20Sopenharmony_ci .ops = &max8660_ldo67_ops, 2878c2ecf20Sopenharmony_ci .type = REGULATOR_VOLTAGE, 2888c2ecf20Sopenharmony_ci .n_voltages = MAX8660_LDO67_MAX_SEL + 1, 2898c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2908c2ecf20Sopenharmony_ci .min_uV = MAX8660_LDO67_MIN_UV, 2918c2ecf20Sopenharmony_ci .uV_step = MAX8660_LDO67_STEP, 2928c2ecf20Sopenharmony_ci }, 2938c2ecf20Sopenharmony_ci { 2948c2ecf20Sopenharmony_ci .name = "V7(LDO)", 2958c2ecf20Sopenharmony_ci .id = MAX8660_V7, 2968c2ecf20Sopenharmony_ci .ops = &max8660_ldo67_ops, 2978c2ecf20Sopenharmony_ci .type = REGULATOR_VOLTAGE, 2988c2ecf20Sopenharmony_ci .n_voltages = MAX8660_LDO67_MAX_SEL + 1, 2998c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3008c2ecf20Sopenharmony_ci .min_uV = MAX8660_LDO67_MIN_UV, 3018c2ecf20Sopenharmony_ci .uV_step = MAX8660_LDO67_STEP, 3028c2ecf20Sopenharmony_ci }, 3038c2ecf20Sopenharmony_ci}; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cienum { 3068c2ecf20Sopenharmony_ci MAX8660 = 0, 3078c2ecf20Sopenharmony_ci MAX8661 = 1, 3088c2ecf20Sopenharmony_ci}; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 3118c2ecf20Sopenharmony_cistatic const struct of_device_id max8660_dt_ids[] = { 3128c2ecf20Sopenharmony_ci { .compatible = "maxim,max8660", .data = (void *) MAX8660 }, 3138c2ecf20Sopenharmony_ci { .compatible = "maxim,max8661", .data = (void *) MAX8661 }, 3148c2ecf20Sopenharmony_ci { } 3158c2ecf20Sopenharmony_ci}; 3168c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, max8660_dt_ids); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic int max8660_pdata_from_dt(struct device *dev, 3198c2ecf20Sopenharmony_ci struct device_node **of_node, 3208c2ecf20Sopenharmony_ci struct max8660_platform_data *pdata) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci int matched, i; 3238c2ecf20Sopenharmony_ci struct device_node *np; 3248c2ecf20Sopenharmony_ci struct max8660_subdev_data *sub; 3258c2ecf20Sopenharmony_ci struct of_regulator_match rmatch[ARRAY_SIZE(max8660_reg)] = { }; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci np = of_get_child_by_name(dev->of_node, "regulators"); 3288c2ecf20Sopenharmony_ci if (!np) { 3298c2ecf20Sopenharmony_ci dev_err(dev, "missing 'regulators' subnode in DT\n"); 3308c2ecf20Sopenharmony_ci return -EINVAL; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(rmatch); i++) 3348c2ecf20Sopenharmony_ci rmatch[i].name = max8660_reg[i].name; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci matched = of_regulator_match(dev, np, rmatch, ARRAY_SIZE(rmatch)); 3378c2ecf20Sopenharmony_ci of_node_put(np); 3388c2ecf20Sopenharmony_ci if (matched <= 0) 3398c2ecf20Sopenharmony_ci return matched; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci pdata->subdevs = devm_kcalloc(dev, 3428c2ecf20Sopenharmony_ci matched, 3438c2ecf20Sopenharmony_ci sizeof(struct max8660_subdev_data), 3448c2ecf20Sopenharmony_ci GFP_KERNEL); 3458c2ecf20Sopenharmony_ci if (!pdata->subdevs) 3468c2ecf20Sopenharmony_ci return -ENOMEM; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci pdata->num_subdevs = matched; 3498c2ecf20Sopenharmony_ci sub = pdata->subdevs; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci for (i = 0; i < matched; i++) { 3528c2ecf20Sopenharmony_ci sub->id = i; 3538c2ecf20Sopenharmony_ci sub->name = rmatch[i].name; 3548c2ecf20Sopenharmony_ci sub->platform_data = rmatch[i].init_data; 3558c2ecf20Sopenharmony_ci of_node[i] = rmatch[i].of_node; 3568c2ecf20Sopenharmony_ci sub++; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci return 0; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci#else 3628c2ecf20Sopenharmony_cistatic inline int max8660_pdata_from_dt(struct device *dev, 3638c2ecf20Sopenharmony_ci struct device_node **of_node, 3648c2ecf20Sopenharmony_ci struct max8660_platform_data *pdata) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci return 0; 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci#endif 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic int max8660_probe(struct i2c_client *client, 3718c2ecf20Sopenharmony_ci const struct i2c_device_id *i2c_id) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 3748c2ecf20Sopenharmony_ci struct max8660_platform_data pdata_of, *pdata = dev_get_platdata(dev); 3758c2ecf20Sopenharmony_ci struct regulator_config config = { }; 3768c2ecf20Sopenharmony_ci struct max8660 *max8660; 3778c2ecf20Sopenharmony_ci int boot_on, i, id, ret = -EINVAL; 3788c2ecf20Sopenharmony_ci struct device_node *of_node[MAX8660_V_END]; 3798c2ecf20Sopenharmony_ci unsigned long type; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (dev->of_node && !pdata) { 3828c2ecf20Sopenharmony_ci const struct of_device_id *id; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci id = of_match_device(of_match_ptr(max8660_dt_ids), dev); 3858c2ecf20Sopenharmony_ci if (!id) 3868c2ecf20Sopenharmony_ci return -ENODEV; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci ret = max8660_pdata_from_dt(dev, of_node, &pdata_of); 3898c2ecf20Sopenharmony_ci if (ret < 0) 3908c2ecf20Sopenharmony_ci return ret; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci pdata = &pdata_of; 3938c2ecf20Sopenharmony_ci type = (unsigned long) id->data; 3948c2ecf20Sopenharmony_ci } else { 3958c2ecf20Sopenharmony_ci type = i2c_id->driver_data; 3968c2ecf20Sopenharmony_ci memset(of_node, 0, sizeof(of_node)); 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (pdata->num_subdevs > MAX8660_V_END) { 4008c2ecf20Sopenharmony_ci dev_err(dev, "Too many regulators found!\n"); 4018c2ecf20Sopenharmony_ci return -EINVAL; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci max8660 = devm_kzalloc(dev, sizeof(struct max8660), GFP_KERNEL); 4058c2ecf20Sopenharmony_ci if (!max8660) 4068c2ecf20Sopenharmony_ci return -ENOMEM; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci max8660->client = client; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (pdata->en34_is_high) { 4118c2ecf20Sopenharmony_ci /* Simulate always on */ 4128c2ecf20Sopenharmony_ci max8660->shadow_regs[MAX8660_OVER1] = 5; 4138c2ecf20Sopenharmony_ci } else { 4148c2ecf20Sopenharmony_ci /* Otherwise devices can be toggled via software */ 4158c2ecf20Sopenharmony_ci max8660_dcdc_ops.enable = max8660_dcdc_enable; 4168c2ecf20Sopenharmony_ci max8660_dcdc_ops.disable = max8660_dcdc_disable; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* 4208c2ecf20Sopenharmony_ci * First, set up shadow registers to prevent glitches. As some 4218c2ecf20Sopenharmony_ci * registers are shared between regulators, everything must be properly 4228c2ecf20Sopenharmony_ci * set up for all regulators in advance. 4238c2ecf20Sopenharmony_ci */ 4248c2ecf20Sopenharmony_ci max8660->shadow_regs[MAX8660_ADTV1] = 4258c2ecf20Sopenharmony_ci max8660->shadow_regs[MAX8660_ADTV2] = 4268c2ecf20Sopenharmony_ci max8660->shadow_regs[MAX8660_SDTV1] = 4278c2ecf20Sopenharmony_ci max8660->shadow_regs[MAX8660_SDTV2] = 0x1b; 4288c2ecf20Sopenharmony_ci max8660->shadow_regs[MAX8660_MDTV1] = 4298c2ecf20Sopenharmony_ci max8660->shadow_regs[MAX8660_MDTV2] = 0x04; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci for (i = 0; i < pdata->num_subdevs; i++) { 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci if (!pdata->subdevs[i].platform_data) 4348c2ecf20Sopenharmony_ci boot_on = false; 4358c2ecf20Sopenharmony_ci else 4368c2ecf20Sopenharmony_ci boot_on = pdata->subdevs[i].platform_data->constraints.boot_on; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci switch (pdata->subdevs[i].id) { 4398c2ecf20Sopenharmony_ci case MAX8660_V3: 4408c2ecf20Sopenharmony_ci if (boot_on) 4418c2ecf20Sopenharmony_ci max8660->shadow_regs[MAX8660_OVER1] |= 1; 4428c2ecf20Sopenharmony_ci break; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci case MAX8660_V4: 4458c2ecf20Sopenharmony_ci if (boot_on) 4468c2ecf20Sopenharmony_ci max8660->shadow_regs[MAX8660_OVER1] |= 4; 4478c2ecf20Sopenharmony_ci break; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci case MAX8660_V5: 4508c2ecf20Sopenharmony_ci break; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci case MAX8660_V6: 4538c2ecf20Sopenharmony_ci if (boot_on) 4548c2ecf20Sopenharmony_ci max8660->shadow_regs[MAX8660_OVER2] |= 2; 4558c2ecf20Sopenharmony_ci break; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci case MAX8660_V7: 4588c2ecf20Sopenharmony_ci if (type == MAX8661) { 4598c2ecf20Sopenharmony_ci dev_err(dev, "Regulator not on this chip!\n"); 4608c2ecf20Sopenharmony_ci return -EINVAL; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (boot_on) 4648c2ecf20Sopenharmony_ci max8660->shadow_regs[MAX8660_OVER2] |= 4; 4658c2ecf20Sopenharmony_ci break; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci default: 4688c2ecf20Sopenharmony_ci dev_err(dev, "invalid regulator %s\n", 4698c2ecf20Sopenharmony_ci pdata->subdevs[i].name); 4708c2ecf20Sopenharmony_ci return ret; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci /* Finally register devices */ 4758c2ecf20Sopenharmony_ci for (i = 0; i < pdata->num_subdevs; i++) { 4768c2ecf20Sopenharmony_ci struct regulator_dev *rdev; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci id = pdata->subdevs[i].id; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci config.dev = dev; 4818c2ecf20Sopenharmony_ci config.init_data = pdata->subdevs[i].platform_data; 4828c2ecf20Sopenharmony_ci config.of_node = of_node[i]; 4838c2ecf20Sopenharmony_ci config.driver_data = max8660; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci rdev = devm_regulator_register(&client->dev, 4868c2ecf20Sopenharmony_ci &max8660_reg[id], &config); 4878c2ecf20Sopenharmony_ci if (IS_ERR(rdev)) { 4888c2ecf20Sopenharmony_ci dev_err(&client->dev, "failed to register %s\n", 4898c2ecf20Sopenharmony_ci max8660_reg[id].name); 4908c2ecf20Sopenharmony_ci return PTR_ERR(rdev); 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci i2c_set_clientdata(client, max8660); 4958c2ecf20Sopenharmony_ci return 0; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic const struct i2c_device_id max8660_id[] = { 4998c2ecf20Sopenharmony_ci { .name = "max8660", .driver_data = MAX8660 }, 5008c2ecf20Sopenharmony_ci { .name = "max8661", .driver_data = MAX8661 }, 5018c2ecf20Sopenharmony_ci { } 5028c2ecf20Sopenharmony_ci}; 5038c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, max8660_id); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic struct i2c_driver max8660_driver = { 5068c2ecf20Sopenharmony_ci .probe = max8660_probe, 5078c2ecf20Sopenharmony_ci .driver = { 5088c2ecf20Sopenharmony_ci .name = "max8660", 5098c2ecf20Sopenharmony_ci }, 5108c2ecf20Sopenharmony_ci .id_table = max8660_id, 5118c2ecf20Sopenharmony_ci}; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic int __init max8660_init(void) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci return i2c_add_driver(&max8660_driver); 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_cisubsys_initcall(max8660_init); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic void __exit max8660_exit(void) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci i2c_del_driver(&max8660_driver); 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_cimodule_exit(max8660_exit); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci/* Module information */ 5268c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MAXIM 8660/8661 voltage regulator driver"); 5278c2ecf20Sopenharmony_ciMODULE_AUTHOR("Wolfram Sang"); 5288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 529