1 /* 2 * isl6271a-regulator.c 3 * 4 * Support for Intersil ISL6271A voltage regulator 5 * 6 * Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com> 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License as 10 * published by the Free Software Foundation version 2. 11 * 12 * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, 13 * whether express or implied; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * General Public License for more details. 16 */ 17 18 #include <linux/kernel.h> 19 #include <linux/module.h> 20 #include <linux/init.h> 21 #include <linux/err.h> 22 #include <linux/platform_device.h> 23 #include <linux/regulator/driver.h> 24 #include <linux/i2c.h> 25 #include <linux/delay.h> 26 #include <linux/slab.h> 27 28 #define ISL6271A_VOLTAGE_MIN 850000 29 #define ISL6271A_VOLTAGE_MAX 1600000 30 #define ISL6271A_VOLTAGE_STEP 50000 31 32 /* PMIC details */ 33 struct isl_pmic { 34 struct i2c_client *client; 35 struct regulator_dev *rdev[3]; 36 struct mutex mtx; 37 }; 38 39 static int isl6271a_get_voltage(struct regulator_dev *dev) 40 { 41 struct isl_pmic *pmic = rdev_get_drvdata(dev); 42 int idx, data; 43 44 mutex_lock(&pmic->mtx); 45 46 idx = i2c_smbus_read_byte(pmic->client); 47 if (idx < 0) { 48 dev_err(&pmic->client->dev, "Error getting voltage\n"); 49 data = idx; 50 goto out; 51 } 52 53 /* Convert the data from chip to microvolts */ 54 data = ISL6271A_VOLTAGE_MIN + (ISL6271A_VOLTAGE_STEP * (idx & 0xf)); 55 56 out: 57 mutex_unlock(&pmic->mtx); 58 return data; 59 } 60 61 static int isl6271a_set_voltage(struct regulator_dev *dev, 62 int minuV, int maxuV, 63 unsigned *selector) 64 { 65 struct isl_pmic *pmic = rdev_get_drvdata(dev); 66 int vsel, err, data; 67 68 if (minuV < ISL6271A_VOLTAGE_MIN || minuV > ISL6271A_VOLTAGE_MAX) 69 return -EINVAL; 70 if (maxuV < ISL6271A_VOLTAGE_MIN || maxuV > ISL6271A_VOLTAGE_MAX) 71 return -EINVAL; 72 73 /* Align to 50000 mV */ 74 vsel = minuV - (minuV % ISL6271A_VOLTAGE_STEP); 75 76 /* If the result fell out of [minuV,maxuV] range, put it back */ 77 if (vsel < minuV) 78 vsel += ISL6271A_VOLTAGE_STEP; 79 80 /* Convert the microvolts to data for the chip */ 81 data = (vsel - ISL6271A_VOLTAGE_MIN) / ISL6271A_VOLTAGE_STEP; 82 83 *selector = data; 84 85 mutex_lock(&pmic->mtx); 86 87 err = i2c_smbus_write_byte(pmic->client, data); 88 if (err < 0) 89 dev_err(&pmic->client->dev, "Error setting voltage\n"); 90 91 mutex_unlock(&pmic->mtx); 92 return err; 93 } 94 95 static int isl6271a_list_voltage(struct regulator_dev *dev, unsigned selector) 96 { 97 return ISL6271A_VOLTAGE_MIN + (ISL6271A_VOLTAGE_STEP * selector); 98 } 99 100 static struct regulator_ops isl_core_ops = { 101 .get_voltage = isl6271a_get_voltage, 102 .set_voltage = isl6271a_set_voltage, 103 .list_voltage = isl6271a_list_voltage, 104 }; 105 106 static int isl6271a_get_fixed_voltage(struct regulator_dev *dev) 107 { 108 int id = rdev_get_id(dev); 109 return (id == 1) ? 1100000 : 1300000; 110 } 111 112 static int isl6271a_list_fixed_voltage(struct regulator_dev *dev, unsigned selector) 113 { 114 int id = rdev_get_id(dev); 115 return (id == 1) ? 1100000 : 1300000; 116 } 117 118 static struct regulator_ops isl_fixed_ops = { 119 .get_voltage = isl6271a_get_fixed_voltage, 120 .list_voltage = isl6271a_list_fixed_voltage, 121 }; 122 123 static struct regulator_desc isl_rd[] = { 124 { 125 .name = "Core Buck", 126 .id = 0, 127 .n_voltages = 16, 128 .ops = &isl_core_ops, 129 .type = REGULATOR_VOLTAGE, 130 .owner = THIS_MODULE, 131 }, { 132 .name = "LDO1", 133 .id = 1, 134 .n_voltages = 1, 135 .ops = &isl_fixed_ops, 136 .type = REGULATOR_VOLTAGE, 137 .owner = THIS_MODULE, 138 }, { 139 .name = "LDO2", 140 .id = 2, 141 .n_voltages = 1, 142 .ops = &isl_fixed_ops, 143 .type = REGULATOR_VOLTAGE, 144 .owner = THIS_MODULE, 145 }, 146 }; 147 148 static int __devinit isl6271a_probe(struct i2c_client *i2c, 149 const struct i2c_device_id *id) 150 { 151 struct regulator_init_data *init_data = i2c->dev.platform_data; 152 struct isl_pmic *pmic; 153 int err, i; 154 155 if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 156 return -EIO; 157 158 if (!init_data) { 159 dev_err(&i2c->dev, "no platform data supplied\n"); 160 return -EIO; 161 } 162 163 pmic = kzalloc(sizeof(struct isl_pmic), GFP_KERNEL); 164 if (!pmic) 165 return -ENOMEM; 166 167 pmic->client = i2c; 168 169 mutex_init(&pmic->mtx); 170 171 for (i = 0; i < 3; i++) { 172 pmic->rdev[i] = regulator_register(&isl_rd[i], &i2c->dev, 173 init_data, pmic); 174 if (IS_ERR(pmic->rdev[i])) { 175 dev_err(&i2c->dev, "failed to register %s\n", id->name); 176 err = PTR_ERR(pmic->rdev[i]); 177 goto error; 178 } 179 } 180 181 i2c_set_clientdata(i2c, pmic); 182 183 return 0; 184 185 error: 186 while (--i >= 0) 187 regulator_unregister(pmic->rdev[i]); 188 189 kfree(pmic); 190 return err; 191 } 192 193 static int __devexit isl6271a_remove(struct i2c_client *i2c) 194 { 195 struct isl_pmic *pmic = i2c_get_clientdata(i2c); 196 int i; 197 198 for (i = 0; i < 3; i++) 199 regulator_unregister(pmic->rdev[i]); 200 201 kfree(pmic); 202 203 return 0; 204 } 205 206 static const struct i2c_device_id isl6271a_id[] = { 207 {.name = "isl6271a", 0 }, 208 { }, 209 }; 210 211 MODULE_DEVICE_TABLE(i2c, isl6271a_id); 212 213 static struct i2c_driver isl6271a_i2c_driver = { 214 .driver = { 215 .name = "isl6271a", 216 .owner = THIS_MODULE, 217 }, 218 .probe = isl6271a_probe, 219 .remove = __devexit_p(isl6271a_remove), 220 .id_table = isl6271a_id, 221 }; 222 223 static int __init isl6271a_init(void) 224 { 225 return i2c_add_driver(&isl6271a_i2c_driver); 226 } 227 228 static void __exit isl6271a_cleanup(void) 229 { 230 i2c_del_driver(&isl6271a_i2c_driver); 231 } 232 233 subsys_initcall(isl6271a_init); 234 module_exit(isl6271a_cleanup); 235 236 MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); 237 MODULE_DESCRIPTION("Intersil ISL6271A voltage regulator driver"); 238 MODULE_LICENSE("GPL v2"); 239