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 struct regulator_dev *regulator; 3069dc16c3SMark Brown struct wm8994 *wm8994; 3169dc16c3SMark Brown }; 3269dc16c3SMark Brown 3369dc16c3SMark Brown #define WM8994_LDO1_MAX_SELECTOR 0x7 3469dc16c3SMark Brown #define WM8994_LDO2_MAX_SELECTOR 0x3 3569dc16c3SMark Brown 3669dc16c3SMark Brown static struct regulator_ops wm8994_ldo1_ops = { 37f2d103adSAxel Lin .list_voltage = regulator_list_voltage_linear, 38f2d103adSAxel Lin .map_voltage = regulator_map_voltage_linear, 39633b6fcdSMark Brown .get_voltage_sel = regulator_get_voltage_sel_regmap, 40633b6fcdSMark Brown .set_voltage_sel = regulator_set_voltage_sel_regmap, 4169dc16c3SMark Brown }; 4269dc16c3SMark Brown 4369dc16c3SMark Brown static int wm8994_ldo2_list_voltage(struct regulator_dev *rdev, 4469dc16c3SMark Brown unsigned int selector) 4569dc16c3SMark Brown { 465a7743edSMark Brown struct wm8994_ldo *ldo = rdev_get_drvdata(rdev); 475a7743edSMark Brown 4869dc16c3SMark Brown if (selector > WM8994_LDO2_MAX_SELECTOR) 4969dc16c3SMark Brown return -EINVAL; 5069dc16c3SMark Brown 515a7743edSMark Brown switch (ldo->wm8994->type) { 525a7743edSMark Brown case WM8994: 5369dc16c3SMark Brown return (selector * 100000) + 900000; 545a7743edSMark Brown case WM8958: 555a7743edSMark Brown return (selector * 100000) + 1000000; 56a1ff89efSMark Brown case WM1811: 57a1ff89efSMark Brown switch (selector) { 58a1ff89efSMark Brown case 0: 59a1ff89efSMark Brown return -EINVAL; 60a1ff89efSMark Brown default: 61a1ff89efSMark Brown return (selector * 100000) + 950000; 62a1ff89efSMark Brown } 63a1ff89efSMark Brown break; 645a7743edSMark Brown default: 655a7743edSMark Brown return -EINVAL; 665a7743edSMark Brown } 6769dc16c3SMark Brown } 6869dc16c3SMark Brown 6969dc16c3SMark Brown static struct regulator_ops wm8994_ldo2_ops = { 7069dc16c3SMark Brown .list_voltage = wm8994_ldo2_list_voltage, 71633b6fcdSMark Brown .get_voltage_sel = regulator_get_voltage_sel_regmap, 72633b6fcdSMark Brown .set_voltage_sel = regulator_set_voltage_sel_regmap, 7369dc16c3SMark Brown }; 7469dc16c3SMark Brown 7525e4d602SMark Brown static const struct regulator_desc wm8994_ldo_desc[] = { 7669dc16c3SMark Brown { 7769dc16c3SMark Brown .name = "LDO1", 7869dc16c3SMark Brown .id = 1, 7969dc16c3SMark Brown .type = REGULATOR_VOLTAGE, 8069dc16c3SMark Brown .n_voltages = WM8994_LDO1_MAX_SELECTOR + 1, 81633b6fcdSMark Brown .vsel_reg = WM8994_LDO_1, 82633b6fcdSMark Brown .vsel_mask = WM8994_LDO1_VSEL_MASK, 8369dc16c3SMark Brown .ops = &wm8994_ldo1_ops, 84f2d103adSAxel Lin .min_uV = 2400000, 85f2d103adSAxel Lin .uV_step = 100000, 8600581b30SMark Brown .enable_time = 3000, 8769dc16c3SMark Brown .owner = THIS_MODULE, 8869dc16c3SMark Brown }, 8969dc16c3SMark Brown { 9069dc16c3SMark Brown .name = "LDO2", 9169dc16c3SMark Brown .id = 2, 9269dc16c3SMark Brown .type = REGULATOR_VOLTAGE, 9369dc16c3SMark Brown .n_voltages = WM8994_LDO2_MAX_SELECTOR + 1, 94633b6fcdSMark Brown .vsel_reg = WM8994_LDO_2, 95633b6fcdSMark Brown .vsel_mask = WM8994_LDO2_VSEL_MASK, 9669dc16c3SMark Brown .ops = &wm8994_ldo2_ops, 9700581b30SMark Brown .enable_time = 3000, 9869dc16c3SMark Brown .owner = THIS_MODULE, 9969dc16c3SMark Brown }, 10069dc16c3SMark Brown }; 10169dc16c3SMark Brown 10269dc16c3SMark Brown static __devinit int wm8994_ldo_probe(struct platform_device *pdev) 10369dc16c3SMark Brown { 10469dc16c3SMark Brown struct wm8994 *wm8994 = dev_get_drvdata(pdev->dev.parent); 10569dc16c3SMark Brown struct wm8994_pdata *pdata = wm8994->dev->platform_data; 10669dc16c3SMark Brown int id = pdev->id % ARRAY_SIZE(pdata->ldo); 107c172708dSMark Brown struct regulator_config config = { }; 10869dc16c3SMark Brown struct wm8994_ldo *ldo; 10969dc16c3SMark Brown int ret; 11069dc16c3SMark Brown 11169dc16c3SMark Brown dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1); 11269dc16c3SMark Brown 113b9e0348fSMark Brown ldo = devm_kzalloc(&pdev->dev, sizeof(struct wm8994_ldo), GFP_KERNEL); 11469dc16c3SMark Brown if (ldo == NULL) { 11569dc16c3SMark Brown dev_err(&pdev->dev, "Unable to allocate private data\n"); 11669dc16c3SMark Brown return -ENOMEM; 11769dc16c3SMark Brown } 11869dc16c3SMark Brown 11969dc16c3SMark Brown ldo->wm8994 = wm8994; 12069dc16c3SMark Brown 1215d0526eaSMark Brown config.dev = wm8994->dev; 122c172708dSMark Brown config.driver_data = ldo; 123633b6fcdSMark Brown config.regmap = wm8994->regmap; 124495fd8c8SMark Brown if (pdata) { 125b68b45d7SMark Brown config.init_data = pdata->ldo[id].init_data; 126495fd8c8SMark Brown config.ena_gpio = pdata->ldo[id].enable; 127495fd8c8SMark Brown } 128c172708dSMark Brown 129c172708dSMark Brown ldo->regulator = regulator_register(&wm8994_ldo_desc[id], &config); 13069dc16c3SMark Brown if (IS_ERR(ldo->regulator)) { 13169dc16c3SMark Brown ret = PTR_ERR(ldo->regulator); 13269dc16c3SMark Brown dev_err(wm8994->dev, "Failed to register LDO%d: %d\n", 13369dc16c3SMark Brown id + 1, ret); 134495fd8c8SMark Brown goto err; 13569dc16c3SMark Brown } 13669dc16c3SMark Brown 13769dc16c3SMark Brown platform_set_drvdata(pdev, ldo); 13869dc16c3SMark Brown 13969dc16c3SMark Brown return 0; 14069dc16c3SMark Brown 14169dc16c3SMark Brown err: 14269dc16c3SMark Brown return ret; 14369dc16c3SMark Brown } 14469dc16c3SMark Brown 14569dc16c3SMark Brown static __devexit int wm8994_ldo_remove(struct platform_device *pdev) 14669dc16c3SMark Brown { 14769dc16c3SMark Brown struct wm8994_ldo *ldo = platform_get_drvdata(pdev); 14869dc16c3SMark Brown 149598b3578SDmitry Torokhov platform_set_drvdata(pdev, NULL); 150598b3578SDmitry Torokhov 15169dc16c3SMark Brown regulator_unregister(ldo->regulator); 15269dc16c3SMark Brown 15369dc16c3SMark Brown return 0; 15469dc16c3SMark Brown } 15569dc16c3SMark Brown 15669dc16c3SMark Brown static struct platform_driver wm8994_ldo_driver = { 15769dc16c3SMark Brown .probe = wm8994_ldo_probe, 15869dc16c3SMark Brown .remove = __devexit_p(wm8994_ldo_remove), 15969dc16c3SMark Brown .driver = { 16069dc16c3SMark Brown .name = "wm8994-ldo", 161598b3578SDmitry Torokhov .owner = THIS_MODULE, 16269dc16c3SMark Brown }, 16369dc16c3SMark Brown }; 16469dc16c3SMark Brown 16569dc16c3SMark Brown static int __init wm8994_ldo_init(void) 16669dc16c3SMark Brown { 16769dc16c3SMark Brown int ret; 16869dc16c3SMark Brown 16969dc16c3SMark Brown ret = platform_driver_register(&wm8994_ldo_driver); 17069dc16c3SMark Brown if (ret != 0) 17169dc16c3SMark Brown pr_err("Failed to register Wm8994 GP LDO driver: %d\n", ret); 17269dc16c3SMark Brown 17369dc16c3SMark Brown return ret; 17469dc16c3SMark Brown } 17569dc16c3SMark Brown subsys_initcall(wm8994_ldo_init); 17669dc16c3SMark Brown 17769dc16c3SMark Brown static void __exit wm8994_ldo_exit(void) 17869dc16c3SMark Brown { 17969dc16c3SMark Brown platform_driver_unregister(&wm8994_ldo_driver); 18069dc16c3SMark Brown } 18169dc16c3SMark Brown module_exit(wm8994_ldo_exit); 18269dc16c3SMark Brown 18369dc16c3SMark Brown /* Module information */ 18469dc16c3SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 18569dc16c3SMark Brown MODULE_DESCRIPTION("WM8994 LDO driver"); 18669dc16c3SMark Brown MODULE_LICENSE("GPL"); 18769dc16c3SMark Brown MODULE_ALIAS("platform:wm8994-ldo"); 188