···7070 help7171 This driver supports 88PM8607 voltage regulator chips.72727373+config REGULATOR_ACT88657474+ tristate "Active-semi act8865 voltage regulator"7575+ depends on I2C7676+ select REGMAP_I2C7777+ help7878+ This driver controls a active-semi act8865 voltage output7979+ regulator via I2C bus.8080+7381config REGULATOR_AD53987482 tristate "Analog Devices AD5398/AD5821 regulators"7583 depends on I2C
···11+/*22+ * act8865-regulator.c - Voltage regulation for the active-semi ACT886533+ * http://www.active-semi.com/sheets/ACT8865_Datasheet.pdf44+ *55+ * Copyright (C) 2013 Atmel Corporation66+ *77+ * This program is free software; you can redistribute it and/or modify88+ * it under the terms of the GNU General Public License as published by99+ * the Free Software Foundation; either version 2 of the License, or1010+ * (at your option) any later version.1111+ *1212+ * This program is distributed in the hope that it will be useful,1313+ * but WITHOUT ANY WARRANTY; without even the implied warranty of1414+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the1515+ * GNU General Public License for more details.1616+ */1717+1818+#include <linux/module.h>1919+#include <linux/init.h>2020+#include <linux/i2c.h>2121+#include <linux/err.h>2222+#include <linux/platform_device.h>2323+#include <linux/regulator/driver.h>2424+#include <linux/regulator/act8865.h>2525+#include <linux/of.h>2626+#include <linux/of_device.h>2727+#include <linux/regulator/of_regulator.h>2828+#include <linux/regmap.h>2929+3030+/*3131+ * ACT8865 Global Register Map.3232+ */3333+#define ACT8865_SYS_MODE 0x003434+#define ACT8865_SYS_CTRL 0x013535+#define ACT8865_DCDC1_VSET1 0x203636+#define ACT8865_DCDC1_VSET2 0x213737+#define ACT8865_DCDC1_CTRL 0x223838+#define ACT8865_DCDC2_VSET1 0x303939+#define ACT8865_DCDC2_VSET2 0x314040+#define ACT8865_DCDC2_CTRL 0x324141+#define ACT8865_DCDC3_VSET1 0x404242+#define ACT8865_DCDC3_VSET2 0x414343+#define ACT8865_DCDC3_CTRL 0x424444+#define ACT8865_LDO1_VSET 0x504545+#define ACT8865_LDO1_CTRL 0x514646+#define ACT8865_LDO2_VSET 0x544747+#define ACT8865_LDO2_CTRL 0x554848+#define ACT8865_LDO3_VSET 0x604949+#define ACT8865_LDO3_CTRL 0x615050+#define ACT8865_LDO4_VSET 0x645151+#define ACT8865_LDO4_CTRL 0x655252+5353+/*5454+ * Field Definitions.5555+ */5656+#define ACT8865_ENA 0x80 /* ON - [7] */5757+#define ACT8865_VSEL_MASK 0x3F /* VSET - [5:0] */5858+5959+/*6060+ * ACT8865 voltage number6161+ */6262+#define ACT8865_VOLTAGE_NUM 646363+6464+struct act8865 {6565+ struct regulator_dev *rdev[ACT8865_REG_NUM];6666+ struct regmap *regmap;6767+};6868+6969+static const struct regmap_config act8865_regmap_config = {7070+ .reg_bits = 8,7171+ .val_bits = 8,7272+};7373+7474+static const struct regulator_linear_range act8865_volatge_ranges[] = {7575+ REGULATOR_LINEAR_RANGE(600000, 0, 23, 25000),7676+ REGULATOR_LINEAR_RANGE(1200000, 24, 47, 50000),7777+ REGULATOR_LINEAR_RANGE(2400000, 48, 63, 100000),7878+};7979+8080+static struct regulator_ops act8865_ops = {8181+ .list_voltage = regulator_list_voltage_linear_range,8282+ .map_voltage = regulator_map_voltage_linear_range,8383+ .get_voltage_sel = regulator_get_voltage_sel_regmap,8484+ .set_voltage_sel = regulator_set_voltage_sel_regmap,8585+ .enable = regulator_enable_regmap,8686+ .disable = regulator_disable_regmap,8787+ .is_enabled = regulator_is_enabled_regmap,8888+ .set_suspend_enable = regulator_enable_regmap,8989+ .set_suspend_disable = regulator_disable_regmap,9090+};9191+9292+static const struct regulator_desc act8865_reg[] = {9393+ {9494+ .name = "DCDC_REG1",9595+ .id = ACT8865_ID_DCDC1,9696+ .ops = &act8865_ops,9797+ .type = REGULATOR_VOLTAGE,9898+ .n_voltages = ACT8865_VOLTAGE_NUM,9999+ .linear_ranges = act8865_volatge_ranges,100100+ .n_linear_ranges = ARRAY_SIZE(act8865_volatge_ranges),101101+ .vsel_reg = ACT8865_DCDC1_VSET1,102102+ .vsel_mask = ACT8865_VSEL_MASK,103103+ .enable_reg = ACT8865_DCDC1_CTRL,104104+ .enable_mask = ACT8865_ENA,105105+ .owner = THIS_MODULE,106106+ },107107+ {108108+ .name = "DCDC_REG2",109109+ .id = ACT8865_ID_DCDC2,110110+ .ops = &act8865_ops,111111+ .type = REGULATOR_VOLTAGE,112112+ .n_voltages = ACT8865_VOLTAGE_NUM,113113+ .linear_ranges = act8865_volatge_ranges,114114+ .n_linear_ranges = ARRAY_SIZE(act8865_volatge_ranges),115115+ .vsel_reg = ACT8865_DCDC2_VSET1,116116+ .vsel_mask = ACT8865_VSEL_MASK,117117+ .enable_reg = ACT8865_DCDC2_CTRL,118118+ .enable_mask = ACT8865_ENA,119119+ .owner = THIS_MODULE,120120+ },121121+ {122122+ .name = "DCDC_REG3",123123+ .id = ACT8865_ID_DCDC3,124124+ .ops = &act8865_ops,125125+ .type = REGULATOR_VOLTAGE,126126+ .n_voltages = ACT8865_VOLTAGE_NUM,127127+ .linear_ranges = act8865_volatge_ranges,128128+ .n_linear_ranges = ARRAY_SIZE(act8865_volatge_ranges),129129+ .vsel_reg = ACT8865_DCDC3_VSET1,130130+ .vsel_mask = ACT8865_VSEL_MASK,131131+ .enable_reg = ACT8865_DCDC3_CTRL,132132+ .enable_mask = ACT8865_ENA,133133+ .owner = THIS_MODULE,134134+ },135135+ {136136+ .name = "LDO_REG1",137137+ .id = ACT8865_ID_LDO1,138138+ .ops = &act8865_ops,139139+ .type = REGULATOR_VOLTAGE,140140+ .n_voltages = ACT8865_VOLTAGE_NUM,141141+ .linear_ranges = act8865_volatge_ranges,142142+ .n_linear_ranges = ARRAY_SIZE(act8865_volatge_ranges),143143+ .vsel_reg = ACT8865_LDO1_VSET,144144+ .vsel_mask = ACT8865_VSEL_MASK,145145+ .enable_reg = ACT8865_LDO1_CTRL,146146+ .enable_mask = ACT8865_ENA,147147+ .owner = THIS_MODULE,148148+ },149149+ {150150+ .name = "LDO_REG2",151151+ .id = ACT8865_ID_LDO2,152152+ .ops = &act8865_ops,153153+ .type = REGULATOR_VOLTAGE,154154+ .n_voltages = ACT8865_VOLTAGE_NUM,155155+ .linear_ranges = act8865_volatge_ranges,156156+ .n_linear_ranges = ARRAY_SIZE(act8865_volatge_ranges),157157+ .vsel_reg = ACT8865_LDO2_VSET,158158+ .vsel_mask = ACT8865_VSEL_MASK,159159+ .enable_reg = ACT8865_LDO2_CTRL,160160+ .enable_mask = ACT8865_ENA,161161+ .owner = THIS_MODULE,162162+ },163163+ {164164+ .name = "LDO_REG3",165165+ .id = ACT8865_ID_LDO3,166166+ .ops = &act8865_ops,167167+ .type = REGULATOR_VOLTAGE,168168+ .n_voltages = ACT8865_VOLTAGE_NUM,169169+ .linear_ranges = act8865_volatge_ranges,170170+ .n_linear_ranges = ARRAY_SIZE(act8865_volatge_ranges),171171+ .vsel_reg = ACT8865_LDO3_VSET,172172+ .vsel_mask = ACT8865_VSEL_MASK,173173+ .enable_reg = ACT8865_LDO3_CTRL,174174+ .enable_mask = ACT8865_ENA,175175+ .owner = THIS_MODULE,176176+ },177177+ {178178+ .name = "LDO_REG4",179179+ .id = ACT8865_ID_LDO4,180180+ .ops = &act8865_ops,181181+ .type = REGULATOR_VOLTAGE,182182+ .n_voltages = ACT8865_VOLTAGE_NUM,183183+ .linear_ranges = act8865_volatge_ranges,184184+ .n_linear_ranges = ARRAY_SIZE(act8865_volatge_ranges),185185+ .vsel_reg = ACT8865_LDO4_VSET,186186+ .vsel_mask = ACT8865_VSEL_MASK,187187+ .enable_reg = ACT8865_LDO4_CTRL,188188+ .enable_mask = ACT8865_ENA,189189+ .owner = THIS_MODULE,190190+ },191191+};192192+193193+#ifdef CONFIG_OF194194+static const struct of_device_id act8865_dt_ids[] = {195195+ { .compatible = "active-semi,act8865" },196196+ { }197197+};198198+MODULE_DEVICE_TABLE(of, act8865_dt_ids);199199+200200+static struct of_regulator_match act8865_matches[] = {201201+ [ACT8865_ID_DCDC1] = { .name = "DCDC_REG1"},202202+ [ACT8865_ID_DCDC2] = { .name = "DCDC_REG2"},203203+ [ACT8865_ID_DCDC3] = { .name = "DCDC_REG3"},204204+ [ACT8865_ID_LDO1] = { .name = "LDO_REG1"},205205+ [ACT8865_ID_LDO2] = { .name = "LDO_REG2"},206206+ [ACT8865_ID_LDO3] = { .name = "LDO_REG3"},207207+ [ACT8865_ID_LDO4] = { .name = "LDO_REG4"},208208+};209209+210210+static int act8865_pdata_from_dt(struct device *dev,211211+ struct device_node **of_node,212212+ struct act8865_platform_data *pdata)213213+{214214+ int matched, i;215215+ struct device_node *np;216216+ struct act8865_regulator_data *regulator;217217+218218+ np = of_find_node_by_name(dev->of_node, "regulators");219219+ if (!np) {220220+ dev_err(dev, "missing 'regulators' subnode in DT\n");221221+ return -EINVAL;222222+ }223223+224224+ matched = of_regulator_match(dev, np,225225+ act8865_matches, ARRAY_SIZE(act8865_matches));226226+ if (matched <= 0)227227+ return matched;228228+229229+ pdata->regulators = devm_kzalloc(dev,230230+ sizeof(struct act8865_regulator_data) * matched,231231+ GFP_KERNEL);232232+ if (!pdata->regulators) {233233+ dev_err(dev, "%s: failed to allocate act8865 registor\n",234234+ __func__);235235+ return -ENOMEM;236236+ }237237+238238+ pdata->num_regulators = matched;239239+ regulator = pdata->regulators;240240+241241+ for (i = 0; i < matched; i++) {242242+ if (!act8865_matches[i].init_data)243243+ continue;244244+245245+ regulator->id = i;246246+ regulator->name = act8865_matches[i].name;247247+ regulator->platform_data = act8865_matches[i].init_data;248248+ of_node[i] = act8865_matches[i].of_node;249249+ regulator++;250250+ }251251+252252+ return 0;253253+}254254+#else255255+static inline int act8865_pdata_from_dt(struct device *dev,256256+ struct device_node **of_node,257257+ struct act8865_platform_data *pdata)258258+{259259+ return 0;260260+}261261+#endif262262+263263+static int act8865_pmic_probe(struct i2c_client *client,264264+ const struct i2c_device_id *i2c_id)265265+{266266+ struct regulator_dev **rdev;267267+ struct device *dev = &client->dev;268268+ struct act8865_platform_data *pdata = dev_get_platdata(dev);269269+ struct regulator_config config = { };270270+ struct act8865 *act8865;271271+ struct device_node *of_node[ACT8865_REG_NUM];272272+ int i, id;273273+ int ret = -EINVAL;274274+ int error;275275+276276+ if (dev->of_node && !pdata) {277277+ const struct of_device_id *id;278278+ struct act8865_platform_data pdata_of;279279+280280+ id = of_match_device(of_match_ptr(act8865_dt_ids), dev);281281+ if (!id)282282+ return -ENODEV;283283+284284+ ret = act8865_pdata_from_dt(dev, of_node, &pdata_of);285285+ if (ret < 0)286286+ return ret;287287+288288+ pdata = &pdata_of;289289+ }290290+291291+ if (pdata->num_regulators > ACT8865_REG_NUM) {292292+ dev_err(dev, "Too many regulators found!\n");293293+ return -EINVAL;294294+ }295295+296296+ act8865 = devm_kzalloc(dev, sizeof(struct act8865) +297297+ sizeof(struct regulator_dev *) * ACT8865_REG_NUM,298298+ GFP_KERNEL);299299+ if (!act8865)300300+ return -ENOMEM;301301+302302+ rdev = act8865->rdev;303303+304304+ act8865->regmap = devm_regmap_init_i2c(client, &act8865_regmap_config);305305+ if (IS_ERR(act8865->regmap)) {306306+ error = PTR_ERR(act8865->regmap);307307+ dev_err(&client->dev, "Failed to allocate register map: %d\n",308308+ error);309309+ return error;310310+ }311311+312312+ /* Finally register devices */313313+ for (i = 0; i < pdata->num_regulators; i++) {314314+315315+ id = pdata->regulators[i].id;316316+317317+ config.dev = dev;318318+ config.init_data = pdata->regulators[i].platform_data;319319+ config.of_node = of_node[i];320320+ config.driver_data = act8865;321321+ config.regmap = act8865->regmap;322322+323323+ rdev[i] = devm_regulator_register(&client->dev,324324+ &act8865_reg[i], &config);325325+ if (IS_ERR(rdev[i])) {326326+ dev_err(dev, "failed to register %s\n",327327+ act8865_reg[id].name);328328+ return PTR_ERR(rdev[i]);329329+ }330330+ }331331+332332+ i2c_set_clientdata(client, act8865);333333+334334+ return 0;335335+}336336+337337+static int act8865_pmic_remove(struct i2c_client *client)338338+{339339+ struct act8865 *act8865 = i2c_get_clientdata(client);340340+ int i;341341+342342+ for (i = 0; i < ACT8865_REG_NUM; i++)343343+ regulator_unregister(act8865->rdev[i]);344344+345345+ return 0;346346+}347347+348348+static const struct i2c_device_id act8865_ids[] = {349349+ { "act8865", 0 },350350+ { },351351+};352352+MODULE_DEVICE_TABLE(i2c, act8865_ids);353353+354354+static struct i2c_driver act8865_pmic_driver = {355355+ .driver = {356356+ .name = "act8865",357357+ .owner = THIS_MODULE,358358+ },359359+ .probe = act8865_pmic_probe,360360+ .remove = act8865_pmic_remove,361361+ .id_table = act8865_ids,362362+};363363+364364+module_i2c_driver(act8865_pmic_driver);365365+366366+MODULE_DESCRIPTION("active-semi act8865 voltage regulator driver");367367+MODULE_AUTHOR("Wenyou Yang <wenyou.yang@atmel.com>");368368+MODULE_LICENSE("GPL v2");
+53
include/linux/regulator/act8865.h
···11+/*22+ * act8865.h -- Voltage regulation for the active-semi act886533+ *44+ * Copyright (C) 2013 Atmel Corporation.55+ *66+ * This program is free software; you can redistribute it and/or modify77+ * it under the terms of the GNU General Public License as published by88+ * the Free Software Foundation; version 2 of the License.99+ *1010+ * This program is distributed in the hope that it will be useful,1111+ * but WITHOUT ANY WARRANTY; without even the implied warranty of1212+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the1313+ * GNU General Public License for more details.1414+ */1515+1616+#ifndef __LINUX_REGULATOR_ACT8865_H1717+#define __LINUX_REGULATOR_ACT8865_H1818+1919+#include <linux/regulator/machine.h>2020+2121+enum {2222+ ACT8865_ID_DCDC1,2323+ ACT8865_ID_DCDC2,2424+ ACT8865_ID_DCDC3,2525+ ACT8865_ID_LDO1,2626+ ACT8865_ID_LDO2,2727+ ACT8865_ID_LDO3,2828+ ACT8865_ID_LDO4,2929+ ACT8865_REG_NUM,3030+};3131+3232+/**3333+ * act8865_regulator_data - regulator data3434+ * @id: regulator id3535+ * @name: regulator name3636+ * @platform_data: regulator init data3737+ */3838+struct act8865_regulator_data {3939+ int id;4040+ const char *name;4141+ struct regulator_init_data *platform_data;4242+};4343+4444+/**4545+ * act8865_platform_data - platform data for act88654646+ * @num_regulators: number of regulators used4747+ * @regulators: pointer to regulators used4848+ */4949+struct act8865_platform_data {5050+ int num_regulators;5151+ struct act8865_regulator_data *regulators;5252+};5353+#endif