/*++ 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 . WonderMedia Technologies, Inc. 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. --*/ #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_HAS_EARLYSUSPEND #include #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");