287 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Regulators driver for Maxim max89254 *5 * Copyright (C) 2009 Marvell International Ltd.6 * Haojian Zhuang <haojian.zhuang@marvell.com>7 */8#include <linux/kernel.h>9#include <linux/module.h>10#include <linux/init.h>11#include <linux/err.h>12#include <linux/i2c.h>13#include <linux/platform_device.h>14#include <linux/regulator/driver.h>15#include <linux/regulator/machine.h>16#include <linux/mfd/max8925.h>17#include <linux/of.h>18#include <linux/regulator/of_regulator.h>19 20#define SD1_DVM_VMIN 85000021#define SD1_DVM_VMAX 100000022#define SD1_DVM_STEP 5000023#define SD1_DVM_SHIFT 5 /* SDCTL1 bit5 */24#define SD1_DVM_EN 6 /* SDV1 bit 6 */25 26/* bit definitions in LDO control registers */27#define LDO_SEQ_I2C 0x7 /* Power U/D by i2c */28#define LDO_SEQ_MASK 0x7 /* Power U/D sequence mask */29#define LDO_SEQ_SHIFT 2 /* Power U/D sequence offset */30#define LDO_I2C_EN 0x1 /* Enable by i2c */31#define LDO_I2C_EN_MASK 0x1 /* Enable mask by i2c */32#define LDO_I2C_EN_SHIFT 0 /* Enable offset by i2c */33 34struct max8925_regulator_info {35 struct regulator_desc desc;36 struct i2c_client *i2c;37 38 int vol_reg;39 int enable_reg;40};41 42static int max8925_set_voltage_sel(struct regulator_dev *rdev,43 unsigned int selector)44{45 struct max8925_regulator_info *info = rdev_get_drvdata(rdev);46 unsigned char mask = rdev->desc->n_voltages - 1;47 48 return max8925_set_bits(info->i2c, info->vol_reg, mask, selector);49}50 51static int max8925_get_voltage_sel(struct regulator_dev *rdev)52{53 struct max8925_regulator_info *info = rdev_get_drvdata(rdev);54 unsigned char data, mask;55 int ret;56 57 ret = max8925_reg_read(info->i2c, info->vol_reg);58 if (ret < 0)59 return ret;60 mask = rdev->desc->n_voltages - 1;61 data = ret & mask;62 63 return data;64}65 66static int max8925_enable(struct regulator_dev *rdev)67{68 struct max8925_regulator_info *info = rdev_get_drvdata(rdev);69 70 return max8925_set_bits(info->i2c, info->enable_reg,71 LDO_SEQ_MASK << LDO_SEQ_SHIFT |72 LDO_I2C_EN_MASK << LDO_I2C_EN_SHIFT,73 LDO_SEQ_I2C << LDO_SEQ_SHIFT |74 LDO_I2C_EN << LDO_I2C_EN_SHIFT);75}76 77static int max8925_disable(struct regulator_dev *rdev)78{79 struct max8925_regulator_info *info = rdev_get_drvdata(rdev);80 81 return max8925_set_bits(info->i2c, info->enable_reg,82 LDO_SEQ_MASK << LDO_SEQ_SHIFT |83 LDO_I2C_EN_MASK << LDO_I2C_EN_SHIFT,84 LDO_SEQ_I2C << LDO_SEQ_SHIFT);85}86 87static int max8925_is_enabled(struct regulator_dev *rdev)88{89 struct max8925_regulator_info *info = rdev_get_drvdata(rdev);90 int ldo_seq, ret;91 92 ret = max8925_reg_read(info->i2c, info->enable_reg);93 if (ret < 0)94 return ret;95 ldo_seq = (ret >> LDO_SEQ_SHIFT) & LDO_SEQ_MASK;96 if (ldo_seq != LDO_SEQ_I2C)97 return 1;98 else99 return ret & (LDO_I2C_EN_MASK << LDO_I2C_EN_SHIFT);100}101 102static int max8925_set_dvm_voltage(struct regulator_dev *rdev, int uV)103{104 struct max8925_regulator_info *info = rdev_get_drvdata(rdev);105 unsigned char data, mask;106 107 if (uV < SD1_DVM_VMIN || uV > SD1_DVM_VMAX)108 return -EINVAL;109 110 data = DIV_ROUND_UP(uV - SD1_DVM_VMIN, SD1_DVM_STEP);111 data <<= SD1_DVM_SHIFT;112 mask = 3 << SD1_DVM_SHIFT;113 114 return max8925_set_bits(info->i2c, info->enable_reg, mask, data);115}116 117static int max8925_set_dvm_enable(struct regulator_dev *rdev)118{119 struct max8925_regulator_info *info = rdev_get_drvdata(rdev);120 121 return max8925_set_bits(info->i2c, info->vol_reg, 1 << SD1_DVM_EN,122 1 << SD1_DVM_EN);123}124 125static int max8925_set_dvm_disable(struct regulator_dev *rdev)126{127 struct max8925_regulator_info *info = rdev_get_drvdata(rdev);128 129 return max8925_set_bits(info->i2c, info->vol_reg, 1 << SD1_DVM_EN, 0);130}131 132static const struct regulator_ops max8925_regulator_sdv_ops = {133 .map_voltage = regulator_map_voltage_linear,134 .list_voltage = regulator_list_voltage_linear,135 .set_voltage_sel = max8925_set_voltage_sel,136 .get_voltage_sel = max8925_get_voltage_sel,137 .enable = max8925_enable,138 .disable = max8925_disable,139 .is_enabled = max8925_is_enabled,140 .set_suspend_voltage = max8925_set_dvm_voltage,141 .set_suspend_enable = max8925_set_dvm_enable,142 .set_suspend_disable = max8925_set_dvm_disable,143};144 145static const struct regulator_ops max8925_regulator_ldo_ops = {146 .map_voltage = regulator_map_voltage_linear,147 .list_voltage = regulator_list_voltage_linear,148 .set_voltage_sel = max8925_set_voltage_sel,149 .get_voltage_sel = max8925_get_voltage_sel,150 .enable = max8925_enable,151 .disable = max8925_disable,152 .is_enabled = max8925_is_enabled,153};154 155#define MAX8925_SDV(_id, min, max, step) \156{ \157 .desc = { \158 .name = "SDV" #_id, \159 .of_match = of_match_ptr("SDV" #_id), \160 .regulators_node = of_match_ptr("regulators"), \161 .ops = &max8925_regulator_sdv_ops, \162 .type = REGULATOR_VOLTAGE, \163 .id = MAX8925_ID_SD##_id, \164 .owner = THIS_MODULE, \165 .n_voltages = 64, \166 .min_uV = min * 1000, \167 .uV_step = step * 1000, \168 }, \169 .vol_reg = MAX8925_SDV##_id, \170 .enable_reg = MAX8925_SDCTL##_id, \171}172 173#define MAX8925_LDO(_id, min, max, step) \174{ \175 .desc = { \176 .name = "LDO" #_id, \177 .of_match = of_match_ptr("LDO" #_id), \178 .regulators_node = of_match_ptr("regulators"), \179 .ops = &max8925_regulator_ldo_ops, \180 .type = REGULATOR_VOLTAGE, \181 .id = MAX8925_ID_LDO##_id, \182 .owner = THIS_MODULE, \183 .n_voltages = 64, \184 .min_uV = min * 1000, \185 .uV_step = step * 1000, \186 }, \187 .vol_reg = MAX8925_LDOVOUT##_id, \188 .enable_reg = MAX8925_LDOCTL##_id, \189}190 191static struct max8925_regulator_info max8925_regulator_info[] = {192 MAX8925_SDV(1, 637.5, 1425, 12.5),193 MAX8925_SDV(2, 650, 2225, 25),194 MAX8925_SDV(3, 750, 3900, 50),195 196 MAX8925_LDO(1, 750, 3900, 50),197 MAX8925_LDO(2, 650, 2250, 25),198 MAX8925_LDO(3, 650, 2250, 25),199 MAX8925_LDO(4, 750, 3900, 50),200 MAX8925_LDO(5, 750, 3900, 50),201 MAX8925_LDO(6, 750, 3900, 50),202 MAX8925_LDO(7, 750, 3900, 50),203 MAX8925_LDO(8, 750, 3900, 50),204 MAX8925_LDO(9, 750, 3900, 50),205 MAX8925_LDO(10, 750, 3900, 50),206 MAX8925_LDO(11, 750, 3900, 50),207 MAX8925_LDO(12, 750, 3900, 50),208 MAX8925_LDO(13, 750, 3900, 50),209 MAX8925_LDO(14, 750, 3900, 50),210 MAX8925_LDO(15, 750, 3900, 50),211 MAX8925_LDO(16, 750, 3900, 50),212 MAX8925_LDO(17, 650, 2250, 25),213 MAX8925_LDO(18, 650, 2250, 25),214 MAX8925_LDO(19, 750, 3900, 50),215 MAX8925_LDO(20, 750, 3900, 50),216};217 218static int max8925_regulator_probe(struct platform_device *pdev)219{220 struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);221 struct regulator_init_data *pdata = dev_get_platdata(&pdev->dev);222 struct regulator_config config = { };223 struct max8925_regulator_info *ri;224 struct resource *res;225 struct regulator_dev *rdev;226 int i;227 228 res = platform_get_resource(pdev, IORESOURCE_REG, 0);229 if (!res) {230 dev_err(&pdev->dev, "No REG resource!\n");231 return -EINVAL;232 }233 for (i = 0; i < ARRAY_SIZE(max8925_regulator_info); i++) {234 ri = &max8925_regulator_info[i];235 if (ri->vol_reg == res->start)236 break;237 }238 239 if (i == ARRAY_SIZE(max8925_regulator_info)) {240 dev_err(&pdev->dev, "Failed to find regulator %llu\n",241 (unsigned long long)res->start);242 return -EINVAL;243 }244 ri->i2c = chip->i2c;245 246 config.dev = chip->dev;247 config.driver_data = ri;248 249 if (pdata)250 config.init_data = pdata;251 252 rdev = devm_regulator_register(&pdev->dev, &ri->desc, &config);253 if (IS_ERR(rdev)) {254 dev_err(&pdev->dev, "failed to register regulator %s\n",255 ri->desc.name);256 return PTR_ERR(rdev);257 }258 259 platform_set_drvdata(pdev, rdev);260 return 0;261}262 263static struct platform_driver max8925_regulator_driver = {264 .driver = {265 .name = "max8925-regulator",266 .probe_type = PROBE_PREFER_ASYNCHRONOUS,267 },268 .probe = max8925_regulator_probe,269};270 271static int __init max8925_regulator_init(void)272{273 return platform_driver_register(&max8925_regulator_driver);274}275subsys_initcall(max8925_regulator_init);276 277static void __exit max8925_regulator_exit(void)278{279 platform_driver_unregister(&max8925_regulator_driver);280}281module_exit(max8925_regulator_exit);282 283MODULE_LICENSE("GPL");284MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");285MODULE_DESCRIPTION("Regulator Driver for Maxim 8925 PMIC");286MODULE_ALIAS("platform:max8925-regulator");287