18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Regulators driver for Maxim max8649 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2009-2010 Marvell International Ltd. 68c2ecf20Sopenharmony_ci * Haojian Zhuang <haojian.zhuang@marvell.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/err.h> 118c2ecf20Sopenharmony_ci#include <linux/i2c.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/regulator/driver.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/regulator/max8649.h> 168c2ecf20Sopenharmony_ci#include <linux/regmap.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define MAX8649_DCDC_VMIN 750000 /* uV */ 198c2ecf20Sopenharmony_ci#define MAX8649_DCDC_VMAX 1380000 /* uV */ 208c2ecf20Sopenharmony_ci#define MAX8649_DCDC_STEP 10000 /* uV */ 218c2ecf20Sopenharmony_ci#define MAX8649_VOL_MASK 0x3f 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* Registers */ 248c2ecf20Sopenharmony_ci#define MAX8649_MODE0 0x00 258c2ecf20Sopenharmony_ci#define MAX8649_MODE1 0x01 268c2ecf20Sopenharmony_ci#define MAX8649_MODE2 0x02 278c2ecf20Sopenharmony_ci#define MAX8649_MODE3 0x03 288c2ecf20Sopenharmony_ci#define MAX8649_CONTROL 0x04 298c2ecf20Sopenharmony_ci#define MAX8649_SYNC 0x05 308c2ecf20Sopenharmony_ci#define MAX8649_RAMP 0x06 318c2ecf20Sopenharmony_ci#define MAX8649_CHIP_ID1 0x08 328c2ecf20Sopenharmony_ci#define MAX8649_CHIP_ID2 0x09 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* Bits */ 358c2ecf20Sopenharmony_ci#define MAX8649_EN_PD (1 << 7) 368c2ecf20Sopenharmony_ci#define MAX8649_VID0_PD (1 << 6) 378c2ecf20Sopenharmony_ci#define MAX8649_VID1_PD (1 << 5) 388c2ecf20Sopenharmony_ci#define MAX8649_VID_MASK (3 << 5) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define MAX8649_FORCE_PWM (1 << 7) 418c2ecf20Sopenharmony_ci#define MAX8649_SYNC_EXTCLK (1 << 6) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define MAX8649_EXT_MASK (3 << 6) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define MAX8649_RAMP_MASK (7 << 5) 468c2ecf20Sopenharmony_ci#define MAX8649_RAMP_DOWN (1 << 1) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistruct max8649_regulator_info { 498c2ecf20Sopenharmony_ci struct device *dev; 508c2ecf20Sopenharmony_ci struct regmap *regmap; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci unsigned mode:2; /* bit[1:0] = VID1, VID0 */ 538c2ecf20Sopenharmony_ci unsigned extclk_freq:2; 548c2ecf20Sopenharmony_ci unsigned extclk:1; 558c2ecf20Sopenharmony_ci unsigned ramp_timing:3; 568c2ecf20Sopenharmony_ci unsigned ramp_down:1; 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic int max8649_enable_time(struct regulator_dev *rdev) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci struct max8649_regulator_info *info = rdev_get_drvdata(rdev); 628c2ecf20Sopenharmony_ci int voltage, rate, ret; 638c2ecf20Sopenharmony_ci unsigned int val; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci /* get voltage */ 668c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, rdev->desc->vsel_reg, &val); 678c2ecf20Sopenharmony_ci if (ret != 0) 688c2ecf20Sopenharmony_ci return ret; 698c2ecf20Sopenharmony_ci val &= MAX8649_VOL_MASK; 708c2ecf20Sopenharmony_ci voltage = regulator_list_voltage_linear(rdev, (unsigned char)val); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* get rate */ 738c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, MAX8649_RAMP, &val); 748c2ecf20Sopenharmony_ci if (ret != 0) 758c2ecf20Sopenharmony_ci return ret; 768c2ecf20Sopenharmony_ci ret = (val & MAX8649_RAMP_MASK) >> 5; 778c2ecf20Sopenharmony_ci rate = (32 * 1000) >> ret; /* uV/uS */ 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci return DIV_ROUND_UP(voltage, rate); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic int max8649_set_mode(struct regulator_dev *rdev, unsigned int mode) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct max8649_regulator_info *info = rdev_get_drvdata(rdev); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci switch (mode) { 878c2ecf20Sopenharmony_ci case REGULATOR_MODE_FAST: 888c2ecf20Sopenharmony_ci regmap_update_bits(info->regmap, rdev->desc->vsel_reg, 898c2ecf20Sopenharmony_ci MAX8649_FORCE_PWM, MAX8649_FORCE_PWM); 908c2ecf20Sopenharmony_ci break; 918c2ecf20Sopenharmony_ci case REGULATOR_MODE_NORMAL: 928c2ecf20Sopenharmony_ci regmap_update_bits(info->regmap, rdev->desc->vsel_reg, 938c2ecf20Sopenharmony_ci MAX8649_FORCE_PWM, 0); 948c2ecf20Sopenharmony_ci break; 958c2ecf20Sopenharmony_ci default: 968c2ecf20Sopenharmony_ci return -EINVAL; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci return 0; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic unsigned int max8649_get_mode(struct regulator_dev *rdev) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct max8649_regulator_info *info = rdev_get_drvdata(rdev); 1048c2ecf20Sopenharmony_ci unsigned int val; 1058c2ecf20Sopenharmony_ci int ret; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, rdev->desc->vsel_reg, &val); 1088c2ecf20Sopenharmony_ci if (ret != 0) 1098c2ecf20Sopenharmony_ci return ret; 1108c2ecf20Sopenharmony_ci if (val & MAX8649_FORCE_PWM) 1118c2ecf20Sopenharmony_ci return REGULATOR_MODE_FAST; 1128c2ecf20Sopenharmony_ci return REGULATOR_MODE_NORMAL; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic const struct regulator_ops max8649_dcdc_ops = { 1168c2ecf20Sopenharmony_ci .set_voltage_sel = regulator_set_voltage_sel_regmap, 1178c2ecf20Sopenharmony_ci .get_voltage_sel = regulator_get_voltage_sel_regmap, 1188c2ecf20Sopenharmony_ci .list_voltage = regulator_list_voltage_linear, 1198c2ecf20Sopenharmony_ci .map_voltage = regulator_map_voltage_linear, 1208c2ecf20Sopenharmony_ci .enable = regulator_enable_regmap, 1218c2ecf20Sopenharmony_ci .disable = regulator_disable_regmap, 1228c2ecf20Sopenharmony_ci .is_enabled = regulator_is_enabled_regmap, 1238c2ecf20Sopenharmony_ci .enable_time = max8649_enable_time, 1248c2ecf20Sopenharmony_ci .set_mode = max8649_set_mode, 1258c2ecf20Sopenharmony_ci .get_mode = max8649_get_mode, 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci}; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic struct regulator_desc dcdc_desc = { 1308c2ecf20Sopenharmony_ci .name = "max8649", 1318c2ecf20Sopenharmony_ci .ops = &max8649_dcdc_ops, 1328c2ecf20Sopenharmony_ci .type = REGULATOR_VOLTAGE, 1338c2ecf20Sopenharmony_ci .n_voltages = 1 << 6, 1348c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1358c2ecf20Sopenharmony_ci .vsel_mask = MAX8649_VOL_MASK, 1368c2ecf20Sopenharmony_ci .min_uV = MAX8649_DCDC_VMIN, 1378c2ecf20Sopenharmony_ci .uV_step = MAX8649_DCDC_STEP, 1388c2ecf20Sopenharmony_ci .enable_reg = MAX8649_CONTROL, 1398c2ecf20Sopenharmony_ci .enable_mask = MAX8649_EN_PD, 1408c2ecf20Sopenharmony_ci .enable_is_inverted = true, 1418c2ecf20Sopenharmony_ci}; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic const struct regmap_config max8649_regmap_config = { 1448c2ecf20Sopenharmony_ci .reg_bits = 8, 1458c2ecf20Sopenharmony_ci .val_bits = 8, 1468c2ecf20Sopenharmony_ci}; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic int max8649_regulator_probe(struct i2c_client *client, 1498c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct max8649_platform_data *pdata = dev_get_platdata(&client->dev); 1528c2ecf20Sopenharmony_ci struct max8649_regulator_info *info = NULL; 1538c2ecf20Sopenharmony_ci struct regulator_dev *regulator; 1548c2ecf20Sopenharmony_ci struct regulator_config config = { }; 1558c2ecf20Sopenharmony_ci unsigned int val; 1568c2ecf20Sopenharmony_ci unsigned char data; 1578c2ecf20Sopenharmony_ci int ret; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci info = devm_kzalloc(&client->dev, sizeof(struct max8649_regulator_info), 1608c2ecf20Sopenharmony_ci GFP_KERNEL); 1618c2ecf20Sopenharmony_ci if (!info) 1628c2ecf20Sopenharmony_ci return -ENOMEM; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci info->regmap = devm_regmap_init_i2c(client, &max8649_regmap_config); 1658c2ecf20Sopenharmony_ci if (IS_ERR(info->regmap)) { 1668c2ecf20Sopenharmony_ci ret = PTR_ERR(info->regmap); 1678c2ecf20Sopenharmony_ci dev_err(&client->dev, "Failed to allocate register map: %d\n", ret); 1688c2ecf20Sopenharmony_ci return ret; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci info->dev = &client->dev; 1728c2ecf20Sopenharmony_ci i2c_set_clientdata(client, info); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci info->mode = pdata->mode; 1758c2ecf20Sopenharmony_ci switch (info->mode) { 1768c2ecf20Sopenharmony_ci case 0: 1778c2ecf20Sopenharmony_ci dcdc_desc.vsel_reg = MAX8649_MODE0; 1788c2ecf20Sopenharmony_ci break; 1798c2ecf20Sopenharmony_ci case 1: 1808c2ecf20Sopenharmony_ci dcdc_desc.vsel_reg = MAX8649_MODE1; 1818c2ecf20Sopenharmony_ci break; 1828c2ecf20Sopenharmony_ci case 2: 1838c2ecf20Sopenharmony_ci dcdc_desc.vsel_reg = MAX8649_MODE2; 1848c2ecf20Sopenharmony_ci break; 1858c2ecf20Sopenharmony_ci case 3: 1868c2ecf20Sopenharmony_ci dcdc_desc.vsel_reg = MAX8649_MODE3; 1878c2ecf20Sopenharmony_ci break; 1888c2ecf20Sopenharmony_ci default: 1898c2ecf20Sopenharmony_ci break; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, MAX8649_CHIP_ID1, &val); 1938c2ecf20Sopenharmony_ci if (ret != 0) { 1948c2ecf20Sopenharmony_ci dev_err(info->dev, "Failed to detect ID of MAX8649:%d\n", 1958c2ecf20Sopenharmony_ci ret); 1968c2ecf20Sopenharmony_ci return ret; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci dev_info(info->dev, "Detected MAX8649 (ID:%x)\n", val); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* enable VID0 & VID1 */ 2018c2ecf20Sopenharmony_ci regmap_update_bits(info->regmap, MAX8649_CONTROL, MAX8649_VID_MASK, 0); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* enable/disable external clock synchronization */ 2048c2ecf20Sopenharmony_ci info->extclk = pdata->extclk; 2058c2ecf20Sopenharmony_ci data = (info->extclk) ? MAX8649_SYNC_EXTCLK : 0; 2068c2ecf20Sopenharmony_ci regmap_update_bits(info->regmap, dcdc_desc.vsel_reg, 2078c2ecf20Sopenharmony_ci MAX8649_SYNC_EXTCLK, data); 2088c2ecf20Sopenharmony_ci if (info->extclk) { 2098c2ecf20Sopenharmony_ci /* set external clock frequency */ 2108c2ecf20Sopenharmony_ci info->extclk_freq = pdata->extclk_freq; 2118c2ecf20Sopenharmony_ci regmap_update_bits(info->regmap, MAX8649_SYNC, MAX8649_EXT_MASK, 2128c2ecf20Sopenharmony_ci info->extclk_freq << 6); 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (pdata->ramp_timing) { 2168c2ecf20Sopenharmony_ci info->ramp_timing = pdata->ramp_timing; 2178c2ecf20Sopenharmony_ci regmap_update_bits(info->regmap, MAX8649_RAMP, MAX8649_RAMP_MASK, 2188c2ecf20Sopenharmony_ci info->ramp_timing << 5); 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci info->ramp_down = pdata->ramp_down; 2228c2ecf20Sopenharmony_ci if (info->ramp_down) { 2238c2ecf20Sopenharmony_ci regmap_update_bits(info->regmap, MAX8649_RAMP, MAX8649_RAMP_DOWN, 2248c2ecf20Sopenharmony_ci MAX8649_RAMP_DOWN); 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci config.dev = &client->dev; 2288c2ecf20Sopenharmony_ci config.init_data = pdata->regulator; 2298c2ecf20Sopenharmony_ci config.driver_data = info; 2308c2ecf20Sopenharmony_ci config.regmap = info->regmap; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci regulator = devm_regulator_register(&client->dev, &dcdc_desc, 2338c2ecf20Sopenharmony_ci &config); 2348c2ecf20Sopenharmony_ci if (IS_ERR(regulator)) { 2358c2ecf20Sopenharmony_ci dev_err(info->dev, "failed to register regulator %s\n", 2368c2ecf20Sopenharmony_ci dcdc_desc.name); 2378c2ecf20Sopenharmony_ci return PTR_ERR(regulator); 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return 0; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic const struct i2c_device_id max8649_id[] = { 2448c2ecf20Sopenharmony_ci { "max8649", 0 }, 2458c2ecf20Sopenharmony_ci { } 2468c2ecf20Sopenharmony_ci}; 2478c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, max8649_id); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic struct i2c_driver max8649_driver = { 2508c2ecf20Sopenharmony_ci .probe = max8649_regulator_probe, 2518c2ecf20Sopenharmony_ci .driver = { 2528c2ecf20Sopenharmony_ci .name = "max8649", 2538c2ecf20Sopenharmony_ci }, 2548c2ecf20Sopenharmony_ci .id_table = max8649_id, 2558c2ecf20Sopenharmony_ci}; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int __init max8649_init(void) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci return i2c_add_driver(&max8649_driver); 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_cisubsys_initcall(max8649_init); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic void __exit max8649_exit(void) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci i2c_del_driver(&max8649_driver); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_cimodule_exit(max8649_exit); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/* Module information */ 2708c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MAXIM 8649 voltage regulator driver"); 2718c2ecf20Sopenharmony_ciMODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); 2728c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 273