| 1 | |
| 2 | |
| 3 | * max8660.c -- Voltage regulation for the Maxim 8660/8661
|
| 4 | *
|
| 5 | * based on max1586.c and wm8400-regulator.c
|
| 6 | *
|
| 7 | * Copyright (C) 2009 Wolfram Sang, Pengutronix e.K.
|
| 8 | *
|
| 9 | * Some info:
|
| 10 | *
|
| 11 | * Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX8660-MAX8661.pdf
|
| 12 | *
|
| 13 | * This chip is a bit nasty because it is a write-only device. Thus, the driver
|
| 14 | * uses shadow registers to keep track of its values. The main problem appears
|
| 15 | * to be the initialization: When Linux boots up, we cannot know if the chip is
|
| 16 | * in the default state or not, so we would have to pass such information in
|
| 17 | * platform_data. As this adds a bit of complexity to the driver, this is left
|
| 18 | * out for now until it is really needed.
|
| 19 | *
|
| 20 | * [A|S|M]DTV1 registers are currently not used, but [A|S|M]DTV2.
|
| 21 | *
|
| 22 | * If the driver is feature complete, it might be worth to check if one set of
|
| 23 | * functions for V3-V7 is sufficient. For maximum flexibility during
|
| 24 | * development, they are separated for now.
|
| 25 |
|
| 26 |
|
| 27 | #include <linux/module.h>
|
| 28 | #include <linux/err.h>
|
| 29 | #include <linux/i2c.h>
|
| 30 | #include <linux/platform_device.h>
|
| 31 | #include <linux/regulator/driver.h>
|
| 32 | #include <linux/slab.h>
|
| 33 | #include <linux/regulator/max8660.h>
|
| 34 | #include <linux/of.h>
|
| 35 | #include <linux/of_device.h>
|
| 36 | #include <linux/regulator/of_regulator.h>
|
| 37 |
|
| 38 | #define MAX8660_DCDC_MIN_UV 725000
|
| 39 | #define MAX8660_DCDC_MAX_UV 1800000
|
| 40 | #define MAX8660_DCDC_STEP 25000
|
| 41 | #define MAX8660_DCDC_MAX_SEL 0x2b
|
| 42 |
|
| 43 | #define MAX8660_LDO5_MIN_UV 1700000
|
| 44 | #define MAX8660_LDO5_MAX_UV 2000000
|
| 45 | #define MAX8660_LDO5_STEP 25000
|
| 46 | #define MAX8660_LDO5_MAX_SEL 0x0c
|
| 47 |
|
| 48 | #define MAX8660_LDO67_MIN_UV 1800000
|
| 49 | #define MAX8660_LDO67_MAX_UV 3300000
|
| 50 | #define MAX8660_LDO67_STEP 100000
|
| 51 | #define MAX8660_LDO67_MAX_SEL 0x0f
|
| 52 |
|
| 53 | enum {
|
| 54 | MAX8660_OVER1,
|
| 55 | MAX8660_OVER2,
|
| 56 | MAX8660_VCC1,
|
| 57 | MAX8660_ADTV1,
|
| 58 | MAX8660_ADTV2,
|
| 59 | MAX8660_SDTV1,
|
| 60 | MAX8660_SDTV2,
|
| 61 | MAX8660_MDTV1,
|
| 62 | MAX8660_MDTV2,
|
| 63 | MAX8660_L12VCR,
|
| 64 | MAX8660_FPWM,
|
| 65 | MAX8660_N_REGS,
|
| 66 | };
|
| 67 |
|
| 68 | struct max8660 {
|
| 69 | struct i2c_client *client;
|
| 70 | u8 shadow_regs[MAX8660_N_REGS];
|
| 71 | };
|
| 72 |
|
| 73 | static int max8660_write(struct max8660 *max8660, u8 reg, u8 mask, u8 val)
|
| 74 | {
|
| 75 | static const u8 max8660_addresses[MAX8660_N_REGS] = {
|
| 76 | 0x10, 0x12, 0x20, 0x23, 0x24, 0x29, 0x2a, 0x32, 0x33, 0x39, 0x80
|
| 77 | };
|
| 78 |
|
| 79 | int ret;
|
| 80 | u8 reg_val = (max8660->shadow_regs[reg] & mask) | val;
|
| 81 |
|
| 82 | dev_vdbg(&max8660->client->dev, "Writing reg %02x with %02x\n",
|
| 83 | max8660_addresses[reg], reg_val);
|
| 84 |
|
| 85 | ret = i2c_smbus_write_byte_data(max8660->client,
|
| 86 | max8660_addresses[reg], reg_val);
|
| 87 | if (ret == 0)
|
| 88 | max8660->shadow_regs[reg] = reg_val;
|
| 89 |
|
| 90 | return ret;
|
| 91 | }
|
| 92 |
|
| 93 |
|
| 94 | |
| 95 | * DCDC functions
|
| 96 |
|
| 97 |
|
| 98 | static int max8660_dcdc_is_enabled(struct regulator_dev *rdev)
|
| 99 | {
|
| 100 | struct max8660 *max8660 = rdev_get_drvdata(rdev);
|
| 101 | u8 val = max8660->shadow_regs[MAX8660_OVER1];
|
| 102 | u8 mask = (rdev_get_id(rdev) == MAX8660_V3) ? 1 : 4;
|
| 103 |
|
| 104 | return !!(val & mask);
|
| 105 | }
|
| 106 |
|
| 107 | static int max8660_dcdc_enable(struct regulator_dev *rdev)
|
| 108 | {
|
| 109 | struct max8660 *max8660 = rdev_get_drvdata(rdev);
|
| 110 | u8 bit = (rdev_get_id(rdev) == MAX8660_V3) ? 1 : 4;
|
| 111 |
|
| 112 | return max8660_write(max8660, MAX8660_OVER1, 0xff, bit);
|
| 113 | }
|
| 114 |
|
| 115 | static int max8660_dcdc_disable(struct regulator_dev *rdev)
|
| 116 | {
|
| 117 | struct max8660 *max8660 = rdev_get_drvdata(rdev);
|
| 118 | u8 mask = (rdev_get_id(rdev) == MAX8660_V3) ? ~1 : ~4;
|
| 119 |
|
| 120 | return max8660_write(max8660, MAX8660_OVER1, mask, 0);
|
| 121 | }
|
| 122 |
|
| 123 | static int max8660_dcdc_get_voltage_sel(struct regulator_dev *rdev)
|
| 124 | {
|
| 125 | struct max8660 *max8660 = rdev_get_drvdata(rdev);
|
| 126 | u8 reg = (rdev_get_id(rdev) == MAX8660_V3) ? MAX8660_ADTV2 : MAX8660_SDTV2;
|
| 127 | u8 selector = max8660->shadow_regs[reg];
|
| 128 |
|
| 129 | return selector;
|
| 130 | }
|
| 131 |
|
| 132 | static int max8660_dcdc_set_voltage_sel(struct regulator_dev *rdev,
|
| 133 | unsigned int selector)
|
| 134 | {
|
| 135 | struct max8660 *max8660 = rdev_get_drvdata(rdev);
|
| 136 | u8 reg, bits;
|
| 137 | int ret;
|
| 138 |
|
| 139 | reg = (rdev_get_id(rdev) == MAX8660_V3) ? MAX8660_ADTV2 : MAX8660_SDTV2;
|
| 140 | ret = max8660_write(max8660, reg, 0, selector);
|
| 141 | if (ret)
|
| 142 | return ret;
|
| 143 |
|
| 144 |
|
| 145 | bits = (rdev_get_id(rdev) == MAX8660_V3) ? 0x03 : 0x30;
|
| 146 | return max8660_write(max8660, MAX8660_VCC1, 0xff, bits);
|
| 147 | }
|
| 148 |
|
| 149 | static struct regulator_ops max8660_dcdc_ops = {
|
| 150 | .is_enabled = max8660_dcdc_is_enabled,
|
| 151 | .list_voltage = regulator_list_voltage_linear,
|
| 152 | .map_voltage = regulator_map_voltage_linear,
|
| 153 | .set_voltage_sel = max8660_dcdc_set_voltage_sel,
|
| 154 | .get_voltage_sel = max8660_dcdc_get_voltage_sel,
|
| 155 | };
|
| 156 |
|
| 157 |
|
| 158 | |
| 159 | * LDO5 functions
|
| 160 |
|
| 161 |
|
| 162 | static int max8660_ldo5_get_voltage_sel(struct regulator_dev *rdev)
|
| 163 | {
|
| 164 | struct max8660 *max8660 = rdev_get_drvdata(rdev);
|
| 165 |
|
| 166 | u8 selector = max8660->shadow_regs[MAX8660_MDTV2];
|
| 167 | return selector;
|
| 168 | }
|
| 169 |
|
| 170 | static int max8660_ldo5_set_voltage_sel(struct regulator_dev *rdev,
|
| 171 | unsigned int selector)
|
| 172 | {
|
| 173 | struct max8660 *max8660 = rdev_get_drvdata(rdev);
|
| 174 | int ret;
|
| 175 |
|
| 176 | ret = max8660_write(max8660, MAX8660_MDTV2, 0, selector);
|
| 177 | if (ret)
|
| 178 | return ret;
|
| 179 |
|
| 180 |
|
| 181 | return max8660_write(max8660, MAX8660_VCC1, 0xff, 0xc0);
|
| 182 | }
|
| 183 |
|
| 184 | static const struct regulator_ops max8660_ldo5_ops = {
|
| 185 | .list_voltage = regulator_list_voltage_linear,
|
| 186 | .map_voltage = regulator_map_voltage_linear,
|
| 187 | .set_voltage_sel = max8660_ldo5_set_voltage_sel,
|
| 188 | .get_voltage_sel = max8660_ldo5_get_voltage_sel,
|
| 189 | };
|
| 190 |
|
| 191 |
|
| 192 | |
| 193 | * LDO67 functions
|
| 194 |
|
| 195 |
|
| 196 | static int max8660_ldo67_is_enabled(struct regulator_dev *rdev)
|
| 197 | {
|
| 198 | struct max8660 *max8660 = rdev_get_drvdata(rdev);
|
| 199 | u8 val = max8660->shadow_regs[MAX8660_OVER2];
|
| 200 | u8 mask = (rdev_get_id(rdev) == MAX8660_V6) ? 2 : 4;
|
| 201 |
|
| 202 | return !!(val & mask);
|
| 203 | }
|
| 204 |
|
| 205 | static int max8660_ldo67_enable(struct regulator_dev *rdev)
|
| 206 | {
|
| 207 | struct max8660 *max8660 = rdev_get_drvdata(rdev);
|
| 208 | u8 bit = (rdev_get_id(rdev) == MAX8660_V6) ? 2 : 4;
|
| 209 |
|
| 210 | return max8660_write(max8660, MAX8660_OVER2, 0xff, bit);
|
| 211 | }
|
| 212 |
|
| 213 | static int max8660_ldo67_disable(struct regulator_dev *rdev)
|
| 214 | {
|
| 215 | struct max8660 *max8660 = rdev_get_drvdata(rdev);
|
| 216 | u8 mask = (rdev_get_id(rdev) == MAX8660_V6) ? ~2 : ~4;
|
| 217 |
|
| 218 | return max8660_write(max8660, MAX8660_OVER2, mask, 0);
|
| 219 | }
|
| 220 |
|
| 221 | static int max8660_ldo67_get_voltage_sel(struct regulator_dev *rdev)
|
| 222 | {
|
| 223 | struct max8660 *max8660 = rdev_get_drvdata(rdev);
|
| 224 | u8 shift = (rdev_get_id(rdev) == MAX8660_V6) ? 0 : 4;
|
| 225 | u8 selector = (max8660->shadow_regs[MAX8660_L12VCR] >> shift) & 0xf;
|
| 226 |
|
| 227 | return selector;
|
| 228 | }
|
| 229 |
|
| 230 | static int max8660_ldo67_set_voltage_sel(struct regulator_dev *rdev,
|
| 231 | unsigned int selector)
|
| 232 | {
|
| 233 | struct max8660 *max8660 = rdev_get_drvdata(rdev);
|
| 234 |
|
| 235 | if (rdev_get_id(rdev) == MAX8660_V6)
|
| 236 | return max8660_write(max8660, MAX8660_L12VCR, 0xf0, selector);
|
| 237 | else
|
| 238 | return max8660_write(max8660, MAX8660_L12VCR, 0x0f,
|
| 239 | selector << 4);
|
| 240 | }
|
| 241 |
|
| 242 | static const struct regulator_ops max8660_ldo67_ops = {
|
| 243 | .is_enabled = max8660_ldo67_is_enabled,
|
| 244 | .enable = max8660_ldo67_enable,
|
| 245 | .disable = max8660_ldo67_disable,
|
| 246 | .list_voltage = regulator_list_voltage_linear,
|
| 247 | .map_voltage = regulator_map_voltage_linear,
|
| 248 | .get_voltage_sel = max8660_ldo67_get_voltage_sel,
|
| 249 | .set_voltage_sel = max8660_ldo67_set_voltage_sel,
|
| 250 | };
|
| 251 |
|
| 252 | static const struct regulator_desc max8660_reg[] = {
|
| 253 | {
|
| 254 | .name = "V3(DCDC)",
|
| 255 | .id = MAX8660_V3,
|
| 256 | .ops = &max8660_dcdc_ops,
|
| 257 | .type = REGULATOR_VOLTAGE,
|
| 258 | .n_voltages = MAX8660_DCDC_MAX_SEL + 1,
|
| 259 | .owner = THIS_MODULE,
|
| 260 | .min_uV = MAX8660_DCDC_MIN_UV,
|
| 261 | .uV_step = MAX8660_DCDC_STEP,
|
| 262 | },
|
| 263 | {
|
| 264 | .name = "V4(DCDC)",
|
| 265 | .id = MAX8660_V4,
|
| 266 | .ops = &max8660_dcdc_ops,
|
| 267 | .type = REGULATOR_VOLTAGE,
|
| 268 | .n_voltages = MAX8660_DCDC_MAX_SEL + 1,
|
| 269 | .owner = THIS_MODULE,
|
| 270 | .min_uV = MAX8660_DCDC_MIN_UV,
|
| 271 | .uV_step = MAX8660_DCDC_STEP,
|
| 272 | },
|
| 273 | {
|
| 274 | .name = "V5(LDO)",
|
| 275 | .id = MAX8660_V5,
|
| 276 | .ops = &max8660_ldo5_ops,
|
| 277 | .type = REGULATOR_VOLTAGE,
|
| 278 | .n_voltages = MAX8660_LDO5_MAX_SEL + 1,
|
| 279 | .owner = THIS_MODULE,
|
| 280 | .min_uV = MAX8660_LDO5_MIN_UV,
|
| 281 | .uV_step = MAX8660_LDO5_STEP,
|
| 282 | },
|
| 283 | {
|
| 284 | .name = "V6(LDO)",
|
| 285 | .id = MAX8660_V6,
|
| 286 | .ops = &max8660_ldo67_ops,
|
| 287 | .type = REGULATOR_VOLTAGE,
|
| 288 | .n_voltages = MAX8660_LDO67_MAX_SEL + 1,
|
| 289 | .owner = THIS_MODULE,
|
| 290 | .min_uV = MAX8660_LDO67_MIN_UV,
|
| 291 | .uV_step = MAX8660_LDO67_STEP,
|
| 292 | },
|
| 293 | {
|
| 294 | .name = "V7(LDO)",
|
| 295 | .id = MAX8660_V7,
|
| 296 | .ops = &max8660_ldo67_ops,
|
| 297 | .type = REGULATOR_VOLTAGE,
|
| 298 | .n_voltages = MAX8660_LDO67_MAX_SEL + 1,
|
| 299 | .owner = THIS_MODULE,
|
| 300 | .min_uV = MAX8660_LDO67_MIN_UV,
|
| 301 | .uV_step = MAX8660_LDO67_STEP,
|
| 302 | },
|
| 303 | };
|
| 304 |
|
| 305 | enum {
|
| 306 | MAX8660 = 0,
|
| 307 | MAX8661 = 1,
|
| 308 | };
|
| 309 |
|
| 310 | #ifdef CONFIG_OF
|
| 311 | static const struct of_device_id max8660_dt_ids[] = {
|
| 312 | { .compatible = "maxim,max8660", .data = (void *) MAX8660 },
|
| 313 | { .compatible = "maxim,max8661", .data = (void *) MAX8661 },
|
| 314 | { }
|
| 315 | };
|
| 316 | MODULE_DEVICE_TABLE(of, max8660_dt_ids);
|
| 317 |
|
| 318 | static int max8660_pdata_from_dt(struct device *dev,
|
| 319 | struct device_node **of_node,
|
| 320 | struct max8660_platform_data *pdata)
|
| 321 | {
|
| 322 | int matched, i;
|
| 323 | struct device_node *np;
|
| 324 | struct max8660_subdev_data *sub;
|
| 325 | struct of_regulator_match rmatch[ARRAY_SIZE(max8660_reg)] = { };
|
| 326 |
|
| 327 | np = of_get_child_by_name(dev->of_node, "regulators");
|
| 328 | if (!np) {
|
| 329 | dev_err(dev, "missing 'regulators' subnode in DT\n");
|
| 330 | return -EINVAL;
|
| 331 | }
|
| 332 |
|
| 333 | for (i = 0; i < ARRAY_SIZE(rmatch); i++)
|
| 334 | rmatch[i].name = max8660_reg[i].name;
|
| 335 |
|
| 336 | matched = of_regulator_match(dev, np, rmatch, ARRAY_SIZE(rmatch));
|
| 337 | of_node_put(np);
|
| 338 | if (matched <= 0)
|
| 339 | return matched;
|
| 340 |
|
| 341 | pdata->subdevs = devm_kcalloc(dev,
|
| 342 | matched,
|
| 343 | sizeof(struct max8660_subdev_data),
|
| 344 | GFP_KERNEL);
|
| 345 | if (!pdata->subdevs)
|
| 346 | return -ENOMEM;
|
| 347 |
|
| 348 | pdata->num_subdevs = matched;
|
| 349 | sub = pdata->subdevs;
|
| 350 |
|
| 351 | for (i = 0; i < matched; i++) {
|
| 352 | sub->id = i;
|
| 353 | sub->name = rmatch[i].name;
|
| 354 | sub->platform_data = rmatch[i].init_data;
|
| 355 | of_node[i] = rmatch[i].of_node;
|
| 356 | sub++;
|
| 357 | }
|
| 358 |
|
| 359 | return 0;
|
| 360 | }
|
| 361 | #else
|
| 362 | static inline int max8660_pdata_from_dt(struct device *dev,
|
| 363 | struct device_node **of_node,
|
| 364 | struct max8660_platform_data *pdata)
|
| 365 | {
|
| 366 | return 0;
|
| 367 | }
|
| 368 | #endif
|
| 369 |
|
| 370 | static int max8660_probe(struct i2c_client *client)
|
| 371 | {
|
| 372 | const struct i2c_device_id *i2c_id = i2c_client_get_device_id(client);
|
| 373 | struct device *dev = &client->dev;
|
| 374 | struct max8660_platform_data pdata_of, *pdata = dev_get_platdata(dev);
|
| 375 | struct regulator_config config = { };
|
| 376 | struct max8660 *max8660;
|
| 377 | int boot_on, i, id, ret = -EINVAL;
|
| 378 | struct device_node *of_node[MAX8660_V_END];
|
| 379 | unsigned long type;
|
| 380 |
|
| 381 | if (dev->of_node && !pdata) {
|
| 382 | const struct of_device_id *id;
|
| 383 |
|
| 384 | id = of_match_device(of_match_ptr(max8660_dt_ids), dev);
|
| 385 | if (!id)
|
| 386 | return -ENODEV;
|
| 387 |
|
| 388 | ret = max8660_pdata_from_dt(dev, of_node, &pdata_of);
|
| 389 | if (ret < 0)
|
| 390 | return ret;
|
| 391 |
|
| 392 | pdata = &pdata_of;
|
| 393 | type = (unsigned long) id->data;
|
| 394 | } else {
|
| 395 | type = i2c_id->driver_data;
|
| 396 | memset(of_node, 0, sizeof(of_node));
|
| 397 | }
|
| 398 |
|
| 399 | if (pdata->num_subdevs > MAX8660_V_END) {
|
| 400 | dev_err(dev, "Too many regulators found!\n");
|
| 401 | return -EINVAL;
|
| 402 | }
|
| 403 |
|
| 404 | max8660 = devm_kzalloc(dev, sizeof(struct max8660), GFP_KERNEL);
|
| 405 | if (!max8660)
|
| 406 | return -ENOMEM;
|
| 407 |
|
| 408 | max8660->client = client;
|
| 409 |
|
| 410 | if (pdata->en34_is_high) {
|
| 411 |
|
| 412 | max8660->shadow_regs[MAX8660_OVER1] = 5;
|
| 413 | } else {
|
| 414 |
|
| 415 | max8660_dcdc_ops.enable = max8660_dcdc_enable;
|
| 416 | max8660_dcdc_ops.disable = max8660_dcdc_disable;
|
| 417 | }
|
| 418 |
|
| 419 | |
| 420 | * First, set up shadow registers to prevent glitches. As some
|
| 421 | * registers are shared between regulators, everything must be properly
|
| 422 | * set up for all regulators in advance.
|
| 423 |
|
| 424 | max8660->shadow_regs[MAX8660_ADTV1] =
|
| 425 | max8660->shadow_regs[MAX8660_ADTV2] =
|
| 426 | max8660->shadow_regs[MAX8660_SDTV1] =
|
| 427 | max8660->shadow_regs[MAX8660_SDTV2] = 0x1b;
|
| 428 | max8660->shadow_regs[MAX8660_MDTV1] =
|
| 429 | max8660->shadow_regs[MAX8660_MDTV2] = 0x04;
|
| 430 |
|
| 431 | for (i = 0; i < pdata->num_subdevs; i++) {
|
| 432 |
|
| 433 | if (!pdata->subdevs[i].platform_data)
|
| 434 | boot_on = false;
|
| 435 | else
|
| 436 | boot_on = pdata->subdevs[i].platform_data->constraints.boot_on;
|
| 437 |
|
| 438 | switch (pdata->subdevs[i].id) {
|
| 439 | case MAX8660_V3:
|
| 440 | if (boot_on)
|
| 441 | max8660->shadow_regs[MAX8660_OVER1] |= 1;
|
| 442 | break;
|
| 443 |
|
| 444 | case MAX8660_V4:
|
| 445 | if (boot_on)
|
| 446 | max8660->shadow_regs[MAX8660_OVER1] |= 4;
|
| 447 | break;
|
| 448 |
|
| 449 | case MAX8660_V5:
|
| 450 | break;
|
| 451 |
|
| 452 | case MAX8660_V6:
|
| 453 | if (boot_on)
|
| 454 | max8660->shadow_regs[MAX8660_OVER2] |= 2;
|
| 455 | break;
|
| 456 |
|
| 457 | case MAX8660_V7:
|
| 458 | if (type == MAX8661) {
|
| 459 | dev_err(dev, "Regulator not on this chip!\n");
|
| 460 | return -EINVAL;
|
| 461 | }
|
| 462 |
|
| 463 | if (boot_on)
|
| 464 | max8660->shadow_regs[MAX8660_OVER2] |= 4;
|
| 465 | break;
|
| 466 |
|
| 467 | default:
|
| 468 | dev_err(dev, "invalid regulator %s\n",
|
| 469 | pdata->subdevs[i].name);
|
| 470 | return ret;
|
| 471 | }
|
| 472 | }
|
| 473 |
|
| 474 |
|
| 475 | for (i = 0; i < pdata->num_subdevs; i++) {
|
| 476 | struct regulator_dev *rdev;
|
| 477 |
|
| 478 | id = pdata->subdevs[i].id;
|
| 479 |
|
| 480 | config.dev = dev;
|
| 481 | config.init_data = pdata->subdevs[i].platform_data;
|
| 482 | config.of_node = of_node[i];
|
| 483 | config.driver_data = max8660;
|
| 484 |
|
| 485 | rdev = devm_regulator_register(&client->dev,
|
| 486 | &max8660_reg[id], &config);
|
| 487 | if (IS_ERR(rdev)) {
|
| 488 | dev_err(&client->dev, "failed to register %s\n",
|
| 489 | max8660_reg[id].name);
|
| 490 | return PTR_ERR(rdev);
|
| 491 | }
|
| 492 | }
|
| 493 |
|
| 494 | i2c_set_clientdata(client, max8660);
|
| 495 | return 0;
|
| 496 | }
|
| 497 |
|
| 498 | static const struct i2c_device_id max8660_id[] = {
|
| 499 | { .name = "max8660", .driver_data = MAX8660 },
|
| 500 | { .name = "max8661", .driver_data = MAX8661 },
|
| 501 | { }
|
| 502 | };
|
| 503 | MODULE_DEVICE_TABLE(i2c, max8660_id);
|
| 504 |
|
| 505 | static struct i2c_driver max8660_driver = {
|
| 506 | .probe = max8660_probe,
|
| 507 | .driver = {
|
| 508 | .name = "max8660",
|
| 509 | .probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
| 510 | },
|
| 511 | .id_table = max8660_id,
|
| 512 | };
|
| 513 |
|
| 514 | static int __init max8660_init(void)
|
| 515 | {
|
| 516 | return i2c_add_driver(&max8660_driver);
|
| 517 | }
|
| 518 | subsys_initcall(max8660_init);
|
| 519 |
|
| 520 | static void __exit max8660_exit(void)
|
| 521 | {
|
| 522 | i2c_del_driver(&max8660_driver);
|
| 523 | }
|
| 524 | module_exit(max8660_exit);
|
| 525 |
|
| 526 |
|
| 527 | MODULE_DESCRIPTION("MAXIM 8660/8661 voltage regulator driver");
|
| 528 | MODULE_AUTHOR("Wolfram Sang");
|
| 529 | MODULE_LICENSE("GPL v2");
|