139b1772aSDaniel Ribeiro /* 239b1772aSDaniel Ribeiro * PCAP2 Regulator Driver 339b1772aSDaniel Ribeiro * 439b1772aSDaniel Ribeiro * Copyright (c) 2009 Daniel Ribeiro <drwyrm@gmail.com> 539b1772aSDaniel Ribeiro * 639b1772aSDaniel Ribeiro * This program is free software; you can redistribute it and/or modify it 739b1772aSDaniel Ribeiro * under the terms of the GNU General Public License as published by the 839b1772aSDaniel Ribeiro * Free Software Foundation; either version 2 of the License, or (at your 939b1772aSDaniel Ribeiro * option) any later version. 1039b1772aSDaniel Ribeiro */ 1139b1772aSDaniel Ribeiro 1239b1772aSDaniel Ribeiro #include <linux/kernel.h> 1339b1772aSDaniel Ribeiro #include <linux/module.h> 1439b1772aSDaniel Ribeiro #include <linux/init.h> 1539b1772aSDaniel Ribeiro #include <linux/err.h> 1639b1772aSDaniel Ribeiro #include <linux/platform_device.h> 1739b1772aSDaniel Ribeiro #include <linux/regulator/driver.h> 1839b1772aSDaniel Ribeiro #include <linux/regulator/machine.h> 1939b1772aSDaniel Ribeiro #include <linux/mfd/ezx-pcap.h> 2039b1772aSDaniel Ribeiro 2139b1772aSDaniel Ribeiro static const u16 V1_table[] = { 2239b1772aSDaniel Ribeiro 2775, 1275, 1600, 1725, 1825, 1925, 2075, 2275, 2339b1772aSDaniel Ribeiro }; 2439b1772aSDaniel Ribeiro 2539b1772aSDaniel Ribeiro static const u16 V2_table[] = { 2639b1772aSDaniel Ribeiro 2500, 2775, 2739b1772aSDaniel Ribeiro }; 2839b1772aSDaniel Ribeiro 2939b1772aSDaniel Ribeiro static const u16 V3_table[] = { 3039b1772aSDaniel Ribeiro 1075, 1275, 1550, 1725, 1876, 1950, 2075, 2275, 3139b1772aSDaniel Ribeiro }; 3239b1772aSDaniel Ribeiro 3339b1772aSDaniel Ribeiro static const u16 V4_table[] = { 3439b1772aSDaniel Ribeiro 1275, 1550, 1725, 1875, 1950, 2075, 2275, 2775, 3539b1772aSDaniel Ribeiro }; 3639b1772aSDaniel Ribeiro 3739b1772aSDaniel Ribeiro static const u16 V5_table[] = { 3839b1772aSDaniel Ribeiro 1875, 2275, 2475, 2775, 3939b1772aSDaniel Ribeiro }; 4039b1772aSDaniel Ribeiro 4139b1772aSDaniel Ribeiro static const u16 V6_table[] = { 4239b1772aSDaniel Ribeiro 2475, 2775, 4339b1772aSDaniel Ribeiro }; 4439b1772aSDaniel Ribeiro 4539b1772aSDaniel Ribeiro static const u16 V7_table[] = { 4639b1772aSDaniel Ribeiro 1875, 2775, 4739b1772aSDaniel Ribeiro }; 4839b1772aSDaniel Ribeiro 4939b1772aSDaniel Ribeiro #define V8_table V4_table 5039b1772aSDaniel Ribeiro 5139b1772aSDaniel Ribeiro static const u16 V9_table[] = { 5239b1772aSDaniel Ribeiro 1575, 1875, 2475, 2775, 5339b1772aSDaniel Ribeiro }; 5439b1772aSDaniel Ribeiro 5539b1772aSDaniel Ribeiro static const u16 V10_table[] = { 5639b1772aSDaniel Ribeiro 5000, 5739b1772aSDaniel Ribeiro }; 5839b1772aSDaniel Ribeiro 5939b1772aSDaniel Ribeiro static const u16 VAUX1_table[] = { 6039b1772aSDaniel Ribeiro 1875, 2475, 2775, 3000, 6139b1772aSDaniel Ribeiro }; 6239b1772aSDaniel Ribeiro 6339b1772aSDaniel Ribeiro #define VAUX2_table VAUX1_table 6439b1772aSDaniel Ribeiro 6539b1772aSDaniel Ribeiro static const u16 VAUX3_table[] = { 6639b1772aSDaniel Ribeiro 1200, 1200, 1200, 1200, 1400, 1600, 1800, 2000, 6739b1772aSDaniel Ribeiro 2200, 2400, 2600, 2800, 3000, 3200, 3400, 3600, 6839b1772aSDaniel Ribeiro }; 6939b1772aSDaniel Ribeiro 7039b1772aSDaniel Ribeiro static const u16 VAUX4_table[] = { 7139b1772aSDaniel Ribeiro 1800, 1800, 3000, 5000, 7239b1772aSDaniel Ribeiro }; 7339b1772aSDaniel Ribeiro 7439b1772aSDaniel Ribeiro static const u16 VSIM_table[] = { 7539b1772aSDaniel Ribeiro 1875, 3000, 7639b1772aSDaniel Ribeiro }; 7739b1772aSDaniel Ribeiro 7839b1772aSDaniel Ribeiro static const u16 VSIM2_table[] = { 7939b1772aSDaniel Ribeiro 1875, 8039b1772aSDaniel Ribeiro }; 8139b1772aSDaniel Ribeiro 8239b1772aSDaniel Ribeiro static const u16 VVIB_table[] = { 8339b1772aSDaniel Ribeiro 1300, 1800, 2000, 3000, 8439b1772aSDaniel Ribeiro }; 8539b1772aSDaniel Ribeiro 8639b1772aSDaniel Ribeiro static const u16 SW1_table[] = { 8739b1772aSDaniel Ribeiro 900, 950, 1000, 1050, 1100, 1150, 1200, 1250, 8839b1772aSDaniel Ribeiro 1300, 1350, 1400, 1450, 1500, 1600, 1875, 2250, 8939b1772aSDaniel Ribeiro }; 9039b1772aSDaniel Ribeiro 9139b1772aSDaniel Ribeiro #define SW2_table SW1_table 9239b1772aSDaniel Ribeiro 9339b1772aSDaniel Ribeiro static const u16 SW3_table[] = { 9439b1772aSDaniel Ribeiro 4000, 4500, 5000, 5500, 9539b1772aSDaniel Ribeiro }; 9639b1772aSDaniel Ribeiro 9739b1772aSDaniel Ribeiro struct pcap_regulator { 9839b1772aSDaniel Ribeiro const u8 reg; 9939b1772aSDaniel Ribeiro const u8 en; 10039b1772aSDaniel Ribeiro const u8 index; 10139b1772aSDaniel Ribeiro const u8 stby; 10239b1772aSDaniel Ribeiro const u8 lowpwr; 10339b1772aSDaniel Ribeiro const u8 n_voltages; 10439b1772aSDaniel Ribeiro const u16 *voltage_table; 10539b1772aSDaniel Ribeiro }; 10639b1772aSDaniel Ribeiro 10739b1772aSDaniel Ribeiro #define NA 0xff 10839b1772aSDaniel Ribeiro 10939b1772aSDaniel Ribeiro #define VREG_INFO(_vreg, _reg, _en, _index, _stby, _lowpwr) \ 11039b1772aSDaniel Ribeiro [_vreg] = { \ 11139b1772aSDaniel Ribeiro .reg = _reg, \ 11239b1772aSDaniel Ribeiro .en = _en, \ 11339b1772aSDaniel Ribeiro .index = _index, \ 11439b1772aSDaniel Ribeiro .stby = _stby, \ 11539b1772aSDaniel Ribeiro .lowpwr = _lowpwr, \ 11639b1772aSDaniel Ribeiro .n_voltages = ARRAY_SIZE(_vreg##_table), \ 11739b1772aSDaniel Ribeiro .voltage_table = _vreg##_table, \ 11839b1772aSDaniel Ribeiro } 11939b1772aSDaniel Ribeiro 12039b1772aSDaniel Ribeiro static struct pcap_regulator vreg_table[] = { 12139b1772aSDaniel Ribeiro VREG_INFO(V1, PCAP_REG_VREG1, 1, 2, 18, 0), 12239b1772aSDaniel Ribeiro VREG_INFO(V2, PCAP_REG_VREG1, 5, 6, 19, 22), 12339b1772aSDaniel Ribeiro VREG_INFO(V3, PCAP_REG_VREG1, 7, 8, 20, 23), 12439b1772aSDaniel Ribeiro VREG_INFO(V4, PCAP_REG_VREG1, 11, 12, 21, 24), 12539b1772aSDaniel Ribeiro /* V5 STBY and LOWPWR are on PCAP_REG_VREG2 */ 12639b1772aSDaniel Ribeiro VREG_INFO(V5, PCAP_REG_VREG1, 15, 16, 12, 19), 12739b1772aSDaniel Ribeiro 12839b1772aSDaniel Ribeiro VREG_INFO(V6, PCAP_REG_VREG2, 1, 2, 14, 20), 12939b1772aSDaniel Ribeiro VREG_INFO(V7, PCAP_REG_VREG2, 3, 4, 15, 21), 13039b1772aSDaniel Ribeiro VREG_INFO(V8, PCAP_REG_VREG2, 5, 6, 16, 22), 13139b1772aSDaniel Ribeiro VREG_INFO(V9, PCAP_REG_VREG2, 9, 10, 17, 23), 13239b1772aSDaniel Ribeiro VREG_INFO(V10, PCAP_REG_VREG2, 10, NA, 18, 24), 13339b1772aSDaniel Ribeiro 13439b1772aSDaniel Ribeiro VREG_INFO(VAUX1, PCAP_REG_AUXVREG, 1, 2, 22, 23), 13539b1772aSDaniel Ribeiro /* VAUX2 ... VSIM2 STBY and LOWPWR are on PCAP_REG_LOWPWR */ 13639b1772aSDaniel Ribeiro VREG_INFO(VAUX2, PCAP_REG_AUXVREG, 4, 5, 0, 1), 13739b1772aSDaniel Ribeiro VREG_INFO(VAUX3, PCAP_REG_AUXVREG, 7, 8, 2, 3), 13839b1772aSDaniel Ribeiro VREG_INFO(VAUX4, PCAP_REG_AUXVREG, 12, 13, 4, 5), 13939b1772aSDaniel Ribeiro VREG_INFO(VSIM, PCAP_REG_AUXVREG, 17, 18, NA, 6), 14039b1772aSDaniel Ribeiro VREG_INFO(VSIM2, PCAP_REG_AUXVREG, 16, NA, NA, 7), 14139b1772aSDaniel Ribeiro VREG_INFO(VVIB, PCAP_REG_AUXVREG, 19, 20, NA, NA), 14239b1772aSDaniel Ribeiro 14339b1772aSDaniel Ribeiro VREG_INFO(SW1, PCAP_REG_SWCTRL, 1, 2, NA, NA), 14439b1772aSDaniel Ribeiro VREG_INFO(SW2, PCAP_REG_SWCTRL, 6, 7, NA, NA), 14539b1772aSDaniel Ribeiro /* SW3 STBY is on PCAP_REG_AUXVREG */ 14639b1772aSDaniel Ribeiro VREG_INFO(SW3, PCAP_REG_SWCTRL, 11, 12, 24, NA), 14739b1772aSDaniel Ribeiro 14839b1772aSDaniel Ribeiro /* SWxS used to control SWx voltage on standby */ 14939b1772aSDaniel Ribeiro /* VREG_INFO(SW1S, PCAP_REG_LOWPWR, NA, 12, NA, NA), 15039b1772aSDaniel Ribeiro VREG_INFO(SW2S, PCAP_REG_LOWPWR, NA, 20, NA, NA), */ 15139b1772aSDaniel Ribeiro }; 15239b1772aSDaniel Ribeiro 153d5ec9635SAxel Lin static int pcap_regulator_set_voltage_sel(struct regulator_dev *rdev, 154d5ec9635SAxel Lin unsigned selector) 15539b1772aSDaniel Ribeiro { 15639b1772aSDaniel Ribeiro struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)]; 15739b1772aSDaniel Ribeiro void *pcap = rdev_get_drvdata(rdev); 15839b1772aSDaniel Ribeiro 15939b1772aSDaniel Ribeiro /* the regulator doesn't support voltage switching */ 16039b1772aSDaniel Ribeiro if (vreg->n_voltages == 1) 16139b1772aSDaniel Ribeiro return -EINVAL; 16239b1772aSDaniel Ribeiro 16339b1772aSDaniel Ribeiro return ezx_pcap_set_bits(pcap, vreg->reg, 16439b1772aSDaniel Ribeiro (vreg->n_voltages - 1) << vreg->index, 165d5ec9635SAxel Lin selector << vreg->index); 16639b1772aSDaniel Ribeiro } 16739b1772aSDaniel Ribeiro 16839b1772aSDaniel Ribeiro static int pcap_regulator_get_voltage(struct regulator_dev *rdev) 16939b1772aSDaniel Ribeiro { 17039b1772aSDaniel Ribeiro struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)]; 17139b1772aSDaniel Ribeiro void *pcap = rdev_get_drvdata(rdev); 17239b1772aSDaniel Ribeiro u32 tmp; 17339b1772aSDaniel Ribeiro int mV; 17439b1772aSDaniel Ribeiro 17539b1772aSDaniel Ribeiro if (vreg->n_voltages == 1) 17639b1772aSDaniel Ribeiro return vreg->voltage_table[0] * 1000; 17739b1772aSDaniel Ribeiro 17839b1772aSDaniel Ribeiro ezx_pcap_read(pcap, vreg->reg, &tmp); 17939b1772aSDaniel Ribeiro tmp = ((tmp >> vreg->index) & (vreg->n_voltages - 1)); 18039b1772aSDaniel Ribeiro mV = vreg->voltage_table[tmp]; 18139b1772aSDaniel Ribeiro 18239b1772aSDaniel Ribeiro return mV * 1000; 18339b1772aSDaniel Ribeiro } 18439b1772aSDaniel Ribeiro 18539b1772aSDaniel Ribeiro static int pcap_regulator_enable(struct regulator_dev *rdev) 18639b1772aSDaniel Ribeiro { 18739b1772aSDaniel Ribeiro struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)]; 18839b1772aSDaniel Ribeiro void *pcap = rdev_get_drvdata(rdev); 18939b1772aSDaniel Ribeiro 19039b1772aSDaniel Ribeiro if (vreg->en == NA) 19139b1772aSDaniel Ribeiro return -EINVAL; 19239b1772aSDaniel Ribeiro 19339b1772aSDaniel Ribeiro return ezx_pcap_set_bits(pcap, vreg->reg, 1 << vreg->en, 1 << vreg->en); 19439b1772aSDaniel Ribeiro } 19539b1772aSDaniel Ribeiro 19639b1772aSDaniel Ribeiro static int pcap_regulator_disable(struct regulator_dev *rdev) 19739b1772aSDaniel Ribeiro { 19839b1772aSDaniel Ribeiro struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)]; 19939b1772aSDaniel Ribeiro void *pcap = rdev_get_drvdata(rdev); 20039b1772aSDaniel Ribeiro 20139b1772aSDaniel Ribeiro if (vreg->en == NA) 20239b1772aSDaniel Ribeiro return -EINVAL; 20339b1772aSDaniel Ribeiro 20439b1772aSDaniel Ribeiro return ezx_pcap_set_bits(pcap, vreg->reg, 1 << vreg->en, 0); 20539b1772aSDaniel Ribeiro } 20639b1772aSDaniel Ribeiro 20739b1772aSDaniel Ribeiro static int pcap_regulator_is_enabled(struct regulator_dev *rdev) 20839b1772aSDaniel Ribeiro { 20939b1772aSDaniel Ribeiro struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)]; 21039b1772aSDaniel Ribeiro void *pcap = rdev_get_drvdata(rdev); 21139b1772aSDaniel Ribeiro u32 tmp; 21239b1772aSDaniel Ribeiro 21339b1772aSDaniel Ribeiro if (vreg->en == NA) 21439b1772aSDaniel Ribeiro return -EINVAL; 21539b1772aSDaniel Ribeiro 21639b1772aSDaniel Ribeiro ezx_pcap_read(pcap, vreg->reg, &tmp); 21739b1772aSDaniel Ribeiro return (tmp >> vreg->en) & 1; 21839b1772aSDaniel Ribeiro } 21939b1772aSDaniel Ribeiro 22039b1772aSDaniel Ribeiro static int pcap_regulator_list_voltage(struct regulator_dev *rdev, 22139b1772aSDaniel Ribeiro unsigned int index) 22239b1772aSDaniel Ribeiro { 22339b1772aSDaniel Ribeiro struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)]; 22439b1772aSDaniel Ribeiro 22539b1772aSDaniel Ribeiro return vreg->voltage_table[index] * 1000; 22639b1772aSDaniel Ribeiro } 22739b1772aSDaniel Ribeiro 22839b1772aSDaniel Ribeiro static struct regulator_ops pcap_regulator_ops = { 22939b1772aSDaniel Ribeiro .list_voltage = pcap_regulator_list_voltage, 230d5ec9635SAxel Lin .set_voltage_sel = pcap_regulator_set_voltage_sel, 23139b1772aSDaniel Ribeiro .get_voltage = pcap_regulator_get_voltage, 23239b1772aSDaniel Ribeiro .enable = pcap_regulator_enable, 23339b1772aSDaniel Ribeiro .disable = pcap_regulator_disable, 23439b1772aSDaniel Ribeiro .is_enabled = pcap_regulator_is_enabled, 23539b1772aSDaniel Ribeiro }; 23639b1772aSDaniel Ribeiro 23739b1772aSDaniel Ribeiro #define VREG(_vreg) \ 23839b1772aSDaniel Ribeiro [_vreg] = { \ 23939b1772aSDaniel Ribeiro .name = #_vreg, \ 24039b1772aSDaniel Ribeiro .id = _vreg, \ 24139b1772aSDaniel Ribeiro .n_voltages = ARRAY_SIZE(_vreg##_table), \ 24239b1772aSDaniel Ribeiro .ops = &pcap_regulator_ops, \ 24339b1772aSDaniel Ribeiro .type = REGULATOR_VOLTAGE, \ 24439b1772aSDaniel Ribeiro .owner = THIS_MODULE, \ 24539b1772aSDaniel Ribeiro } 24639b1772aSDaniel Ribeiro 24739b1772aSDaniel Ribeiro static struct regulator_desc pcap_regulators[] = { 24839b1772aSDaniel Ribeiro VREG(V1), VREG(V2), VREG(V3), VREG(V4), VREG(V5), VREG(V6), VREG(V7), 24939b1772aSDaniel Ribeiro VREG(V8), VREG(V9), VREG(V10), VREG(VAUX1), VREG(VAUX2), VREG(VAUX3), 25039b1772aSDaniel Ribeiro VREG(VAUX4), VREG(VSIM), VREG(VSIM2), VREG(VVIB), VREG(SW1), VREG(SW2), 25139b1772aSDaniel Ribeiro }; 25239b1772aSDaniel Ribeiro 25339b1772aSDaniel Ribeiro static int __devinit pcap_regulator_probe(struct platform_device *pdev) 25439b1772aSDaniel Ribeiro { 25539b1772aSDaniel Ribeiro struct regulator_dev *rdev; 25670fde5cbSAntonio Ospite void *pcap = dev_get_drvdata(pdev->dev.parent); 25739b1772aSDaniel Ribeiro 25839b1772aSDaniel Ribeiro rdev = regulator_register(&pcap_regulators[pdev->id], &pdev->dev, 2592c043bcbSRajendra Nayak pdev->dev.platform_data, pcap, NULL); 26039b1772aSDaniel Ribeiro if (IS_ERR(rdev)) 26139b1772aSDaniel Ribeiro return PTR_ERR(rdev); 26239b1772aSDaniel Ribeiro 26339b1772aSDaniel Ribeiro platform_set_drvdata(pdev, rdev); 26439b1772aSDaniel Ribeiro 26539b1772aSDaniel Ribeiro return 0; 26639b1772aSDaniel Ribeiro } 26739b1772aSDaniel Ribeiro 26839b1772aSDaniel Ribeiro static int __devexit pcap_regulator_remove(struct platform_device *pdev) 26939b1772aSDaniel Ribeiro { 27039b1772aSDaniel Ribeiro struct regulator_dev *rdev = platform_get_drvdata(pdev); 27139b1772aSDaniel Ribeiro 27239b1772aSDaniel Ribeiro regulator_unregister(rdev); 2736a74857dSDmitry Torokhov platform_set_drvdata(pdev, NULL); 27439b1772aSDaniel Ribeiro 27539b1772aSDaniel Ribeiro return 0; 27639b1772aSDaniel Ribeiro } 27739b1772aSDaniel Ribeiro 27839b1772aSDaniel Ribeiro static struct platform_driver pcap_regulator_driver = { 27939b1772aSDaniel Ribeiro .driver = { 28039b1772aSDaniel Ribeiro .name = "pcap-regulator", 2816a74857dSDmitry Torokhov .owner = THIS_MODULE, 28239b1772aSDaniel Ribeiro }, 28339b1772aSDaniel Ribeiro .probe = pcap_regulator_probe, 28439b1772aSDaniel Ribeiro .remove = __devexit_p(pcap_regulator_remove), 28539b1772aSDaniel Ribeiro }; 28639b1772aSDaniel Ribeiro 28739b1772aSDaniel Ribeiro static int __init pcap_regulator_init(void) 28839b1772aSDaniel Ribeiro { 28939b1772aSDaniel Ribeiro return platform_driver_register(&pcap_regulator_driver); 29039b1772aSDaniel Ribeiro } 29139b1772aSDaniel Ribeiro 29239b1772aSDaniel Ribeiro static void __exit pcap_regulator_exit(void) 29339b1772aSDaniel Ribeiro { 29439b1772aSDaniel Ribeiro platform_driver_unregister(&pcap_regulator_driver); 29539b1772aSDaniel Ribeiro } 29639b1772aSDaniel Ribeiro 297e397e7edSAntonio Ospite subsys_initcall(pcap_regulator_init); 29839b1772aSDaniel Ribeiro module_exit(pcap_regulator_exit); 29939b1772aSDaniel Ribeiro 30039b1772aSDaniel Ribeiro MODULE_AUTHOR("Daniel Ribeiro <drwyrm@gmail.com>"); 30139b1772aSDaniel Ribeiro MODULE_DESCRIPTION("PCAP2 Regulator Driver"); 30239b1772aSDaniel Ribeiro MODULE_LICENSE("GPL"); 303