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, int minuV, int maxuV) 62 { 63 struct isl_pmic *pmic = rdev_get_drvdata(dev); 64 int vsel, err, data; 65 66 if (minuV < ISL6271A_VOLTAGE_MIN || minuV > ISL6271A_VOLTAGE_MAX) 67 return -EINVAL; 68 if (maxuV < ISL6271A_VOLTAGE_MIN || maxuV > ISL6271A_VOLTAGE_MAX) 69 return -EINVAL; 70 71 /* Align to 50000 mV */ 72 vsel = minuV - (minuV % ISL6271A_VOLTAGE_STEP); 73 74 /* If the result fell out of [minuV,maxuV] range, put it back */ 75 if (vsel < minuV) 76 vsel += ISL6271A_VOLTAGE_STEP; 77 78 /* Convert the microvolts to data for the chip */ 79 data = (vsel - ISL6271A_VOLTAGE_MIN) / ISL6271A_VOLTAGE_STEP; 80 81 mutex_lock(&pmic->mtx); 82 83 err = i2c_smbus_write_byte(pmic->client, data); 84 if (err < 0) 85 dev_err(&pmic->client->dev, "Error setting voltage\n"); 86 87 mutex_unlock(&pmic->mtx); 88 return err; 89 } 90 91 static int isl6271a_list_voltage(struct regulator_dev *dev, unsigned selector) 92 { 93 return ISL6271A_VOLTAGE_MIN + (ISL6271A_VOLTAGE_STEP * selector); 94 } 95 96 static struct regulator_ops isl_core_ops = { 97 .get_voltage = isl6271a_get_voltage, 98 .set_voltage = isl6271a_set_voltage, 99 .list_voltage = isl6271a_list_voltage, 100 }; 101 102 static int isl6271a_get_fixed_voltage(struct regulator_dev *dev) 103 { 104 int id = rdev_get_id(dev); 105 return (id == 1) ? 1100000 : 1300000; 106 } 107 108 static int isl6271a_list_fixed_voltage(struct regulator_dev *dev, unsigned selector) 109 { 110 int id = rdev_get_id(dev); 111 return (id == 1) ? 1100000 : 1300000; 112 } 113 114 static struct regulator_ops isl_fixed_ops = { 115 .get_voltage = isl6271a_get_fixed_voltage, 116 .list_voltage = isl6271a_list_fixed_voltage, 117 }; 118 119 static struct regulator_desc isl_rd[] = { 120 { 121 .name = "Core Buck", 122 .id = 0, 123 .n_voltages = 16, 124 .ops = &isl_core_ops, 125 .type = REGULATOR_VOLTAGE, 126 .owner = THIS_MODULE, 127 }, { 128 .name = "LDO1", 129 .id = 1, 130 .n_voltages = 1, 131 .ops = &isl_fixed_ops, 132 .type = REGULATOR_VOLTAGE, 133 .owner = THIS_MODULE, 134 }, { 135 .name = "LDO2", 136 .id = 2, 137 .n_voltages = 1, 138 .ops = &isl_fixed_ops, 139 .type = REGULATOR_VOLTAGE, 140 .owner = THIS_MODULE, 141 }, 142 }; 143 144 static int __devinit isl6271a_probe(struct i2c_client *i2c, 145 const struct i2c_device_id *id) 146 { 147 struct regulator_init_data *init_data = i2c->dev.platform_data; 148 struct isl_pmic *pmic; 149 int err, i; 150 151 if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 152 return -EIO; 153 154 if (!init_data) { 155 dev_err(&i2c->dev, "no platform data supplied\n"); 156 return -EIO; 157 } 158 159 pmic = kzalloc(sizeof(struct isl_pmic), GFP_KERNEL); 160 if (!pmic) 161 return -ENOMEM; 162 163 pmic->client = i2c; 164 165 mutex_init(&pmic->mtx); 166 167 for (i = 0; i < 3; i++) { 168 pmic->rdev[i] = regulator_register(&isl_rd[i], &i2c->dev, 169 init_data, pmic); 170 if (IS_ERR(pmic->rdev[i])) { 171 dev_err(&i2c->dev, "failed to register %s\n", id->name); 172 err = PTR_ERR(pmic->rdev); 173 goto error; 174 } 175 } 176 177 i2c_set_clientdata(i2c, pmic); 178 179 return 0; 180 181 error: 182 while (--i >= 0) 183 regulator_unregister(pmic->rdev[i]); 184 185 kfree(pmic); 186 return err; 187 } 188 189 static int __devexit isl6271a_remove(struct i2c_client *i2c) 190 { 191 struct isl_pmic *pmic = i2c_get_clientdata(i2c); 192 int i; 193 194 for (i = 0; i < 3; i++) 195 regulator_unregister(pmic->rdev[i]); 196 197 kfree(pmic); 198 199 return 0; 200 } 201 202 static const struct i2c_device_id isl6271a_id[] = { 203 {.name = "isl6271a", 0 }, 204 { }, 205 }; 206 207 MODULE_DEVICE_TABLE(i2c, isl6271a_id); 208 209 static struct i2c_driver isl6271a_i2c_driver = { 210 .driver = { 211 .name = "isl6271a", 212 .owner = THIS_MODULE, 213 }, 214 .probe = isl6271a_probe, 215 .remove = __devexit_p(isl6271a_remove), 216 .id_table = isl6271a_id, 217 }; 218 219 static int __init isl6271a_init(void) 220 { 221 return i2c_add_driver(&isl6271a_i2c_driver); 222 } 223 224 static void __exit isl6271a_cleanup(void) 225 { 226 i2c_del_driver(&isl6271a_i2c_driver); 227 } 228 229 subsys_initcall(isl6271a_init); 230 module_exit(isl6271a_cleanup); 231 232 MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); 233 MODULE_DESCRIPTION("Intersil ISL6271A voltage regulator driver"); 234 MODULE_LICENSE("GPL v2"); 235