summaryrefslogtreecommitdiff
path: root/drivers/regulator/wmt-gpio-regulator.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/regulator/wmt-gpio-regulator.c')
-rwxr-xr-xdrivers/regulator/wmt-gpio-regulator.c596
1 files changed, 596 insertions, 0 deletions
diff --git a/drivers/regulator/wmt-gpio-regulator.c b/drivers/regulator/wmt-gpio-regulator.c
new file mode 100755
index 00000000..55302b60
--- /dev/null
+++ b/drivers/regulator/wmt-gpio-regulator.c
@@ -0,0 +1,596 @@
+/*++
+ drivers/regulator/wmt-gpio-regulator.c
+
+ Copyright (c) 2012 WonderMedia Technologies, Inc.
+
+ This program is free software: you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software Foundation,
+ either version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License along with
+ this program. If not, see <http://www.gnu.org/licenses/>.
+
+ WonderMedia Technologies, Inc.
+ 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C.
+--*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/machine.h>
+#include <mach/hardware.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+struct wmt_gpio_pmic_info {
+ /* resource ID, for resource control state machine */
+ u8 id;
+
+ /* regulator specific turn-on delay */
+ u16 delay;
+
+ /* State REMAP default configuration */
+ u8 remap;
+
+ /* chip constraints on regulator behavior */
+ u32 min_uV;
+ u32 max_uV;
+ u32 cur_uvoltage;
+
+ u8 enable;
+ u8 voltage_idx;
+ /* used by regulator core */
+ struct regulator_desc desc;
+};
+DECLARE_WAIT_QUEUE_HEAD(wmt_gpio_pmic_wait);
+static unsigned int ostimer_int_pending = 0;
+static int suspend_stage = 0;
+static int early_suspend_stage = 0;
+#define WMT_VOLTAGE_IDX0 1150000
+#define WMT_VOLTAGE_IDX1 1250000
+#define WMT_VOLTAGE_IDX2 1450000
+
+struct wmt_pmicgpio_set_s {
+ volatile int bitmap;
+ volatile int ctraddr;
+ volatile int ocaddr;
+ volatile int odaddr;
+ volatile int opcaddr;
+ volatile int opdaddr;
+};
+
+struct wmt_gpo_params{
+ unsigned int name_id;
+ unsigned int active;
+ unsigned int bitmap;
+ volatile int ctraddr;
+ volatile int ocaddr;
+ volatile int odaddr;
+ volatile int opcaddr;
+ volatile int opdaddr;
+};
+
+struct wmt_voltage_map{
+ int voltage;
+};
+
+struct wmt_pmic_param_s {
+ unsigned char en_bit;
+ unsigned char *dev_id;
+ unsigned int pin_num;
+ unsigned int map_num;
+ unsigned int up_time;
+ unsigned int down_time;
+ struct wmt_gpo_params *pmic_gpio;
+ struct wmt_voltage_map *voltage_map;
+};
+
+static struct wmt_pmic_param_s g_pmic_param;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static struct early_suspend wmt_gpio_pmic_early_suspend;
+#endif
+
+#ifdef CONFIG_MTD_WMT_SF
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+#endif
+static void init_gpio(void)
+{
+ int i = 0;
+ /*we need to check gpio status to avoid gpio be reset while system was not power down*/
+ for (i = 0; i < g_pmic_param.pin_num; ++i) {
+ if (*(volatile unsigned int *)((g_pmic_param.pmic_gpio)[i].odaddr) & (g_pmic_param.pmic_gpio)[i].bitmap)
+ return;
+ }
+ i = 0;
+ for (i = 0; i < g_pmic_param.pin_num; ++i) {
+ *(volatile unsigned int *)((g_pmic_param.pmic_gpio)[i].odaddr) &= ~((g_pmic_param.pmic_gpio)[i].bitmap);
+ *(volatile unsigned int *)((g_pmic_param.pmic_gpio)[i].ocaddr) |= ((g_pmic_param.pmic_gpio)[i].bitmap);
+ *(volatile unsigned int *)((g_pmic_param.pmic_gpio)[i].ctraddr) |= ((g_pmic_param.pmic_gpio)[i].bitmap);
+ }
+}
+
+/* use ostimer 3
+ * delay_time:us
+ */
+static int set_ostimer_irq(unsigned int delay_time)
+{
+ unsigned int val, sw_count;
+ unsigned int trigger_time;
+ unsigned int remain_time;
+ int ret = 0;
+ val = REG32_VAL(OSTC_ADDR);
+ sw_count = 300000;
+ if ((val&0x02) == 0) {
+ val |= 0x02;
+ REG32_VAL(OSTC_ADDR) = val;
+ }
+ while(1) {
+ val = REG32_VAL(OSTA_ADDR);
+ if ((val&0x20) != 0x20)
+ break;
+ if (--sw_count == 0) {
+ ret = -1;
+ printk(KERN_ERR "Read OST Count Request Failed\n");
+ break;
+ }
+ }
+ val = REG32_VAL(OSCR_ADDR);
+ trigger_time = val;
+ sw_count = 300000;
+ while(1) {
+ val = REG32_VAL(OSTA_ADDR);
+ if ((val&0x08) != 0x08)
+ break;
+ if (--sw_count == 0) {
+ ret = -1;
+ printk(KERN_ERR "OST Match3 Request Failed\n");
+ break;
+ }
+ }
+
+ /*overrun issue*/
+ if ((0xFFFFFFFF - trigger_time < delay_time * 3)) {
+ remain_time = 0xFFFFFFFF - trigger_time;
+ remain_time = delay_time * 3 - remain_time;
+ REG32_VAL(OSM3_ADDR) = remain_time;
+ } else
+ REG32_VAL(OSM3_ADDR) = trigger_time + delay_time * 3;
+ REG32_VAL(OSTS_ADDR) = 0x08;
+ REG32_VAL(OSTI_ADDR) |= 0x08;
+ return ret;
+}
+
+static void clear_ostimer_irq(void)
+{
+ REG32_VAL(OSTS_ADDR) = 0x08;
+ REG32_VAL(OSTI_ADDR) &= ~0x08;
+}
+
+
+/*----------------------------------------------------------------------*/
+
+#if 0
+static int wmt_gpio_pmic_list_voltage(struct regulator_dev *rdev, unsigned uV)
+{
+ int i = 0;
+ int selector;
+ for (i = 0; i < g_pmic_param.map_num; ++i) {
+ if (uV < (g_pmic_param.voltage_map)[i].voltage) {
+ selector = (g_pmic_param.voltage_map)[i].voltage;
+ break;
+ }
+ }
+
+ return selector;
+}
+#endif
+
+static void wmt_set_voltage(int idx, int is_up)
+{
+ int i = 0;
+
+ for (i = 0; i < g_pmic_param.pin_num; ++i) {
+ if (!((*(volatile unsigned int *)(g_pmic_param.pmic_gpio)[i].odaddr) & (g_pmic_param.pmic_gpio)[i].bitmap))
+ *(volatile unsigned int *)(g_pmic_param.pmic_gpio)[i].odaddr |= (g_pmic_param.pmic_gpio)[i].bitmap;
+
+ }
+ for (i = 0; i < g_pmic_param.pin_num; ++i) {
+ if (!(idx & (1 << i)))
+ *(volatile unsigned int *)(g_pmic_param.pmic_gpio)[i].odaddr &= ~(g_pmic_param.pmic_gpio)[i].bitmap;
+ }
+
+ if (early_suspend_stage == 0) {
+ if (is_up == 1) {
+ if (g_pmic_param.up_time > 0) {
+ set_ostimer_irq(g_pmic_param.up_time);
+ wait_event(wmt_gpio_pmic_wait, ostimer_int_pending);
+ }
+ } else {
+ if (g_pmic_param.down_time > 0) {
+ set_ostimer_irq(g_pmic_param.down_time);
+ wait_event(wmt_gpio_pmic_wait, ostimer_int_pending);
+ }
+ }
+ } else {
+ if (is_up == 1) {
+ if (g_pmic_param.up_time > 0) {
+ if (g_pmic_param.up_time < 1000)
+ mdelay(1);
+ else
+ mdelay(g_pmic_param.up_time/1000 + 1);
+ }
+ } else {
+ if (g_pmic_param.down_time > 0) {
+ if (g_pmic_param.down_time < 1000)
+ mdelay(1);
+ else
+ mdelay(g_pmic_param.down_time/1000 + 1);
+ }
+ }
+ }
+ ostimer_int_pending = 0;
+}
+static int
+wmt_gpio_pmic_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
+ unsigned int *selector)
+{
+ struct wmt_gpio_pmic_info *info = rdev_get_drvdata(rdev);
+ int selvol;
+ int is_up = 0;
+ int i = 0;
+
+ if ((min_uV < info->min_uV) || (max_uV > info->max_uV))
+ return -EDOM;
+
+ selvol = min_uV;
+ selector = &selvol;
+ if (suspend_stage == 1)
+ return -EDOM;
+ if (selvol > info->cur_uvoltage)
+ is_up = 1;
+ info->cur_uvoltage = selvol;
+ while (i < g_pmic_param.map_num) {
+ if ((g_pmic_param.voltage_map)[i].voltage == selvol)
+ break;
+ ++i;
+ }
+ wmt_set_voltage(i, is_up);
+ info->voltage_idx = i;
+ return *selector;
+
+}
+
+static int wmt_gpio_pmic_get_voltage(struct regulator_dev *rdev)
+{
+ struct wmt_gpio_pmic_info *info = rdev_get_drvdata(rdev);
+
+ return (g_pmic_param.voltage_map)[info->voltage_idx].voltage;
+}
+
+static int wmt_gpio_pmic_enable(struct regulator_dev *rdev)
+
+{
+ return 0;
+}
+
+static int wmt_gpio_pmic_disable(struct regulator_dev *rdev)
+{
+ return 0;
+}
+static int wmt_gpio_pmic_is_enabled(struct regulator_dev *rdev)
+{
+ struct wmt_gpio_pmic_info *info = rdev_get_drvdata(rdev);
+ if (info->enable == 1)
+ return 0;
+ else
+ return -EDOM;
+ return 0;
+}
+static int wmt_gpio_pmic_set_mode(struct regulator_dev *rdev, unsigned mode)
+{
+ return 0;
+}
+static int wmt_gpio_pmic_get_status(struct regulator_dev *rdev)
+{
+ return 0;
+}
+static struct regulator_ops wmt_gpio_pmicdo_ops = {
+ .list_voltage = NULL,
+
+ .set_voltage = wmt_gpio_pmic_set_voltage,
+ .get_voltage = wmt_gpio_pmic_get_voltage,
+
+ .enable = wmt_gpio_pmic_enable,
+ .disable = wmt_gpio_pmic_disable,
+ .is_enabled = wmt_gpio_pmic_is_enabled,
+
+ .set_mode = wmt_gpio_pmic_set_mode,
+
+ .get_status = wmt_gpio_pmic_get_status,
+};
+
+
+#define WMT_GPIO_PMIC_LABEL 0
+/*----------------------------------------------------------------------*/
+
+#define WMT_GPIO_PMIC_MODE0(label, min_uVolts, max_uVolts, num) { \
+ .min_uV = min_uVolts, \
+ .max_uV = max_uVolts, \
+ .voltage_idx = 0, \
+ .cur_uvoltage = 1150000, \
+ .enable = 1, \
+ .desc = { \
+ .name = #label, \
+ .id = WMT_GPIO_PMIC_LABEL, \
+ .n_voltages = (max_uVolts - min_uVolts), \
+ .ops = &wmt_gpio_pmicdo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ }, \
+ }
+
+static struct wmt_gpio_pmic_info wmt_pmic_regs[] = {
+ WMT_GPIO_PMIC_MODE0(WMT_GPIO_PMIC_LABEL, 1150000, 1450000, 1),
+};
+
+static struct regulator_consumer_supply wmt_corepower__supply =
+ REGULATOR_SUPPLY("wmt_corepower", NULL);
+ /*
+ REGULATOR_SUPPLY("wmt_corepower", "wmt_cpufreq");
+ */
+static struct regulator_init_data wmt_corepower = {
+ .constraints = {
+ .min_uV = 1150000,
+ .max_uV = 1450000,
+ .apply_uV = true,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS
+ | REGULATOR_CHANGE_VOLTAGE,
+ },
+ .num_consumer_supplies = 1,
+ .consumer_supplies = &wmt_corepower__supply,
+};
+
+static struct platform_device wmt_gpio_pmic_device = {
+ .name = "wmt_gpio_pmic",
+ .id = 0,
+ .num_resources = 0,
+ .resource = NULL,
+};
+static irqreturn_t wmt_ostimer_isr(int irq, void *dev_id)
+{
+ clear_ostimer_irq();
+ ostimer_int_pending = 1;
+ wake_up(&wmt_gpio_pmic_wait);
+ return IRQ_HANDLED;
+}
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void wmt_gpio_early_suspend(struct early_suspend *h)
+{
+ early_suspend_stage = 1;
+}
+static void wmt_gpio_late_resume(struct early_suspend *h)
+{
+ early_suspend_stage = 0;
+}
+#endif
+static int __devinit wmt_gpio_pmic_probe(struct platform_device *pdev)
+{
+ int i;
+ struct wmt_gpio_pmic_info *info;
+ struct regulator_init_data *initdata;
+ struct regulation_constraints *c;
+ struct regulator_dev *rdev;
+
+ init_gpio();
+ for (i = 0, info = NULL; i < ARRAY_SIZE(wmt_pmic_regs); i++) {
+ if (wmt_pmic_regs[i].desc.id != pdev->id)
+ continue;
+ info = wmt_pmic_regs + i;
+ break;
+ }
+ if (!info)
+ return -ENODEV;
+
+ initdata = &wmt_corepower;
+
+ c = &initdata->constraints;
+ c->valid_modes_mask &= REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY;
+ c->valid_ops_mask &= REGULATOR_CHANGE_VOLTAGE
+ | REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS;
+ c->always_on = true;
+
+ rdev = regulator_register(&info->desc, &pdev->dev, initdata, info, NULL);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "can't register %s, %ld\n",
+ info->desc.name, PTR_ERR(rdev));
+ return PTR_ERR(rdev);
+ }
+ request_irq(IRQ_OST3, wmt_ostimer_isr,
+ IRQF_DISABLED,
+ "pmic", NULL);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ wmt_gpio_pmic_early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ wmt_gpio_pmic_early_suspend.suspend = wmt_gpio_early_suspend;
+ wmt_gpio_pmic_early_suspend.resume = wmt_gpio_late_resume;
+ register_early_suspend(&wmt_gpio_pmic_early_suspend);
+#endif
+
+ platform_set_drvdata(pdev, rdev);
+
+ return 0;
+}
+
+static int __devexit wmt_gpio_pmic_remove(struct platform_device *pdev)
+{
+ regulator_unregister(platform_get_drvdata(pdev));
+ return 0;
+}
+
+MODULE_ALIAS("platform:wmt_gpio_pmic");
+
+static int wmt_gpio_pmic_suspend(struct platform_device *pdev,
+ pm_message_t mesg)
+{
+ struct regulator_dev *rdev = platform_get_drvdata(pdev);
+ struct wmt_gpio_pmic_info *info = rdev_get_drvdata(rdev);
+ info->enable = 0;
+ suspend_stage = 1;
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ early_suspend_stage = 1;
+#endif
+ return 0;
+}
+
+static int wmt_gpio_pmic_resume(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev = platform_get_drvdata(pdev);
+ struct wmt_gpio_pmic_info *info = rdev_get_drvdata(rdev);
+ init_gpio();
+ mdelay(1);
+ info->enable = 1;
+ suspend_stage = 0;
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ early_suspend_stage = 0;
+#endif
+ return 0;
+}
+
+static struct platform_driver wmt_gpio_pmic_driver = {
+ .probe = wmt_gpio_pmic_probe,
+ .remove = __devexit_p(wmt_gpio_pmic_remove),
+ .suspend = wmt_gpio_pmic_suspend,
+ .resume = wmt_gpio_pmic_resume,
+ .driver.name = "wmt_gpio_pmic",
+ .driver.owner = THIS_MODULE,
+};
+
+#ifdef CONFIG_MTD_WMT_SF
+#define par_len 240
+static int parse_pmic_param(void)
+{
+ int retval;
+ unsigned char buf[par_len];
+ unsigned char tmp_buf[par_len];
+ int varlen = par_len;
+ int i = 0;
+ int j = 0;
+ int k = 0;
+ int t = 0;
+ int vol = 0;
+ int idx = 0;
+ char *varname = "wmt.pmic.param";
+ retval = wmt_getsyspara(varname, buf, &varlen);
+ if (retval != 0)
+ return -1;
+ if (buf[0] == 0) /*disable*/
+ return -1;
+ i += 2;
+ j = 0;
+ for (; i < par_len; ++i) {
+ if (buf[i] != ':')
+ tmp_buf[j] = buf[i];
+ else
+ break;
+ ++j;
+ }
+ if (strncmp(tmp_buf, "gpio", 4))
+ return -1;
+ ++i;
+ /*parse pin_num, map_num, up_time and down_time*/
+ j = sscanf((buf+i), "%d,%d,%d,%d,",
+ &g_pmic_param.pin_num,
+ &g_pmic_param.map_num,
+ &g_pmic_param.up_time,
+ &g_pmic_param.down_time);
+ k = 0;
+ for (j = i; j < par_len; ++j) {
+ if (buf[j] == ',')
+ ++k;
+ if (k > 3) {
+ ++j;
+ break;
+ }
+ }
+ i = j;
+ g_pmic_param.pmic_gpio = kzalloc(g_pmic_param.pin_num * sizeof(struct wmt_pmic_param_s), GFP_KERNEL);
+ g_pmic_param.voltage_map = kzalloc(g_pmic_param.map_num * sizeof(struct wmt_voltage_map), GFP_KERNEL);
+ for (j = 0; j < g_pmic_param.pin_num; ++j) {
+ k = sscanf((buf +i), "[%x:%x:%x:%x:%x:%x:%x:%x]",
+ &(g_pmic_param.pmic_gpio + j)->name_id,
+ &(g_pmic_param.pmic_gpio + j)->active,
+ &(g_pmic_param.pmic_gpio + j)->bitmap,
+ &(g_pmic_param.pmic_gpio + j)->ctraddr,
+ &(g_pmic_param.pmic_gpio + j)->ocaddr,
+ &(g_pmic_param.pmic_gpio + j)->odaddr,
+ &(g_pmic_param.pmic_gpio + j)->opcaddr,
+ &(g_pmic_param.pmic_gpio + j)->opdaddr);
+ g_pmic_param.pmic_gpio[j].ctraddr += WMT_MMAP_OFFSET;
+ g_pmic_param.pmic_gpio[j].ocaddr += WMT_MMAP_OFFSET;
+ g_pmic_param.pmic_gpio[j].odaddr += WMT_MMAP_OFFSET;
+ g_pmic_param.pmic_gpio[j].opcaddr += WMT_MMAP_OFFSET;
+ g_pmic_param.pmic_gpio[j].opdaddr += WMT_MMAP_OFFSET;
+ for (k = i; k < par_len; ++k) {
+ if (buf[k] == ']') {
+ ++k;
+ break;
+ }
+ }
+ i = k;
+ }
+ ++i;/*','*/
+ for (j = 0; j < g_pmic_param.map_num; ++j) {
+ k = sscanf((buf + i), "[%d,%d]",
+ &idx, &vol);
+ t = 0;
+ for (k = i; k < par_len; ++k) {
+ if (buf[k] == ']') {
+ ++k;
+ break;
+ }
+ }
+ i = k;
+ (g_pmic_param.voltage_map + idx)->voltage = vol * 1000;
+ }
+ return 0;
+}
+#endif
+static int __init wmt_gpio_pmic_init(void)
+{
+ int ret = 0;
+#ifdef CONFIG_MTD_WMT_SF
+ ret = parse_pmic_param();
+#else
+ return -ENODEV;
+#endif
+ if (ret != 0)
+ return -ENODEV;
+ ret = platform_device_register(&wmt_gpio_pmic_device);
+ if (ret)
+ printk("wmt-ebm:register device failed\n");
+ return platform_driver_register(&wmt_gpio_pmic_driver);
+}
+subsys_initcall(wmt_gpio_pmic_init);
+
+static void __exit wmt_gpio_pmic_exit(void)
+{
+ platform_driver_unregister(&wmt_gpio_pmic_driver);
+}
+module_exit(wmt_gpio_pmic_exit)
+
+MODULE_AUTHOR("WonderMedia Technologies, Inc.");
+MODULE_DESCRIPTION("WMT GPIO PMIC Driver");
+MODULE_LICENSE("GPL");