169dc16c3SMark Brown /* 269dc16c3SMark Brown * wm8994-regulator.c -- Regulator driver for the WM8994 369dc16c3SMark Brown * 469dc16c3SMark Brown * Copyright 2009 Wolfson Microelectronics PLC. 569dc16c3SMark Brown * 669dc16c3SMark Brown * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 769dc16c3SMark Brown * 869dc16c3SMark Brown * This program is free software; you can redistribute it and/or modify it 969dc16c3SMark Brown * under the terms of the GNU General Public License as published by the 1069dc16c3SMark Brown * Free Software Foundation; either version 2 of the License, or (at your 1169dc16c3SMark Brown * option) any later version. 1269dc16c3SMark Brown */ 1369dc16c3SMark Brown 1469dc16c3SMark Brown #include <linux/module.h> 1569dc16c3SMark Brown #include <linux/moduleparam.h> 1669dc16c3SMark Brown #include <linux/init.h> 1769dc16c3SMark Brown #include <linux/bitops.h> 1869dc16c3SMark Brown #include <linux/err.h> 1969dc16c3SMark Brown #include <linux/platform_device.h> 2069dc16c3SMark Brown #include <linux/regulator/driver.h> 2169dc16c3SMark Brown #include <linux/gpio.h> 225a0e3ad6STejun Heo #include <linux/slab.h> 2369dc16c3SMark Brown 2469dc16c3SMark Brown #include <linux/mfd/wm8994/core.h> 2569dc16c3SMark Brown #include <linux/mfd/wm8994/registers.h> 2669dc16c3SMark Brown #include <linux/mfd/wm8994/pdata.h> 2769dc16c3SMark Brown 2869dc16c3SMark Brown struct wm8994_ldo { 2969dc16c3SMark Brown int enable; 30598b3578SDmitry Torokhov bool is_enabled; 3169dc16c3SMark Brown struct regulator_dev *regulator; 3269dc16c3SMark Brown struct wm8994 *wm8994; 3369dc16c3SMark Brown }; 3469dc16c3SMark Brown 3569dc16c3SMark Brown #define WM8994_LDO1_MAX_SELECTOR 0x7 3669dc16c3SMark Brown #define WM8994_LDO2_MAX_SELECTOR 0x3 3769dc16c3SMark Brown 3869dc16c3SMark Brown static int wm8994_ldo_enable(struct regulator_dev *rdev) 3969dc16c3SMark Brown { 4069dc16c3SMark Brown struct wm8994_ldo *ldo = rdev_get_drvdata(rdev); 4169dc16c3SMark Brown 4269dc16c3SMark Brown /* If we have no soft control assume that the LDO is always enabled. */ 4369dc16c3SMark Brown if (!ldo->enable) 4469dc16c3SMark Brown return 0; 4569dc16c3SMark Brown 462ae3636bSMark Brown gpio_set_value_cansleep(ldo->enable, 1); 47598b3578SDmitry Torokhov ldo->is_enabled = true; 4869dc16c3SMark Brown 4969dc16c3SMark Brown return 0; 5069dc16c3SMark Brown } 5169dc16c3SMark Brown 5269dc16c3SMark Brown static int wm8994_ldo_disable(struct regulator_dev *rdev) 5369dc16c3SMark Brown { 5469dc16c3SMark Brown struct wm8994_ldo *ldo = rdev_get_drvdata(rdev); 5569dc16c3SMark Brown 5669dc16c3SMark Brown /* If we have no soft control assume that the LDO is always enabled. */ 5769dc16c3SMark Brown if (!ldo->enable) 5869dc16c3SMark Brown return -EINVAL; 5969dc16c3SMark Brown 602ae3636bSMark Brown gpio_set_value_cansleep(ldo->enable, 0); 61598b3578SDmitry Torokhov ldo->is_enabled = false; 6269dc16c3SMark Brown 6369dc16c3SMark Brown return 0; 6469dc16c3SMark Brown } 6569dc16c3SMark Brown 6669dc16c3SMark Brown static int wm8994_ldo_is_enabled(struct regulator_dev *rdev) 6769dc16c3SMark Brown { 6869dc16c3SMark Brown struct wm8994_ldo *ldo = rdev_get_drvdata(rdev); 6969dc16c3SMark Brown 7069dc16c3SMark Brown return ldo->is_enabled; 7169dc16c3SMark Brown } 7269dc16c3SMark Brown 7369dc16c3SMark Brown static int wm8994_ldo_enable_time(struct regulator_dev *rdev) 7469dc16c3SMark Brown { 7569dc16c3SMark Brown /* 3ms is fairly conservative but this shouldn't be too performance 7669dc16c3SMark Brown * critical; can be tweaked per-system if required. */ 7769dc16c3SMark Brown return 3000; 7869dc16c3SMark Brown } 7969dc16c3SMark Brown 8069dc16c3SMark Brown static int wm8994_ldo1_list_voltage(struct regulator_dev *rdev, 8169dc16c3SMark Brown unsigned int selector) 8269dc16c3SMark Brown { 8369dc16c3SMark Brown if (selector > WM8994_LDO1_MAX_SELECTOR) 8469dc16c3SMark Brown return -EINVAL; 8569dc16c3SMark Brown 8669dc16c3SMark Brown return (selector * 100000) + 2400000; 8769dc16c3SMark Brown } 8869dc16c3SMark Brown 89d9f0f287SMark Brown static int wm8994_ldo1_get_voltage_sel(struct regulator_dev *rdev) 9069dc16c3SMark Brown { 9169dc16c3SMark Brown struct wm8994_ldo *ldo = rdev_get_drvdata(rdev); 9269dc16c3SMark Brown int val; 9369dc16c3SMark Brown 9469dc16c3SMark Brown val = wm8994_reg_read(ldo->wm8994, WM8994_LDO_1); 9569dc16c3SMark Brown if (val < 0) 9669dc16c3SMark Brown return val; 9769dc16c3SMark Brown 98d9f0f287SMark Brown return (val & WM8994_LDO1_VSEL_MASK) >> WM8994_LDO1_VSEL_SHIFT; 9969dc16c3SMark Brown } 10069dc16c3SMark Brown 1019f29c9e3SMark Brown static int wm8994_ldo1_set_voltage_sel(struct regulator_dev *rdev, 1029f29c9e3SMark Brown unsigned selector) 10369dc16c3SMark Brown { 10469dc16c3SMark Brown struct wm8994_ldo *ldo = rdev_get_drvdata(rdev); 10569dc16c3SMark Brown 10669dc16c3SMark Brown selector <<= WM8994_LDO1_VSEL_SHIFT; 10769dc16c3SMark Brown 10869dc16c3SMark Brown return wm8994_set_bits(ldo->wm8994, WM8994_LDO_1, 10969dc16c3SMark Brown WM8994_LDO1_VSEL_MASK, selector); 11069dc16c3SMark Brown } 11169dc16c3SMark Brown 11269dc16c3SMark Brown static struct regulator_ops wm8994_ldo1_ops = { 11369dc16c3SMark Brown .enable = wm8994_ldo_enable, 11469dc16c3SMark Brown .disable = wm8994_ldo_disable, 11569dc16c3SMark Brown .is_enabled = wm8994_ldo_is_enabled, 11669dc16c3SMark Brown .enable_time = wm8994_ldo_enable_time, 11769dc16c3SMark Brown 11869dc16c3SMark Brown .list_voltage = wm8994_ldo1_list_voltage, 119d9f0f287SMark Brown .get_voltage_sel = wm8994_ldo1_get_voltage_sel, 1209f29c9e3SMark Brown .set_voltage_sel = wm8994_ldo1_set_voltage_sel, 12169dc16c3SMark Brown }; 12269dc16c3SMark Brown 12369dc16c3SMark Brown static int wm8994_ldo2_list_voltage(struct regulator_dev *rdev, 12469dc16c3SMark Brown unsigned int selector) 12569dc16c3SMark Brown { 1265a7743edSMark Brown struct wm8994_ldo *ldo = rdev_get_drvdata(rdev); 1275a7743edSMark Brown 12869dc16c3SMark Brown if (selector > WM8994_LDO2_MAX_SELECTOR) 12969dc16c3SMark Brown return -EINVAL; 13069dc16c3SMark Brown 1315a7743edSMark Brown switch (ldo->wm8994->type) { 1325a7743edSMark Brown case WM8994: 13369dc16c3SMark Brown return (selector * 100000) + 900000; 1345a7743edSMark Brown case WM8958: 1355a7743edSMark Brown return (selector * 100000) + 1000000; 136a1ff89efSMark Brown case WM1811: 137a1ff89efSMark Brown switch (selector) { 138a1ff89efSMark Brown case 0: 139a1ff89efSMark Brown return -EINVAL; 140a1ff89efSMark Brown default: 141a1ff89efSMark Brown return (selector * 100000) + 950000; 142a1ff89efSMark Brown } 143a1ff89efSMark Brown break; 1445a7743edSMark Brown default: 1455a7743edSMark Brown return -EINVAL; 1465a7743edSMark Brown } 14769dc16c3SMark Brown } 14869dc16c3SMark Brown 149d9f0f287SMark Brown static int wm8994_ldo2_get_voltage_sel(struct regulator_dev *rdev) 15069dc16c3SMark Brown { 15169dc16c3SMark Brown struct wm8994_ldo *ldo = rdev_get_drvdata(rdev); 15269dc16c3SMark Brown int val; 15369dc16c3SMark Brown 15469dc16c3SMark Brown val = wm8994_reg_read(ldo->wm8994, WM8994_LDO_2); 15569dc16c3SMark Brown if (val < 0) 15669dc16c3SMark Brown return val; 15769dc16c3SMark Brown 158d9f0f287SMark Brown return (val & WM8994_LDO2_VSEL_MASK) >> WM8994_LDO2_VSEL_SHIFT; 15969dc16c3SMark Brown } 16069dc16c3SMark Brown 1619f29c9e3SMark Brown static int wm8994_ldo2_set_voltage_sel(struct regulator_dev *rdev, 1629f29c9e3SMark Brown unsigned selector) 16369dc16c3SMark Brown { 16469dc16c3SMark Brown struct wm8994_ldo *ldo = rdev_get_drvdata(rdev); 16569dc16c3SMark Brown 16669dc16c3SMark Brown selector <<= WM8994_LDO2_VSEL_SHIFT; 16769dc16c3SMark Brown 16869dc16c3SMark Brown return wm8994_set_bits(ldo->wm8994, WM8994_LDO_2, 16969dc16c3SMark Brown WM8994_LDO2_VSEL_MASK, selector); 17069dc16c3SMark Brown } 17169dc16c3SMark Brown 17269dc16c3SMark Brown static struct regulator_ops wm8994_ldo2_ops = { 17369dc16c3SMark Brown .enable = wm8994_ldo_enable, 17469dc16c3SMark Brown .disable = wm8994_ldo_disable, 17569dc16c3SMark Brown .is_enabled = wm8994_ldo_is_enabled, 17669dc16c3SMark Brown .enable_time = wm8994_ldo_enable_time, 17769dc16c3SMark Brown 17869dc16c3SMark Brown .list_voltage = wm8994_ldo2_list_voltage, 179d9f0f287SMark Brown .get_voltage_sel = wm8994_ldo2_get_voltage_sel, 1809f29c9e3SMark Brown .set_voltage_sel = wm8994_ldo2_set_voltage_sel, 18169dc16c3SMark Brown }; 18269dc16c3SMark Brown 18325e4d602SMark Brown static const struct regulator_desc wm8994_ldo_desc[] = { 18469dc16c3SMark Brown { 18569dc16c3SMark Brown .name = "LDO1", 18669dc16c3SMark Brown .id = 1, 18769dc16c3SMark Brown .type = REGULATOR_VOLTAGE, 18869dc16c3SMark Brown .n_voltages = WM8994_LDO1_MAX_SELECTOR + 1, 18969dc16c3SMark Brown .ops = &wm8994_ldo1_ops, 19069dc16c3SMark Brown .owner = THIS_MODULE, 19169dc16c3SMark Brown }, 19269dc16c3SMark Brown { 19369dc16c3SMark Brown .name = "LDO2", 19469dc16c3SMark Brown .id = 2, 19569dc16c3SMark Brown .type = REGULATOR_VOLTAGE, 19669dc16c3SMark Brown .n_voltages = WM8994_LDO2_MAX_SELECTOR + 1, 19769dc16c3SMark Brown .ops = &wm8994_ldo2_ops, 19869dc16c3SMark Brown .owner = THIS_MODULE, 19969dc16c3SMark Brown }, 20069dc16c3SMark Brown }; 20169dc16c3SMark Brown 20269dc16c3SMark Brown static __devinit int wm8994_ldo_probe(struct platform_device *pdev) 20369dc16c3SMark Brown { 20469dc16c3SMark Brown struct wm8994 *wm8994 = dev_get_drvdata(pdev->dev.parent); 20569dc16c3SMark Brown struct wm8994_pdata *pdata = wm8994->dev->platform_data; 20669dc16c3SMark Brown int id = pdev->id % ARRAY_SIZE(pdata->ldo); 207c172708dSMark Brown struct regulator_config config = { }; 20869dc16c3SMark Brown struct wm8994_ldo *ldo; 20969dc16c3SMark Brown int ret; 21069dc16c3SMark Brown 21169dc16c3SMark Brown dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1); 21269dc16c3SMark Brown 21369dc16c3SMark Brown if (!pdata) 21469dc16c3SMark Brown return -ENODEV; 21569dc16c3SMark Brown 216b9e0348fSMark Brown ldo = devm_kzalloc(&pdev->dev, sizeof(struct wm8994_ldo), GFP_KERNEL); 21769dc16c3SMark Brown if (ldo == NULL) { 21869dc16c3SMark Brown dev_err(&pdev->dev, "Unable to allocate private data\n"); 21969dc16c3SMark Brown return -ENOMEM; 22069dc16c3SMark Brown } 22169dc16c3SMark Brown 22269dc16c3SMark Brown ldo->wm8994 = wm8994; 22369dc16c3SMark Brown 22469dc16c3SMark Brown if (pdata->ldo[id].enable && gpio_is_valid(pdata->ldo[id].enable)) { 22569dc16c3SMark Brown ldo->enable = pdata->ldo[id].enable; 22669dc16c3SMark Brown 22709e3fd20SMark Brown ret = gpio_request_one(ldo->enable, 0, "WM8994 LDO enable"); 22869dc16c3SMark Brown if (ret < 0) { 22969dc16c3SMark Brown dev_err(&pdev->dev, "Failed to get enable GPIO: %d\n", 23069dc16c3SMark Brown ret); 23169dc16c3SMark Brown goto err; 23269dc16c3SMark Brown } 233c4604e49SJoonyoung Shim } else 234c4604e49SJoonyoung Shim ldo->is_enabled = true; 23569dc16c3SMark Brown 2365d0526eaSMark Brown config.dev = wm8994->dev; 237c172708dSMark Brown config.init_data = pdata->ldo[id].init_data; 238c172708dSMark Brown config.driver_data = ldo; 239c172708dSMark Brown 240c172708dSMark Brown ldo->regulator = regulator_register(&wm8994_ldo_desc[id], &config); 24169dc16c3SMark Brown if (IS_ERR(ldo->regulator)) { 24269dc16c3SMark Brown ret = PTR_ERR(ldo->regulator); 24369dc16c3SMark Brown dev_err(wm8994->dev, "Failed to register LDO%d: %d\n", 24469dc16c3SMark Brown id + 1, ret); 24569dc16c3SMark Brown goto err_gpio; 24669dc16c3SMark Brown } 24769dc16c3SMark Brown 24869dc16c3SMark Brown platform_set_drvdata(pdev, ldo); 24969dc16c3SMark Brown 25069dc16c3SMark Brown return 0; 25169dc16c3SMark Brown 25269dc16c3SMark Brown err_gpio: 25369dc16c3SMark Brown if (gpio_is_valid(ldo->enable)) 25469dc16c3SMark Brown gpio_free(ldo->enable); 25569dc16c3SMark Brown err: 25669dc16c3SMark Brown return ret; 25769dc16c3SMark Brown } 25869dc16c3SMark Brown 25969dc16c3SMark Brown static __devexit int wm8994_ldo_remove(struct platform_device *pdev) 26069dc16c3SMark Brown { 26169dc16c3SMark Brown struct wm8994_ldo *ldo = platform_get_drvdata(pdev); 26269dc16c3SMark Brown 263598b3578SDmitry Torokhov platform_set_drvdata(pdev, NULL); 264598b3578SDmitry Torokhov 26569dc16c3SMark Brown regulator_unregister(ldo->regulator); 26669dc16c3SMark Brown if (gpio_is_valid(ldo->enable)) 26769dc16c3SMark Brown gpio_free(ldo->enable); 26869dc16c3SMark Brown 26969dc16c3SMark Brown return 0; 27069dc16c3SMark Brown } 27169dc16c3SMark Brown 27269dc16c3SMark Brown static struct platform_driver wm8994_ldo_driver = { 27369dc16c3SMark Brown .probe = wm8994_ldo_probe, 27469dc16c3SMark Brown .remove = __devexit_p(wm8994_ldo_remove), 27569dc16c3SMark Brown .driver = { 27669dc16c3SMark Brown .name = "wm8994-ldo", 277598b3578SDmitry Torokhov .owner = THIS_MODULE, 27869dc16c3SMark Brown }, 27969dc16c3SMark Brown }; 28069dc16c3SMark Brown 28169dc16c3SMark Brown static int __init wm8994_ldo_init(void) 28269dc16c3SMark Brown { 28369dc16c3SMark Brown int ret; 28469dc16c3SMark Brown 28569dc16c3SMark Brown ret = platform_driver_register(&wm8994_ldo_driver); 28669dc16c3SMark Brown if (ret != 0) 28769dc16c3SMark Brown pr_err("Failed to register Wm8994 GP LDO driver: %d\n", ret); 28869dc16c3SMark Brown 28969dc16c3SMark Brown return ret; 29069dc16c3SMark Brown } 29169dc16c3SMark Brown subsys_initcall(wm8994_ldo_init); 29269dc16c3SMark Brown 29369dc16c3SMark Brown static void __exit wm8994_ldo_exit(void) 29469dc16c3SMark Brown { 29569dc16c3SMark Brown platform_driver_unregister(&wm8994_ldo_driver); 29669dc16c3SMark Brown } 29769dc16c3SMark Brown module_exit(wm8994_ldo_exit); 29869dc16c3SMark Brown 29969dc16c3SMark Brown /* Module information */ 30069dc16c3SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 30169dc16c3SMark Brown MODULE_DESCRIPTION("WM8994 LDO driver"); 30269dc16c3SMark Brown MODULE_LICENSE("GPL"); 30369dc16c3SMark Brown MODULE_ALIAS("platform:wm8994-ldo"); 304