From b2a79c3e1122a6edb421b8c54723f640c2c09523 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E5=BC=A0=E6=99=B4?= Date: Mon, 25 Mar 2013 11:00:14 +0800 Subject: [PATCH] rk808:support pmu rk808 --- arch/arm/mach-rk30/board-pmu-rk808.c | 538 ++++++++++++ arch/arm/mach-rk30/board-rk3168-tb.c | 115 ++- arch/arm/plat-rk/include/plat/board.h | 2 + drivers/mfd/Kconfig | 9 + drivers/mfd/Makefile | 1 + drivers/mfd/rk808-irq.c | 218 +++++ drivers/mfd/rk808.c | 1134 +++++++++++++++++++++++++ drivers/rtc/Kconfig | 6 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-rk808.c | 833 ++++++++++++++++++ include/linux/mfd/rk808.h | 157 ++++ 11 files changed, 3013 insertions(+), 1 deletion(-) create mode 100755 arch/arm/mach-rk30/board-pmu-rk808.c mode change 100755 => 100644 drivers/mfd/Kconfig mode change 100755 => 100644 drivers/mfd/Makefile create mode 100755 drivers/mfd/rk808-irq.c create mode 100755 drivers/mfd/rk808.c mode change 100755 => 100644 drivers/rtc/Kconfig mode change 100755 => 100644 drivers/rtc/Makefile create mode 100755 drivers/rtc/rtc-rk808.c create mode 100755 include/linux/mfd/rk808.h diff --git a/arch/arm/mach-rk30/board-pmu-rk808.c b/arch/arm/mach-rk30/board-pmu-rk808.c new file mode 100755 index 000000000000..63ab3077ea30 --- /dev/null +++ b/arch/arm/mach-rk30/board-pmu-rk808.c @@ -0,0 +1,538 @@ +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_MFD_RK808 + +static int rk808_pre_init(struct rk808 *rk808) +{ + struct regulator *dcdc; + struct regulator *ldo; + int i = 0; + int ret,val; + printk("%s,line=%d\n", __func__,__LINE__); + /***********set buck 12.5mv/us ************/ + val = rk808_reg_read(rk808,RK808_BUCK1_CONFIG_REG); + val &= (~(0x3 <<3)); + val |= (0x3 <<0); + ret = rk808_reg_write(rk808,RK808_BUCK1_CONFIG_REG,val); + if (ret < 0) { + printk(KERN_ERR "Unable to write RK808_BUCK1_CONFIG_REG reg\n"); + return ret; + } + + val = rk808_reg_read(rk808,RK808_BUCK2_CONFIG_REG); + val &= (~(0x3 <<3)); + val |= (0x3 <<0); + ret = rk808_reg_write(rk808,RK808_BUCK2_CONFIG_REG,val); + if (ret <0) { + printk(KERN_ERR "Unable to write RK808_BUCK2_CONFIG_REG reg\n"); + return ret; + } + /*****************************************/ + + /*******enable switch and boost***********/ + val = rk808_reg_read(rk808,RK808_DCDC_EN_REG); + val |= (0x3 << 5); //enable switch1/2 +// val |= (0x1 << 4); //enable boost + ret = rk808_reg_write(rk808,RK808_DCDC_EN_REG,val); + if (ret <0) { + printk(KERN_ERR "Unable to write RK808_DCDC_EN_REG reg\n"); + return ret; + } + /****************************************/ + /***********set dc1\2 dvs voltge*********/ + val = rk808_reg_read(rk808,RK808_BUCK1_DVS_REG); + val |= 0x34; //set dc1 dvs 1.35v + ret = rk808_reg_write(rk808,RK808_BUCK1_DVS_REG,val); + if (ret <0) { + printk(KERN_ERR "Unable to write RK808_BUCK1_DVS_REG reg\n"); + return ret; + } + + val = rk808_reg_read(rk808,RK808_BUCK2_DVS_REG); + val |= 0x34; //set dc2 dvs 1.35v + ret = rk808_reg_write(rk808,RK808_BUCK2_DVS_REG,val); + if (ret <0) { + printk(KERN_ERR "Unable to write RK808_BUCK2_DVS_REG reg\n"); + return ret; + } + /***********************************/ + + /****************set vbat low **********/ + val = rk808_reg_read(rk808,RK808_VB_MON_REG); + val &= (~(0x7<<0)); //set vbat < 3.5v irq + val &= (~(0x1 <<4)); + val |= (0x7 <<0); + val |= (0x1 << 4); + ret = rk808_reg_write(rk808,RK808_VB_MON_REG,val); + if (ret <0) { + printk(KERN_ERR "Unable to write RK808_VB_MON_REG reg\n"); + return ret; + } + /**************************************/ + /********set dcdc/ldo/switch off when in sleep******/ + val = rk808_reg_read(rk808,RK808_SLEEP_SET_OFF_REG1); +// val |= (0x3<<5); + ret = rk808_reg_write(rk808,RK808_SLEEP_SET_OFF_REG1,val); + if (ret <0) { + printk(KERN_ERR "Unable to write RK808_SLEEP_SET_OFF_REG1 reg\n"); + return ret; + } + val = rk808_reg_read(rk808,RK808_SLEEP_SET_OFF_REG2); +// val |= (0x1<<4); + ret = rk808_reg_write(rk808,RK808_SLEEP_SET_OFF_REG2,val); + if (ret <0) { + printk(KERN_ERR "Unable to write RK808_SLEEP_SET_OFF_REG2 reg\n"); + return ret; + } + /**************************************************/ + /**********mask int****************/ + val = rk808_reg_read(rk808,RK808_INT_STS_MSK_REG1); + val |= (0x1<<0); //mask vout_lo_int + ret = rk808_reg_write(rk808,RK808_INT_STS_MSK_REG1,val); + if (ret <0) { + printk(KERN_ERR "Unable to write RK808_INT_STS_MSK_REG1 reg\n"); + return ret; + } + /**********************************/ + printk("%s,line=%d\n", __func__,__LINE__); +} +static int rk808_set_init(struct rk808 *rk808) +{ + struct regulator *dcdc; + struct regulator *ldo; + int i = 0; + printk("%s,line=%d\n", __func__,__LINE__); + + #ifndef CONFIG_RK_CONFIG + g_pmic_type = PMIC_TYPE_RK808; + #endif + printk("%s:g_pmic_type=%d\n",__func__,g_pmic_type); + + for(i = 0; i < ARRAY_SIZE(rk808_dcdc_info); i++) + { + + if(rk808_dcdc_info[i].min_uv == 0 && rk808_dcdc_info[i].max_uv == 0) + continue; + dcdc =regulator_get(NULL, rk808_dcdc_info[i].name); + regulator_set_voltage(dcdc, rk808_dcdc_info[i].min_uv, rk808_dcdc_info[i].max_uv); + regulator_set_suspend_voltage(dcdc, rk808_dcdc_info[i].suspend_vol); + regulator_enable(dcdc); + printk("%s %s =%duV end\n", __func__,rk808_dcdc_info[i].name, regulator_get_voltage(dcdc)); + regulator_put(dcdc); + udelay(100); + } + + for(i = 0; i < ARRAY_SIZE(rk808_ldo_info); i++) + { + if(rk808_ldo_info[i].min_uv == 0 && rk808_ldo_info[i].max_uv == 0) + continue; + ldo =regulator_get(NULL, rk808_ldo_info[i].name); + + regulator_set_voltage(ldo, rk808_ldo_info[i].min_uv, rk808_ldo_info[i].max_uv); + regulator_set_suspend_voltage(ldo, rk808_ldo_info[i].suspend_vol); + regulator_enable(ldo); + printk("%s %s =%duV end\n", __func__,rk808_ldo_info[i].name, regulator_get_voltage(ldo)); + regulator_put(ldo); + } + + #ifdef CONFIG_RK_CONFIG + if(sram_gpio_init(get_port_config(pmic_slp).gpio, &pmic_sleep) < 0){ + printk(KERN_ERR "sram_gpio_init failed\n"); + return -EINVAL; + } + if(port_output_init(pmic_slp, 0, "pmic_slp") < 0){ + printk(KERN_ERR "port_output_init failed\n"); + return -EINVAL; + } + #else + if(sram_gpio_init(PMU_POWER_SLEEP, &pmic_sleep) < 0){ + printk(KERN_ERR "sram_gpio_init failed\n"); + return -EINVAL; + } + gpio_request(PMU_POWER_SLEEP, "NULL"); + gpio_direction_output(PMU_POWER_SLEEP, GPIO_LOW); + + #endif + + printk("%s,line=%d END\n", __func__,__LINE__); + + + return 0; +} + +static struct regulator_consumer_supply rk808_buck1_supply[] = { + { + .supply = "rk_dcdc1", + }, + + { + .supply = "vdd_cpu", + }, + +}; +static struct regulator_consumer_supply rk808_buck2_supply[] = { + { + .supply = "rk_dcdc2", + }, + + { + .supply = "vdd_core", + }, + +}; +static struct regulator_consumer_supply rk808_buck3_supply[] = { + { + .supply = "rk_dcdc3", + }, +}; + +static struct regulator_consumer_supply rk808_buck4_supply[] = { + { + .supply = "rk_dcdc4", + }, + +}; + +static struct regulator_consumer_supply rk808_ldo1_supply[] = { + { + .supply = "rk_ldo1", + }, +}; +static struct regulator_consumer_supply rk808_ldo2_supply[] = { + { + .supply = "rk_ldo2", + }, +}; + +static struct regulator_consumer_supply rk808_ldo3_supply[] = { + { + .supply = "rk_ldo3", + }, +}; +static struct regulator_consumer_supply rk808_ldo4_supply[] = { + { + .supply = "rk_ldo4", + }, +}; +static struct regulator_consumer_supply rk808_ldo5_supply[] = { + { + .supply = "rk_ldo5", + }, +}; +static struct regulator_consumer_supply rk808_ldo6_supply[] = { + { + .supply = "rk_ldo6", + }, +}; + +static struct regulator_consumer_supply rk808_ldo7_supply[] = { + { + .supply = "rk_ldo7", + }, +}; +static struct regulator_consumer_supply rk808_ldo8_supply[] = { + { + .supply = "rk_ldo8", + }, +}; + +static struct regulator_init_data rk808_buck1 = { + .constraints = { + .name = "RK_DCDC1", + .min_uV = 700000, + .max_uV = 1500000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL, + + }, + .num_consumer_supplies = ARRAY_SIZE(rk808_buck1_supply), + .consumer_supplies = rk808_buck1_supply, +}; + +/* */ +static struct regulator_init_data rk808_buck2 = { + .constraints = { + .name = "RK_DCDC2", + .min_uV = 700000, + .max_uV = 1500000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL, + + }, + .num_consumer_supplies = ARRAY_SIZE(rk808_buck2_supply), + .consumer_supplies = rk808_buck2_supply, +}; + +/* */ +static struct regulator_init_data rk808_buck3 = { + .constraints = { + .name = "RK_DCDC3", + .min_uV = 1000000, + .max_uV = 1800000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL, + + }, + .num_consumer_supplies = ARRAY_SIZE(rk808_buck3_supply), + .consumer_supplies = rk808_buck3_supply, +}; + +static struct regulator_init_data rk808_buck4 = { + .constraints = { + .name = "RK_DCDC4", + .min_uV = 1800000, + .max_uV = 3300000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL, + + }, + .num_consumer_supplies = ARRAY_SIZE(rk808_buck4_supply), + .consumer_supplies = rk808_buck4_supply, +}; + +static struct regulator_init_data rk808_ldo1 = { + .constraints = { + .name = "RK_LDO1", + .min_uV = 1800000, + .max_uV = 3400000, + .apply_uV = 1, + + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL, + + }, + .num_consumer_supplies = ARRAY_SIZE(rk808_ldo1_supply), + .consumer_supplies = rk808_ldo1_supply, +}; + +/* */ +static struct regulator_init_data rk808_ldo2 = { + .constraints = { + .name = "RK_LDO2", + .min_uV = 1800000, + .max_uV = 3400000, + .apply_uV = 1, + + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL, + + }, + .num_consumer_supplies = ARRAY_SIZE(rk808_ldo2_supply), + .consumer_supplies = rk808_ldo2_supply, +}; + +/* */ +static struct regulator_init_data rk808_ldo3 = { + .constraints = { + .name = "RK_LDO3", + .min_uV = 800000, + .max_uV = 2500000, + .apply_uV = 1, + + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL, + + }, + .num_consumer_supplies = ARRAY_SIZE(rk808_ldo3_supply), + .consumer_supplies = rk808_ldo3_supply, +}; + +/* */ +static struct regulator_init_data rk808_ldo4 = { + .constraints = { + .name = "RK_LDO4", + .min_uV = 1800000, + .max_uV = 3400000, + .apply_uV = 1, + + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL, + + }, + .num_consumer_supplies = ARRAY_SIZE(rk808_ldo4_supply), + .consumer_supplies = rk808_ldo4_supply, +}; + +static struct regulator_init_data rk808_ldo5 = { + .constraints = { + .name = "RK_LDO5", + .min_uV = 1800000, + .max_uV = 3400000, + .apply_uV = 1, + + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL, + + }, + .num_consumer_supplies = ARRAY_SIZE(rk808_ldo5_supply), + .consumer_supplies = rk808_ldo5_supply, +}; + +static struct regulator_init_data rk808_ldo6 = { + .constraints = { + .name = "RK_LDO6", + .min_uV = 800000, + .max_uV = 2500000, + .apply_uV = 1, + + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL, + + }, + .num_consumer_supplies = ARRAY_SIZE(rk808_ldo6_supply), + .consumer_supplies = rk808_ldo6_supply, +}; + +static struct regulator_init_data rk808_ldo7 = { + .constraints = { + .name = "RK_LDO7", + .min_uV = 800000, + .max_uV = 2500000, + .apply_uV = 1, + + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL, + + }, + .num_consumer_supplies = ARRAY_SIZE(rk808_ldo7_supply), + .consumer_supplies = rk808_ldo7_supply, +}; + +static struct regulator_init_data rk808_ldo8 = { + .constraints = { + .name = "RK_LDO8", + .min_uV = 1800000, + .max_uV = 3400000, + .apply_uV = 1, + + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL, + + }, + .num_consumer_supplies = ARRAY_SIZE(rk808_ldo8_supply), + .consumer_supplies = rk808_ldo8_supply, +}; + +struct rk808_regulator_subdev rk808_regulator_subdev_id[] = { + { + .id=0, + .initdata=&rk808_buck1, + }, + + { + .id=1, + .initdata=&rk808_buck2, + }, + { + .id=2, + .initdata=&rk808_buck3, + }, + { + .id=3, + .initdata=&rk808_buck4, + }, + + { + .id=4, + .initdata=&rk808_ldo1, + }, + + { + .id=5, + .initdata=&rk808_ldo2, + }, + + { + .id=6, + .initdata=&rk808_ldo3, + }, + + { + .id=7, + .initdata=&rk808_ldo4, + }, + + { + .id=8, + .initdata=&rk808_ldo5, + }, + + { + .id=9, + .initdata=&rk808_ldo6, + }, + + { + .id=10, + .initdata=&rk808_ldo7, + }, + + { + .id=11, + .initdata=&rk808_ldo8, + }, +}; + + struct rk808_platform_data rk808_data={ + .pre_init=rk808_pre_init, + .set_init=rk808_set_init, + .num_regulators=12, + .regulators=rk808_regulator_subdev_id, + .irq = (unsigned)RK808_HOST_IRQ, + .irq_base = IRQ_BOARD_BASE, +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +void rk808_early_suspend(struct early_suspend *h) +{ +} + +void rk808_late_resume(struct early_suspend *h) +{ +} +#endif +#ifdef CONFIG_PM + +void rk808_device_suspend(void) +{ +} +void rk808_device_resume(void) +{ +} +#endif + +void __sramfunc board_pmu_rk808_suspend(void) +{ + #ifdef CONFIG_CLK_SWITCH_TO_32K + sram_gpio_set_value(pmic_sleep, GPIO_HIGH); + while(1); + #endif +} +void __sramfunc board_pmu_rk808_resume(void) +{ + #ifdef CONFIG_CLK_SWITCH_TO_32K + sram_gpio_set_value(pmic_sleep, GPIO_LOW); + sram_32k_udelay(1000); + #endif +} + +#endif + + + + diff --git a/arch/arm/mach-rk30/board-rk3168-tb.c b/arch/arm/mach-rk30/board-rk3168-tb.c index 0d86d61e5b7b..e8994557b924 100755 --- a/arch/arm/mach-rk30/board-rk3168-tb.c +++ b/arch/arm/mach-rk30/board-rk3168-tb.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #ifdef CONFIG_CW2015_BATTERY @@ -1784,6 +1785,92 @@ static struct pmu_info act8846_ldo_info[] = { #include "board-pmu-act8846.c" #endif +#ifdef CONFIG_MFD_RK808 +#define PMU_POWER_SLEEP RK30_PIN0_PA1 +#define RK808_HOST_IRQ RK30_PIN0_PB3 + +static struct pmu_info rk808_dcdc_info[] = { + { + .name = "vdd_cpu", //arm + .min_uv = 1100000, + .max_uv = 1100000, + .suspend_vol = 900000, + }, + { + .name = "vdd_core", //logic + .min_uv = 1100000, + .max_uv = 1100000, + .suspend_vol = 900000, + }, + { + .name = "rk_dcdc3", //ddr + .min_uv = 1200000, + .max_uv = 1200000, + .suspend_vol = 1200000, + }, + { + .name = "rk_dcdc4", //vccio + .min_uv = 3300000, + .max_uv = 3300000, + .suspend_vol = 3000000, + }, + +}; +static struct pmu_info rk808_ldo_info[] = { + { + .name = "rk_ldo1", //vcc33 + .min_uv = 3300000, + .max_uv = 3300000, + .suspend_vol = 3300000, + }, + { + .name = "rk_ldo2", //vcctp + .min_uv = 3300000, + .max_uv = 3300000, + .suspend_vol = 3300000, + + }, + { + .name = "rk_ldo3", //vdd10 + .min_uv = 1000000, + .max_uv = 1000000, + .suspend_vol = 1000000, + }, + { + .name = "rk_ldo4", //vcc18 + .min_uv = 1800000, + .max_uv = 1800000, + .suspend_vol = 1800000, + }, + { + .name = "rk_ldo5", //vcc28_cif + .min_uv = 2800000, + .max_uv = 2800000, + .suspend_vol = 2800000, + }, + { + .name = "rk_ldo6", //vdd12 + .min_uv = 1200000, + .max_uv = 1200000, + .suspend_vol = 1200000, + }, + { + .name = "rk_ldo7", //vcc18_cif + .min_uv = 1800000, + .max_uv = 1800000, + .suspend_vol = 1800000, + }, + { + .name = "rk_ldo8", //vcca_33 + .min_uv = 3300000, + .max_uv = 3300000, + .suspend_vol = 3300000, + }, + }; + +#include "board-pmu-rk808.c" +#endif + static struct i2c_board_info __initdata i2c1_info[] = { #if defined (CONFIG_MFD_WM831X_I2C) @@ -1814,6 +1901,16 @@ static struct i2c_board_info __initdata i2c1_info[] = { .platform_data=&act8846_data, }, #endif +#if defined (CONFIG_MFD_RK808) + { + .type = "rk808", + .addr = 0x1b, + .flags = 0, + // .irq = ACT8846_HOST_IRQ, + .platform_data=&rk808_data, + }, +#endif + #if defined (CONFIG_RTC_HYM8563) { .type = "rtc_hym8563", @@ -1848,6 +1945,10 @@ void __sramfunc board_pmu_suspend(void) if(pmic_is_act8846()) board_pmu_act8846_suspend(); #endif + #if defined (CONFIG_MFD_RK808) + if(pmic_is_rk808()) + board_pmu_rk808_suspend(); + #endif } @@ -1864,7 +1965,12 @@ void __sramfunc board_pmu_resume(void) #if defined (CONFIG_REGULATOR_ACT8846) if(pmic_is_act8846()) board_pmu_act8846_resume(); - #endif + #endif + #if defined (CONFIG_MFD_RK808) + if(pmic_is_rk808()) + board_pmu_rk808_resume(); + #endif + } int __sramdata gpio3d6_iomux,gpio3d6_do,gpio3d6_dir,gpio3d6_en; @@ -2083,6 +2189,13 @@ static void rk30_pm_power_off(void) tps65910_device_shutdown();//tps65910 shutdown } #endif + + #if defined(CONFIG_MFD_RK808) + if(pmic_is_rk808()) + { + rk808_device_shutdown();//rk808 shutdown + } + #endif gpio_direction_output(POWER_ON_PIN, GPIO_LOW); while (1); diff --git a/arch/arm/plat-rk/include/plat/board.h b/arch/arm/plat-rk/include/plat/board.h index 5a7bad299fc0..78cddeea938a 100755 --- a/arch/arm/plat-rk/include/plat/board.h +++ b/arch/arm/plat-rk/include/plat/board.h @@ -89,6 +89,7 @@ enum { PMIC_TYPE_TPS65910 =2, PMIC_TYPE_ACT8931 =3, PMIC_TYPE_ACT8846 =3, + PMIC_TYPE_RK808 =4, PMIC_TYPE_MAX, }; extern __sramdata int g_pmic_type; @@ -96,6 +97,7 @@ extern __sramdata int g_pmic_type; #define pmic_is_tps65910() (g_pmic_type == PMIC_TYPE_TPS65910) #define pmic_is_act8931() (g_pmic_type == PMIC_TYPE_ACT8931) #define pmic_is_act8846() (g_pmic_type == PMIC_TYPE_ACT8846) +#define pmic_is_rk808() (g_pmic_type == PMIC_TYPE_RK808) struct pmu_info { char *name; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig old mode 100755 new mode 100644 index 43c5d038be13..9d7295794c9b --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -298,6 +298,15 @@ config TWL6030_GPADC Say yes here if you want support for the TWL6030 General Purpose A/D Convertor. +config MFD_RK808 + bool "RK808 Power Management chip" + depends on I2C=y + select MFD_CORE + select RTC_RK808 + help + if you say yes here you get support for the RK808 series of + Power Management chips. + config AIC3262_CODEC bool "Support TI Codec Aic3262" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile old mode 100755 new mode 100644 index 59d550bfe573..ef1239130589 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -108,3 +108,4 @@ obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o obj-$(CONFIG_MFD_TPS65910) += tps65910.o tps65910-irq.o obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o obj-$(CONFIG_MFD_RK610) += rk610-core.o +obj-$(CONFIG_MFD_RK808) += rk808.o rk808-irq.o diff --git a/drivers/mfd/rk808-irq.c b/drivers/mfd/rk808-irq.c new file mode 100755 index 000000000000..416ad5de79c7 --- /dev/null +++ b/drivers/mfd/rk808-irq.c @@ -0,0 +1,218 @@ +/* + * rk808-irq.c -- TI TPS6591x + * + * Copyright 2010 Texas Instruments Inc. + * + * Author: Graeme Gregory + * Author: Jorge Eduardo Candelaria + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static inline int irq_to_rk808_irq(struct rk808 *rk808, + int irq) +{ + return (irq - rk808->irq_base); +} + +/* + * This is a threaded IRQ handler so can access I2C/SPI. Since all + * interrupts are clear on read the IRQ line will be reasserted and + * the physical IRQ will be handled again if another interrupt is + * asserted while we run - in the normal course of events this is a + * rare occurrence so we save I2C/SPI reads. We're also assuming that + * it's rare to get lots of interrupts firing simultaneously so try to + * minimise I/O. + */ +static irqreturn_t rk808_irq(int irq, void *irq_data) +{ + struct rk808 *rk808 = irq_data; + u32 irq_sts; + u32 irq_mask; + u8 reg; + int i; + //printk(" rk808 irq %d \n",irq); + wake_lock(&rk808->irq_wake); + rk808_i2c_read(rk808, RK808_INT_STS_REG1, 1, ®); + irq_sts = reg; + rk808_i2c_read(rk808, RK808_INT_STS_REG2, 1, ®); + irq_sts |= reg << 8; + + rk808_i2c_read(rk808, RK808_INT_STS_MSK_REG1, 1, ®); + irq_mask = reg; + rk808_i2c_read(rk808, RK808_INT_STS_MSK_REG2, 1, ®); + irq_mask |= reg << 8; + + irq_sts &= ~irq_mask; + + if (!irq_sts) + { + wake_unlock(&rk808->irq_wake); + return IRQ_NONE; + } + + for (i = 0; i < rk808->irq_num; i++) { + + if (!(irq_sts & (1 << i))) + continue; + + handle_nested_irq(rk808->irq_base + i); + } + + /* Write the STS register back to clear IRQs we handled */ + reg = irq_sts & 0xFF; + irq_sts >>= 8; + rk808_i2c_write(rk808, RK808_INT_STS_REG1, 1, reg); + reg = irq_sts & 0xFF; + rk808_i2c_write(rk808, RK808_INT_STS_REG2, 1, reg); + wake_unlock(&rk808->irq_wake); + return IRQ_HANDLED; +} + +static void rk808_irq_lock(struct irq_data *data) +{ + struct rk808 *rk808 = irq_data_get_irq_chip_data(data); + + mutex_lock(&rk808->irq_lock); +} + +static void rk808_irq_sync_unlock(struct irq_data *data) +{ + struct rk808 *rk808 = irq_data_get_irq_chip_data(data); + u32 reg_mask; + u8 reg; + + rk808_i2c_read(rk808, RK808_INT_STS_MSK_REG1, 1, ®); + reg_mask = reg; + rk808_i2c_read(rk808, RK808_INT_STS_MSK_REG2, 1, ®); + reg_mask |= reg << 8; + + if (rk808->irq_mask != reg_mask) { + reg = rk808->irq_mask & 0xff; +// rk808_i2c_write(rk808, RK808_INT_STS_MSK_REG1, 1, reg); + reg = rk808->irq_mask >> 8 & 0xff; +// rk808_i2c_write(rk808, RK808_INT_STS_MSK_REG2, 1, reg); + } + mutex_unlock(&rk808->irq_lock); +} + +static void rk808_irq_enable(struct irq_data *data) +{ + struct rk808 *rk808 = irq_data_get_irq_chip_data(data); + + rk808->irq_mask &= ~( 1 << irq_to_rk808_irq(rk808, data->irq)); +} + +static void rk808_irq_disable(struct irq_data *data) +{ + struct rk808 *rk808 = irq_data_get_irq_chip_data(data); + + rk808->irq_mask |= ( 1 << irq_to_rk808_irq(rk808, data->irq)); +} + +#ifdef CONFIG_PM_SLEEP +static int rk808_irq_set_wake(struct irq_data *data, unsigned int enable) +{ + struct rk808 *rk808 = irq_data_get_irq_chip_data(data); + return irq_set_irq_wake(rk808->chip_irq, enable); +} +#else +#define rk808_irq_set_wake NULL +#endif + +static struct irq_chip rk808_irq_chip = { + .name = "rk808", + .irq_bus_lock = rk808_irq_lock, + .irq_bus_sync_unlock = rk808_irq_sync_unlock, + .irq_disable = rk808_irq_disable, + .irq_enable = rk808_irq_enable, + .irq_set_wake = rk808_irq_set_wake, +}; + +int rk808_irq_init(struct rk808 *rk808, int irq,struct rk808_platform_data *pdata) +{ + int ret, cur_irq; + int flags = IRQF_ONESHOT; + u8 reg; + + printk("%s,line=%d\n", __func__,__LINE__); + + + if (!irq) { + dev_warn(rk808->dev, "No interrupt support, no core IRQ\n"); + return 0; + } + + if (!pdata || !pdata->irq_base) { + dev_warn(rk808->dev, "No interrupt support, no IRQ base\n"); + return 0; + } + + /* Clear unattended interrupts */ + rk808_i2c_read(rk808, RK808_INT_STS_REG1, 1, ®); + rk808_i2c_write(rk808, RK808_INT_STS_REG1, 1, reg); + rk808_i2c_read(rk808, RK808_INT_STS_REG2, 1, ®); + rk808_i2c_write(rk808, RK808_INT_STS_REG2, 1, reg); + rk808_i2c_read(rk808, RK808_RTC_STATUS_REG, 1, ®); + rk808_i2c_write(rk808, RK808_RTC_STATUS_REG, 1, reg);//clear alarm and timer interrupt + + /* Mask top level interrupts */ + rk808->irq_mask = 0xFFFFFF; + + mutex_init(&rk808->irq_lock); + wake_lock_init(&rk808->irq_wake, WAKE_LOCK_SUSPEND, "rk808_irq_wake"); + rk808->chip_irq = irq; + rk808->irq_base = pdata->irq_base; + + rk808->irq_num = RK808_NUM_IRQ; + + /* Register with genirq */ + for (cur_irq = rk808->irq_base; + cur_irq < rk808->irq_num + rk808->irq_base; + cur_irq++) { + irq_set_chip_data(cur_irq, rk808); + irq_set_chip_and_handler(cur_irq, &rk808_irq_chip, + handle_edge_irq); + irq_set_nested_thread(cur_irq, 1); + + /* ARM needs us to explicitly flag the IRQ as valid + * and will set them noprobe when we do so. */ +#ifdef CONFIG_ARM + set_irq_flags(cur_irq, IRQF_VALID); +#else + irq_set_noprobe(cur_irq); +#endif + } + + ret = request_threaded_irq(irq, NULL, rk808_irq, flags, "rk808", rk808); + + irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW); + + if (ret != 0) + dev_err(rk808->dev, "Failed to request IRQ: %d\n", ret); + + return ret; +} + +int rk808_irq_exit(struct rk808 *rk808) +{ + if (rk808->chip_irq) + free_irq(rk808->chip_irq, rk808); + return 0; +} diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c new file mode 100755 index 000000000000..9d261131bbda --- /dev/null +++ b/drivers/mfd/rk808.c @@ -0,0 +1,1134 @@ +/* + * Regulator driver for Active-semi rk808 PMIC chip for rk29xx + * + * Copyright (C) 2010, 2011 ROCKCHIP, Inc. + + * Based on rk808.c that is work by zhangqing + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#include + + +#if 0 +#define DBG(x...) printk(KERN_INFO x) +#else +#define DBG(x...) +#endif +#if 1 +#define DBG_INFO(x...) printk(KERN_INFO x) +#else +#define DBG_INFO(x...) +#endif +#define PM_CONTROL + +struct rk808 *g_rk808; + +static struct mfd_cell rk808s[] = { + { + .name = "rk808-rtc", + }, +}; + +#define BUCK_VOL_MASK 0x3f +#define LDO_VOL_MASK 0x3f + +#define VOL_MIN_IDX 0x00 +#define VOL_MAX_IDX 0x3f + +const static int buck_set_vol_base_addr[] = { + RK808_BUCK1_ON_REG, + RK808_BUCK2_ON_REG, + RK808_BUCK3_CONFIG_REG, + RK808_BUCK4_ON_REG, +}; +const static int buck_contr_base_addr[] = { + RK808_BUCK1_CONFIG_REG, + RK808_BUCK2_CONFIG_REG, + RK808_BUCK3_CONFIG_REG, + RK808_BUCK4_CONFIG_REG, +}; +#define rk808_BUCK_SET_VOL_REG(x) (buck_set_vol_base_addr[x]) +#define rk808_BUCK_CONTR_REG(x) (buck_contr_base_addr[x]) + + +const static int ldo_set_vol_base_addr[] = { + RK808_LDO1_ON_VSEL_REG, + RK808_LDO2_ON_VSEL_REG, + RK808_LDO3_ON_VSEL_REG, + RK808_LDO4_ON_VSEL_REG, + RK808_LDO5_ON_VSEL_REG, + RK808_LDO6_ON_VSEL_REG, + RK808_LDO7_ON_VSEL_REG, + RK808_LDO8_ON_VSEL_REG, +// RK808_LDO1_ON_VSEL_REG, +}; +/* +const static int ldo_contr_base_addr[] = { + rk808_LDO1_CONTR_BASE, + rk808_LDO2_CONTR_BASE, + rk808_LDO3_CONTR_BASE, + rk808_LDO4_CONTR_BASE, + rk808_LDO5_CONTR_BASE, + rk808_LDO6_CONTR_BASE, + rk808_LDO7_CONTR_BASE, + rk808_LDO8_CONTR_BASE, +// rk808_LDO9_CONTR_BASE, +}; +*/ +#define rk808_LDO_SET_VOL_REG(x) (ldo_set_vol_base_addr[x]) +//#define rk808_LDO_CONTR_REG(x) (ldo_contr_base_addr[x]) + +const static int buck_voltage_map[] = { + 700, 712, 725, 737, 750, 762, 775, 787, 800, + 812, 825, 837, 850,862, 875, 887, 900, 912, + 925, 937, 950, 962, 975, 987, 1000, 1012, 1025, + 1037, 1050,1062, 1075, 1087, 1100, 1112, 1125, 1137, + 1150,1162, 1175, 1187, 1200, 1212, 1225, 1237, 1250, + 1262, 1275, 1287, 1300, 1312, 1325, 1337, 1350,1362, + 1375, 1387, 1400, 1412, 1425, 1437, 1450,1462, 1475, + 1487, 1500, +}; + +const static int buck4_voltage_map[] = { + 1800, 1900, 2000, 2100, 2200, 2300, 2400, 2500, 2600, + 2700, 2800, 2900, 3000, 3100, 3200,3300, +}; + +const static int ldo_voltage_map[] = { + 1800, 1900, 2000, 2100, 2200, 2300, 2400, 2500, 2600, + 2700, 2800, 2900, 3000, 3100, 3200,3300, 3400, +}; +const static int ldo3_voltage_map[] = { + 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600, + 1700, 1800, 1900, 2000,2100, 2200, 2500, +}; +const static int ldo6_voltage_map[] = { + 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600, + 1700, 1800, 1900, 2000,2100, 2200, 2300,2400,2500, +}; + +static int rk808_ldo_list_voltage(struct regulator_dev *dev, unsigned index) +{ + int ldo= rdev_get_id(dev) - RK808_LDO1; + if (ldo == 2){ + if (index >= ARRAY_SIZE(ldo3_voltage_map)) + return -EINVAL; + return 1000 * ldo3_voltage_map[index]; + } + else if (ldo == 5 || ldo ==6){ + if (index >= ARRAY_SIZE(ldo6_voltage_map)) + return -EINVAL; + return 1000 * ldo6_voltage_map[index]; + } + else{ + if (index >= ARRAY_SIZE(ldo_voltage_map)) + return -EINVAL; + return 1000 * ldo_voltage_map[index]; + } +} +static int rk808_ldo_is_enabled(struct regulator_dev *dev) +{ + struct rk808 *rk808 = rdev_get_drvdata(dev); + int ldo= rdev_get_id(dev) - RK808_LDO1; + u16 val; + + val = rk808_reg_read(rk808, RK808_LDO_EN_REG); + if (val < 0) + return val; + if (val & (1 << ldo)) + return 1; + else + return 0; +} +static int rk808_ldo_enable(struct regulator_dev *dev) +{ + struct rk808 *rk808 = rdev_get_drvdata(dev); + int ldo= rdev_get_id(dev) - RK808_LDO1; + + return rk808_set_bits(rk808, RK808_LDO_EN_REG, 1 << ldo, 1 << ldo); + +} +static int rk808_ldo_disable(struct regulator_dev *dev) +{ + struct rk808 *rk808 = rdev_get_drvdata(dev); + int ldo= rdev_get_id(dev) - RK808_LDO1; + + return rk808_set_bits(rk808, RK808_LDO_EN_REG, 1 << ldo, 0); + +} +static int rk808_ldo_get_voltage(struct regulator_dev *dev) +{ + struct rk808 *rk808 = rdev_get_drvdata(dev); + int ldo= rdev_get_id(dev) - RK808_LDO1; + u16 reg = 0; + int val; + reg = rk808_reg_read(rk808,rk808_LDO_SET_VOL_REG(ldo)); + reg &= LDO_VOL_MASK; + if (ldo ==2){ + val = 1000 * ldo3_voltage_map[reg]; + } + else if (ldo == 5 || ldo ==6){ + val = 1000 * ldo6_voltage_map[reg]; + } + else{ + val = 1000 * ldo_voltage_map[reg]; + } + return val; +} +static int rk808_ldo_set_sleep_voltage(struct regulator_dev *dev, + int uV) +{ + struct rk808 *rk808 = rdev_get_drvdata(dev); + int ldo= rdev_get_id(dev) - RK808_LDO1; + const int *vol_map = ldo_voltage_map; + int min_vol = uV / 1000; + u16 val; + int ret = 0,num =0; + + if (ldo ==2){ + vol_map = ldo3_voltage_map; + num = 15; + } + else if (ldo == 5 || ldo ==6){ + vol_map = ldo6_voltage_map; + num = 17; + } + else { + num = 16; + } + + if (min_vol < vol_map[0] || + min_vol > vol_map[num]) + return -EINVAL; + + for (val = 0; val <= num; val++){ + if (vol_map[val] >= min_vol) + break; + } + + ret = rk808_set_bits(rk808, rk808_LDO_SET_VOL_REG(ldo) +0x01, + LDO_VOL_MASK, val); + return ret; +} + +static int rk808_ldo_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV,unsigned *selector) +{ + struct rk808 *rk808 = rdev_get_drvdata(dev); + int ldo= rdev_get_id(dev) - RK808_LDO1; + const int *vol_map; + int min_vol = min_uV / 1000; + u16 val; + int ret = 0,num =0; + + if (ldo ==2){ + vol_map = ldo3_voltage_map; + num = 15; + } + else if (ldo == 5 || ldo ==6){ + vol_map = ldo6_voltage_map; + num = 17; + } + else { + vol_map = ldo_voltage_map; + num = 16; + } + + if (min_vol < vol_map[0] || + min_vol > vol_map[num]) + return -EINVAL; + + for (val = 0; val <= num; val++){ + if (vol_map[val] >= min_vol) + break; + } + + ret = rk808_set_bits(rk808, rk808_LDO_SET_VOL_REG(ldo), + LDO_VOL_MASK, val); + return ret; + +} +static unsigned int rk808_ldo_get_mode(struct regulator_dev *dev) +{ + struct rk808 *rk808 = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev) - RK808_LDO1; + u16 mask = 0x80; + u16 val; + val = rk808_reg_read(rk808, rk808_LDO_SET_VOL_REG(ldo)); + if (val < 0) { + return val; + } + val=val & mask; + if (val== mask) + return REGULATOR_MODE_NORMAL; + else + return REGULATOR_MODE_STANDBY; + +} +static int rk808_ldo_set_mode(struct regulator_dev *dev, unsigned int mode) +{ + struct rk808 *rk808 = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev) - RK808_LDO1; + u16 mask = 0x80; + switch(mode) + { + case REGULATOR_MODE_FAST: + return rk808_set_bits(rk808, rk808_LDO_SET_VOL_REG(ldo), mask, mask); + case REGULATOR_MODE_NORMAL: + return rk808_set_bits(rk808, rk808_LDO_SET_VOL_REG(ldo), mask, 0); + default: + printk("error:pmu_rk808 only lowpower and nomal mode\n"); + return -EINVAL; + } + + +} +static struct regulator_ops rk808_ldo_ops = { + .set_voltage = rk808_ldo_set_voltage, + .get_voltage = rk808_ldo_get_voltage, + .list_voltage = rk808_ldo_list_voltage, + .is_enabled = rk808_ldo_is_enabled, + .enable = rk808_ldo_enable, + .disable = rk808_ldo_disable, + .get_mode = rk808_ldo_get_mode, + .set_mode = rk808_ldo_set_mode, + .set_suspend_voltage = rk808_ldo_set_sleep_voltage, + +}; + +static int rk808_dcdc_list_voltage(struct regulator_dev *dev, unsigned selector) +{ + int volt; + int buck = rdev_get_id(dev) - RK808_DCDC1; + + if (selector < 0x0 ||selector > BUCK_VOL_MASK ) + return -EINVAL; + + switch (buck) { + case 0: + case 1: + volt = 700000 + selector * 12500; + break; + case 3: + volt = 1800000 + selector * 100000; + break; + case 2: + volt = 1200000; + break; + default: + BUG(); + return -EINVAL; + } + + return volt ; +} +static int rk808_dcdc_is_enabled(struct regulator_dev *dev) +{ + struct rk808 *rk808 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - RK808_DCDC1; + u16 val; + + val = rk808_reg_read(rk808, RK808_DCDC_EN_REG); + if (val < 0) + return val; + if (val & (1 << buck)) + return 1; + else + return 0; +} +static int rk808_dcdc_enable(struct regulator_dev *dev) +{ + struct rk808 *rk808 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - RK808_DCDC1; + + return rk808_set_bits(rk808, RK808_DCDC_EN_REG, 1 << buck, 1 << buck); + +} +static int rk808_dcdc_disable(struct regulator_dev *dev) +{ + struct rk808 *rk808 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - RK808_DCDC1; + + return rk808_set_bits(rk808, RK808_DCDC_EN_REG, 1 << buck , 0); +} +static int rk808_dcdc_get_voltage(struct regulator_dev *dev) +{ + struct rk808 *rk808 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - RK808_DCDC1; + u16 reg = 0; + int val; + + reg = rk808_reg_read(rk808,rk808_BUCK_SET_VOL_REG(buck)); + + reg &= BUCK_VOL_MASK; + val = rk808_dcdc_list_voltage(dev,reg); + return val; +} +static int rk808_dcdc_select_min_voltage(struct regulator_dev *dev, + int min_uV, int max_uV ,int buck) +{ + u16 vsel =0; + + if (buck == 0 || buck == 1){ + if (min_uV < 700000) + vsel = 0; + else if (min_uV <= 1500000) + vsel = ((min_uV - 700000) / 12500) ; + else + return -EINVAL; + } + else if (buck ==3){ + if (min_uV < 1800000) + vsel = 0; + else if (min_uV <= 3300000) + vsel = ((min_uV - 1800000) / 100000) ; + else + return -EINVAL; + } + if (rk808_dcdc_list_voltage(dev, vsel) > max_uV) + return -EINVAL; + return vsel; +} + +static int rk808_dcdc_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV,unsigned *selector) +{ + struct rk808 *rk808 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - RK808_DCDC1; + u16 val; + int ret = 0; + + if (buck ==2){ + return 0; + }else{ + val = rk808_dcdc_select_min_voltage(dev,min_uV,max_uV,buck); + + ret = rk808_set_bits(rk808, rk808_BUCK_SET_VOL_REG(buck), BUCK_VOL_MASK, val); + } + return ret; +} +static int rk808_dcdc_set_sleep_voltage(struct regulator_dev *dev, + int uV) +{ + struct rk808 *rk808 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - RK808_DCDC1; + u16 val; + int ret = 0; + + if (buck ==2){ + return 0; + }else{ + val = rk808_dcdc_select_min_voltage(dev,uV,uV,buck); + ret = rk808_set_bits(rk808, (rk808_BUCK_SET_VOL_REG(buck) + 0x01), BUCK_VOL_MASK, val); + } + return ret; +} +static unsigned int rk808_dcdc_get_mode(struct regulator_dev *dev) +{ + struct rk808 *rk808 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - RK808_DCDC1; + u16 mask = 0x80; + u16 val; + val = rk808_reg_read(rk808, rk808_BUCK_SET_VOL_REG(buck)); + if (val < 0) { + return val; + } + val=val & mask; + if (val== mask) + return REGULATOR_MODE_FAST; + else + return REGULATOR_MODE_NORMAL; + +} +static int rk808_dcdc_set_mode(struct regulator_dev *dev, unsigned int mode) +{ + struct rk808 *rk808 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - RK808_DCDC1; + u16 mask = 0x80; + switch(mode) + { + case REGULATOR_MODE_FAST: + return rk808_set_bits(rk808, rk808_BUCK_SET_VOL_REG(buck), mask, mask); + case REGULATOR_MODE_NORMAL: + return rk808_set_bits(rk808, rk808_BUCK_SET_VOL_REG(buck), mask, 0); + default: + printk("error:pmu_rk808 only powersave and pwm mode\n"); + return -EINVAL; + } + +} +static int rk808_dcdc_set_voltage_time_sel(struct regulator_dev *dev, unsigned int old_selector, + unsigned int new_selector) +{ + int old_volt, new_volt; + + old_volt = rk808_dcdc_list_voltage(dev, old_selector); + if (old_volt < 0) + return old_volt; + + new_volt = rk808_dcdc_list_voltage(dev, new_selector); + if (new_volt < 0) + return new_volt; + + return DIV_ROUND_UP(abs(old_volt - new_volt)*2, 2500); +} + +static struct regulator_ops rk808_dcdc_ops = { + .set_voltage = rk808_dcdc_set_voltage, + .get_voltage = rk808_dcdc_get_voltage, + .list_voltage= rk808_dcdc_list_voltage, + .is_enabled = rk808_dcdc_is_enabled, + .enable = rk808_dcdc_enable, + .disable = rk808_dcdc_disable, + .get_mode = rk808_dcdc_get_mode, + .set_mode = rk808_dcdc_set_mode, + .set_suspend_voltage = rk808_dcdc_set_sleep_voltage, + .set_voltage_time_sel = rk808_dcdc_set_voltage_time_sel, +}; +static struct regulator_desc regulators[] = { + + { + .name = "RK_DCDC1", + .id = 0, + .ops = &rk808_dcdc_ops, + .n_voltages = ARRAY_SIZE(buck_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "RK_DCDC2", + .id = 1, + .ops = &rk808_dcdc_ops, + .n_voltages = ARRAY_SIZE(buck_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "RK_DCDC3", + .id = 2, + .ops = &rk808_dcdc_ops, + .n_voltages = ARRAY_SIZE(buck4_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "RK_DCDC4", + .id = 3, + .ops = &rk808_dcdc_ops, + .n_voltages = ARRAY_SIZE(buck4_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + + { + .name = "RK_LDO1", + .id =4, + .ops = &rk808_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "RK_LDO2", + .id = 5, + .ops = &rk808_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "RK_LDO3", + .id = 6, + .ops = &rk808_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo3_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "RK_LDO4", + .id = 7, + .ops = &rk808_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + + { + .name = "RK_LDO5", + .id =8, + .ops = &rk808_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "RK_LDO6", + .id = 9, + .ops = &rk808_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo6_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "RK_LDO7", + .id = 10, + .ops = &rk808_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo6_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "RK_LDO8", + .id = 11, + .ops = &rk808_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + +}; + +/* + * + */ + int rk808_i2c_read(struct rk808 *rk808, char reg, int count,u16 *dest) +{ + struct i2c_client *i2c = rk808->i2c; + + int ret; + struct i2c_adapter *adap; + struct i2c_msg msgs[2]; + + if(!i2c) + return ret; + + if (count != 1) + return -EIO; + + adap = i2c->adapter; + + msgs[0].addr = i2c->addr; + msgs[0].buf = ® + msgs[0].flags = 0; + msgs[0].len = 1; + msgs[0].scl_rate = 200*1000; + + msgs[1].buf = (u8 *)dest; + msgs[1].addr = i2c->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = count; + msgs[1].scl_rate = 200*1000; + + ret = i2c_transfer(adap, msgs, 2); + + DBG("***run in %s %x % x\n",__FUNCTION__,i2c->addr,msgs[0].buf); + return 0; +} + +int rk808_i2c_write(struct rk808 *rk808, char reg, int count, const u16 src) +{ + int ret=-1; + struct i2c_client *i2c = rk808->i2c; + struct i2c_adapter *adap; + struct i2c_msg msg; + char tx_buf[2]; + + if(!i2c) + return ret; + if (count != 1) + return -EIO; + + adap = i2c->adapter; + tx_buf[0] = reg; + tx_buf[1] = src; + + msg.addr = i2c->addr; + msg.buf = &tx_buf[0]; + msg.len = 1 +1; + msg.flags = i2c->flags; + msg.scl_rate = 200*1000; + + ret = i2c_transfer(adap, &msg, 1); + return ret; +} + +u8 rk808_reg_read(struct rk808 *rk808, u8 reg) +{ + u16 val = 0; + + mutex_lock(&rk808->io_lock); + + rk808_i2c_read(rk808, reg, 1, &val); + + DBG("reg read 0x%02x -> 0x%02x\n", (int)reg, (unsigned)val&0xff); + + mutex_unlock(&rk808->io_lock); + + return val & 0xff; +} +EXPORT_SYMBOL_GPL(rk808_reg_read); + +int rk808_reg_write(struct rk808 *rk808, u8 reg, u8 val) +{ + int err =0; + + mutex_lock(&rk808->io_lock); + + err = rk808_i2c_write(rk808, reg, 1,val); + if (err < 0) + dev_err(rk808->dev, "Write for reg 0x%x failed\n", reg); + + mutex_unlock(&rk808->io_lock); + return err; +} +EXPORT_SYMBOL_GPL(rk808_reg_write); + + int rk808_set_bits(struct rk808 *rk808, u8 reg, u16 mask, u16 val) +{ + u16 tmp; + int ret; + + mutex_lock(&rk808->io_lock); + + ret = rk808_i2c_read(rk808, reg, 1, &tmp); + DBG("1 reg read 0x%02x -> 0x%02x\n", (int)reg, (unsigned)tmp&0xff); + tmp = (tmp & ~mask) | val; + if (ret == 0) { + ret = rk808_i2c_write(rk808, reg, 1, tmp); + DBG("reg write 0x%02x -> 0x%02x\n", (int)reg, (unsigned)val&0xff); + } + rk808_i2c_read(rk808, reg, 1, &tmp); + DBG("2 reg read 0x%02x -> 0x%02x\n", (int)reg, (unsigned)tmp&0xff); + mutex_unlock(&rk808->io_lock); + + return 0;//ret; +} +EXPORT_SYMBOL_GPL(rk808_set_bits); + +int rk808_clear_bits(struct rk808 *rk808, u8 reg, u8 mask) +{ + u8 data; + int err; + + mutex_lock(&rk808->io_lock); + err = rk808_i2c_read(rk808, reg, 1, &data); + if (err) { + dev_err(rk808->dev, "read from reg %x failed\n", reg); + goto out; + } + + data &= ~mask; + err = rk808_i2c_write(rk808, reg, 1, &data); + if (err) + dev_err(rk808->dev, "write to reg %x failed\n", reg); + +out: + mutex_unlock(&rk808->io_lock); + return err; +} +EXPORT_SYMBOL_GPL(rk808_clear_bits); + +int rk808_bulk_read(struct rk808 *rk808, u8 reg, + int count, u8 *buf) +{ + int ret; + +//#if defined(CONFIG_MFD_RK610) +#if 0 + int i; //Solve communication conflict when rk610 and 65910 on the same i2c + + mutex_lock(&rk808->io_lock); + for(i=0; iio_lock); + return ret; + }else{ + buf[i] = ret & 0x000000FF; + } + } + mutex_unlock(&rk808->io_lock); +#else + mutex_lock(&rk808->io_lock); + + ret = rk808_i2c_read(rk808, reg, count, buf); + + mutex_unlock(&rk808->io_lock); +#endif + return 0; + +} +EXPORT_SYMBOL_GPL(rk808_bulk_read); + +int rk808_bulk_write(struct rk808 *rk808, u8 reg, + int count, u8 *buf) +{ + int ret; + +//#if defined(CONFIG_MFD_RK610) +#if 0 + int i; // //Solve communication conflict when rk610 and 65910 on the same i2c + + mutex_lock(&rk808->io_lock); + for(i=0; iio_lock); + return ret; + } + } + mutex_unlock(&rk808->io_lock); +#else + mutex_lock(&rk808->io_lock); + + ret = rk808_i2c_write(rk808, reg, count, buf); + + mutex_unlock(&rk808->io_lock); +#endif + return 0; + +} +EXPORT_SYMBOL_GPL(rk808_bulk_write); + + +#if 1 +static ssize_t rk808_test_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n) +{ + u32 getdata[8]; + u16 regAddr; + u8 data; + int ret=0; + char cmd; + const char *buftmp = buf; + struct rk808 *rk808 = g_rk808; + /** + * W Addr(8Bit) regAddr(8Bit) data0(8Bit) data1(8Bit) data2(8Bit) data3(8Bit) + * :data can be less than 4 byte + * R regAddr(8Bit) + * C gpio_name(poweron/powerhold/sleep/boot0/boot1) value(H/L) + */ + regAddr = (u16)(getdata[0] & 0xff); + if (strncmp(buf, "start", 5) == 0) { + + + } else if (strncmp(buf, "stop", 4== 0) ){ + + } else{ + sscanf(buftmp, "%c ", &cmd); + printk("------zhangqing: get cmd = %c\n", cmd); + switch(cmd) { + + case 'w': + sscanf(buftmp, "%c %x %x ", &cmd, &getdata[0],&getdata[1]); + regAddr = (u16)(getdata[0] & 0xff); + data = (u8)(getdata[1] & 0xff); + printk("get value = %x\n", data); + + rk808_i2c_write(rk808, regAddr, 1, data); + rk808_i2c_read(rk808, regAddr, 1, &data); + printk("%x %x\n", getdata[1],data); + + break; + + case 'r': + sscanf(buftmp, "%c %x ", &cmd, &getdata[0]); + printk("CMD : %c %x\n", cmd, getdata[0]); + + regAddr = (u16)(getdata[0] & 0xff); + rk808_i2c_read(rk808, regAddr, 1, &data); + printk("%x %x\n", getdata[0],data); + + break; + + default: + printk("Unknown command\n"); + break; + } +} + return n; + +} +static ssize_t rk808_test_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + char *s = buf; + buf = "hello"; + return sprintf(s, "%s\n", buf); + +} + +static struct kobject *rk808_kobj; +struct rk808_attribute { + struct attribute attr; + ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr, + char *buf); + ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n); +}; + +static struct rk808_attribute rk808_attrs[] = { + /* node_name permision show_func store_func */ + __ATTR(rk808_test, S_IRUGO | S_IWUSR, rk808_test_show, rk808_test_store), +}; +#endif + +static int __devinit setup_regulators(struct rk808 *rk808, struct rk808_platform_data *pdata) +{ + int i, err; + + rk808->num_regulators = pdata->num_regulators; + rk808->rdev = kcalloc(pdata->num_regulators, + sizeof(struct regulator_dev *), GFP_KERNEL); + if (!rk808->rdev) { + return -ENOMEM; + } + /* Instantiate the regulators */ + for (i = 0; i < pdata->num_regulators; i++) { + int id = pdata->regulators[i].id; + rk808->rdev[i] = regulator_register(®ulators[id], + rk808->dev, pdata->regulators[i].initdata, rk808); +/* + if (IS_ERR(rk808->rdev[i])) { + err = PTR_ERR(rk808->rdev[i]); + dev_err(rk808->dev, "regulator init failed: %d\n", + err); + goto error; + }*/ + } + + return 0; +error: + while (--i >= 0) + regulator_unregister(rk808->rdev[i]); + kfree(rk808->rdev); + rk808->rdev = NULL; + return err; +} + +static irqreturn_t rk808_vbat_lo_irq(int irq, void *data) +{ +// rk28_send_wakeup_key(); + printk("rk808 vbat low %s:irq=%d\n",__func__,irq); + rk808_set_bits(g_rk808,0x4c,(0x1 << 1),(0x1 <<1)); + return IRQ_HANDLED; +} + +int rk808_device_shutdown(void) +{ + int ret; + int err = -1; + struct rk808 *rk808 = g_rk808; + + printk("%s\n",__func__); + + ret = rk808_reg_read(rk808,RK808_DEVCTRL_REG); + ret = rk808_set_bits(rk808, RK808_DEVCTRL_REG,(0x1<<0),(0x1<<0)); +// ret = rk808_set_bits(rk808, RK808_DEVCTRL_REG,(0x1<<4),(0x1<<4)); + if (ret < 0) { + printk("rk808 power off error!\n"); + return err; + } + return 0; +} +EXPORT_SYMBOL_GPL(rk808_device_shutdown); + +__weak void rk808_device_suspend(void) {} +__weak void rk808_device_resume(void) {} +#ifdef CONFIG_PM +static int rk808_suspend(struct i2c_client *i2c, pm_message_t mesg) +{ + rk808_device_suspend(); + return 0; +} + +static int rk808_resume(struct i2c_client *i2c) +{ + rk808_device_resume(); + return 0; +} +#else +static int rk808_suspend(struct i2c_client *i2c, pm_message_t mesg) +{ + return 0; +} + +static int rk808_resume(struct i2c_client *i2c) +{ + return 0; +} +#endif + + +#ifdef CONFIG_HAS_EARLYSUSPEND +__weak void rk808_early_suspend(struct early_suspend *h) {} +__weak void rk808_late_resume(struct early_suspend *h) {} +#endif + + +static int __devinit rk808_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + struct rk808 *rk808; + struct rk808_platform_data *pdata = i2c->dev.platform_data; + int ret; + + printk("%s,line=%d\n", __func__,__LINE__); + + rk808 = kzalloc(sizeof(struct rk808), GFP_KERNEL); + if (rk808 == NULL) { + ret = -ENOMEM; + goto err; + } + rk808->i2c = i2c; + rk808->dev = &i2c->dev; + i2c_set_clientdata(i2c, rk808); + mutex_init(&rk808->io_lock); + + ret = mfd_add_devices(rk808->dev, -1, + rk808s, ARRAY_SIZE(rk808s), + NULL, 0); + if (ret < 0) + goto err; + + + ret = rk808_reg_read(rk808,0x2f); + if ((ret < 0) || (ret == 0xff)){ + printk("The device is not rk808\n"); + return 0; + } + + + if (pdata) { + ret = setup_regulators(rk808, pdata); + if (ret < 0) + goto err; + } else + dev_warn(rk808->dev, "No platform init data supplied\n"); + + pdata->pre_init(rk808); + + ret = rk808_irq_init(rk808, pdata->irq, pdata); + if (ret < 0) + goto err; + /********************vbat low int**************/ + ret = request_threaded_irq(rk808->irq_base + RK808_IRQ_VB_LO, NULL, rk808_vbat_lo_irq, + IRQF_TRIGGER_RISING, "rk808_vbatlow", + rk808); + if (ret != 0) { + dev_err(rk808->dev, "Failed to request periodic IRQ %d: %d\n", + rk808->irq_base + RK808_IRQ_VB_LO, ret); + + } + + /*********************************************/ + g_rk808 = rk808; + pdata->set_init(rk808); + + #ifdef CONFIG_HAS_EARLYSUSPEND + rk808->rk808_suspend.suspend = rk808_early_suspend, + rk808->rk808_suspend.resume = rk808_late_resume, + rk808->rk808_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1, + register_early_suspend(&rk808->rk808_suspend); + #endif + + #if 1 + int i =0; + rk808_kobj = kobject_create_and_add("rk808", NULL); + if (!rk808_kobj) + return -ENOMEM; + for (i = 0; i < ARRAY_SIZE(rk808_attrs); i++) { + ret = sysfs_create_file(rk808_kobj, &rk808_attrs[i].attr); + if (ret != 0) { + printk("create index %d error\n", i); + return ret; + } + } + #endif + + return 0; + +err: + return ret; + +} + +static int __devexit rk808_i2c_remove(struct i2c_client *i2c) +{ + struct rk808 *rk808 = i2c_get_clientdata(i2c); + int i; + + for (i = 0; i < rk808->num_regulators; i++) + if (rk808->rdev[i]) + regulator_unregister(rk808->rdev[i]); + kfree(rk808->rdev); + i2c_set_clientdata(i2c, NULL); + kfree(rk808); + + return 0; +} + +static const struct i2c_device_id rk808_i2c_id[] = { + { "rk808", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, rk808_i2c_id); + +static struct i2c_driver rk808_i2c_driver = { + .driver = { + .name = "rk808", + .owner = THIS_MODULE, + }, + .probe = rk808_i2c_probe, + .remove = __devexit_p(rk808_i2c_remove), + .id_table = rk808_i2c_id, + #ifdef CONFIG_PM + .suspend = rk808_suspend, + .resume = rk808_resume, + #endif +}; + +static int __init rk808_module_init(void) +{ + int ret; + ret = i2c_add_driver(&rk808_i2c_driver); + if (ret != 0) + pr_err("Failed to register I2C driver: %d\n", ret); + return ret; +} +//module_init(rk808_module_init); +//subsys_initcall(rk808_module_init); +//rootfs_initcall(rk808_module_init); +subsys_initcall_sync(rk808_module_init); + +static void __exit rk808_module_exit(void) +{ + i2c_del_driver(&rk808_i2c_driver); +} +module_exit(rk808_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("zhangqing "); +MODULE_DESCRIPTION("rk808 PMIC driver"); + diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig old mode 100755 new mode 100644 index 7bd04b684246..62424f04a6c2 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1116,4 +1116,10 @@ config TPS65910_RTC help enable tps65910 rtc for system +config RK808_RTC + tristate "rk808 rtc for rk" + depends on MFD_RK808 + help + enable rk808 rtc for system + endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile old mode 100755 new mode 100644 index 6d4c945e6b42..e429fff538ec --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -116,3 +116,4 @@ obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o obj-$(CONFIG_RTC_HYM8563) += rtc-HYM8563.o obj-$(CONFIG_RTC_M41T66) += rtc-m41t66.o obj-$(CONFIG_TPS65910_RTC) += rtc-tps65910.o +obj-$(CONFIG_RK808_RTC) += rtc-rk808.o diff --git a/drivers/rtc/rtc-rk808.c b/drivers/rtc/rtc-rk808.c new file mode 100755 index 000000000000..9f494e335823 --- /dev/null +++ b/drivers/rtc/rtc-rk808.c @@ -0,0 +1,833 @@ +/* + * Real Time Clock driver for Wolfson Microelectronics rk808 + * + * Copyright (C) 2009 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* RTC Definitions */ +/* RTC_CTRL_REG bitfields */ +#define BIT_RTC_CTRL_REG_STOP_RTC_M 0x01 +#define BIT_RTC_CTRL_REG_ROUND_30S_M 0x02 +#define BIT_RTC_CTRL_REG_AUTO_COMP_M 0x04 +#define BIT_RTC_CTRL_REG_MODE_12_24_M 0x08 +#define BIT_RTC_CTRL_REG_TEST_MODE_M 0x10 +#define BIT_RTC_CTRL_REG_SET_32_COUNTER_M 0x20 +#define BIT_RTC_CTRL_REG_GET_TIME_M 0x40 +#define BIT_RTC_CTRL_REG_RTC_V_OPT_M 0x80 + +/* RTC_STATUS_REG bitfields */ +#define BIT_RTC_STATUS_REG_RUN_M 0x02 +#define BIT_RTC_STATUS_REG_1S_EVENT_M 0x04 +#define BIT_RTC_STATUS_REG_1M_EVENT_M 0x08 +#define BIT_RTC_STATUS_REG_1H_EVENT_M 0x10 +#define BIT_RTC_STATUS_REG_1D_EVENT_M 0x20 +#define BIT_RTC_STATUS_REG_ALARM_M 0x40 +#define BIT_RTC_STATUS_REG_POWER_UP_M 0x80 + +/* RTC_INTERRUPTS_REG bitfields */ +#define BIT_RTC_INTERRUPTS_REG_EVERY_M 0x03 +#define BIT_RTC_INTERRUPTS_REG_IT_TIMER_M 0x04 +#define BIT_RTC_INTERRUPTS_REG_IT_ALARM_M 0x08 + +/* DEVCTRL bitfields */ +#define BIT_RTC_PWDN 0x40 + +/* REG_SECONDS_REG through REG_YEARS_REG is how many registers? */ +#define ALL_TIME_REGS 7 +#define ALL_ALM_REGS 6 + + +#define RTC_SET_TIME_RETRIES 5 +#define RTC_GET_TIME_RETRIES 5 + + +struct rk808_rtc { + struct rk808 *rk808; + struct rtc_device *rtc; + unsigned int alarm_enabled:1; +}; + +/* + * Read current time and date in RTC + */ +static int rk808_rtc_readtime(struct device *dev, struct rtc_time *tm) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + struct rk808 *rk808 = rk808_rtc->rk808; + int ret; + int count = 0; + unsigned char rtc_data[ALL_TIME_REGS + 1]; + u8 rtc_ctl; + + /*Dummy read*/ + ret = rk808_reg_read(rk808, RK808_RTC_CTRL_REG); + + /* Has the RTC been programmed? */ + ret = rk808_reg_read(rk808, RK808_RTC_CTRL_REG); + if (ret < 0) { + dev_err(dev, "Failed to read RTC control: %d\n", ret); + return ret; + } + + rtc_ctl = ret & (~BIT_RTC_CTRL_REG_RTC_V_OPT_M); + + ret = rk808_reg_write(rk808, RK808_RTC_CTRL_REG, rtc_ctl); + if (ret < 0) { + dev_err(dev, "Failed to write RTC control: %d\n", ret); + return ret; + } + +#if 0 + /* Read twice to make sure we don't read a corrupt, partially + * incremented, value. + */ + do { + ret = rk808_bulk_read(rk808, RK808_SECONDS_REG, + ALL_TIME_REGS, rtc_data); + if (ret != 0) + continue; + + tm->tm_sec = bcd2bin(rtc_data[0]); + tm->tm_min = bcd2bin(rtc_data[1]); + tm->tm_hour = bcd2bin(rtc_data[2]) -8; + tm->tm_mday = bcd2bin(rtc_data[3]); + tm->tm_mon = bcd2bin(rtc_data[4]) - 1; + tm->tm_year = bcd2bin(rtc_data[5]) + 100; + tm->tm_wday = bcd2bin(rtc_data[6]); + + printk( "RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, tm->tm_wday, + tm->tm_hour+8, tm->tm_min, tm->tm_sec); + + return ret; + + } while (++count < RTC_GET_TIME_RETRIES); + dev_err(dev, "Timed out reading current time\n"); +#else + rtc_data[0] = rk808_reg_read(rk808,0x00); + rtc_data[1] = rk808_reg_read(rk808,0x01); + rtc_data[2] = rk808_reg_read(rk808,0x02); + rtc_data[3] = rk808_reg_read(rk808,0x03); + rtc_data[4] = rk808_reg_read(rk808,0x04); + rtc_data[5] = rk808_reg_read(rk808,0x05); + rtc_data[6] = rk808_reg_read(rk808,0x06); + + tm->tm_sec = bcd2bin(rtc_data[0]); + tm->tm_min = bcd2bin(rtc_data[1]); + tm->tm_hour = bcd2bin(rtc_data[2]) - 8; + tm->tm_mday = bcd2bin(rtc_data[3]); + tm->tm_mon = bcd2bin(rtc_data[4]) - 1; + tm->tm_year = bcd2bin(rtc_data[5]) + 100; + tm->tm_wday = bcd2bin(rtc_data[6]); + + dev_dbg(dev, "RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, tm->tm_wday,tm->tm_hour + 8, tm->tm_min, tm->tm_sec); + +#endif + return 0; + +} + +/* + * Set current time and date in RTC + */ +static int rk808_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + struct rk808 *rk808 = rk808_rtc->rk808; + int ret; + u8 rtc_ctl; + unsigned char rtc_data[ALL_TIME_REGS + 1]; + + rtc_data[0] = bin2bcd(tm->tm_sec); + rtc_data[1] = bin2bcd(tm->tm_min); + rtc_data[2] = bin2bcd(tm->tm_hour + 8); + rtc_data[3] = bin2bcd(tm->tm_mday); + rtc_data[4] = bin2bcd(tm->tm_mon + 1); + rtc_data[5] = bin2bcd(tm->tm_year - 100); + rtc_data[6] = bin2bcd(tm->tm_wday); + + dev_dbg(dev, "set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, tm->tm_wday,tm->tm_hour + 8, tm->tm_min, tm->tm_sec); + + /*Dummy read*/ + ret = rk808_reg_read(rk808, RK808_RTC_CTRL_REG); + + /* Stop RTC while updating the TC registers */ + ret = rk808_reg_read(rk808, RK808_RTC_CTRL_REG); + if (ret < 0) { + dev_err(dev, "Failed to read RTC control: %d\n", ret); + return ret; + } + + rtc_ctl = ret | (BIT_RTC_CTRL_REG_STOP_RTC_M); + + ret = rk808_reg_write(rk808, RK808_RTC_CTRL_REG, rtc_ctl); + if (ret < 0) { + dev_err(dev, "Failed to write RTC control: %d\n", ret); + return ret; + } +#if 0 + /* update all the time registers in one shot */ + ret = rk808_bulk_write(rk808, RK808_SECONDS_REG, + ALL_TIME_REGS, rtc_data); + if (ret < 0) { + dev_err(dev, "Failed to read RTC times: %d\n", ret); + return ret; + } +#else + rk808_reg_write(rk808,0x00,rtc_data[0]); + rk808_reg_write(rk808,0x01,rtc_data[1]); + rk808_reg_write(rk808,0x02,rtc_data[2]); + rk808_reg_write(rk808,0x03,rtc_data[3]); + rk808_reg_write(rk808,0x04,rtc_data[4]); + rk808_reg_write(rk808,0x05,rtc_data[5]); + rk808_reg_write(rk808,0x06,rtc_data[6]); + +#endif + /*Dummy read*/ + ret = rk808_reg_read(rk808, RK808_RTC_CTRL_REG); + + /* Start RTC again */ + ret = rk808_reg_read(rk808, RK808_RTC_CTRL_REG); + if (ret < 0) { + dev_err(dev, "Failed to read RTC control: %d\n", ret); + return ret; + } + + rtc_ctl = ret &(~ BIT_RTC_CTRL_REG_STOP_RTC_M); + + ret = rk808_reg_write(rk808, RK808_RTC_CTRL_REG, rtc_ctl); + if (ret < 0) { + dev_err(dev, "Failed to write RTC control: %d\n", ret); + return ret; + } + + return 0; +} + +/* + * Read alarm time and date in RTC + */ +static int rk808_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + int ret; + unsigned char alrm_data[ALL_ALM_REGS + 1]; +#if 0 + ret = rk808_bulk_read(rk808_rtc->rk808, RK808_ALARM_SECONDS_REG, + ALL_ALM_REGS, alrm_data); + if (ret != 0) { + dev_err(dev, "Failed to read alarm time: %d\n", ret); + return ret; + } +#else + alrm_data[0] = rk808_reg_read(rk808_rtc->rk808,0x08); + alrm_data[1] = rk808_reg_read(rk808_rtc->rk808,0x09); + alrm_data[2] = rk808_reg_read(rk808_rtc->rk808,0x0a); + alrm_data[3] = rk808_reg_read(rk808_rtc->rk808,0x0b); + alrm_data[4] = rk808_reg_read(rk808_rtc->rk808,0x0c); + alrm_data[5] = rk808_reg_read(rk808_rtc->rk808,0x0d); + + +#endif + /* some of these fields may be wildcard/"match all" */ + alrm->time.tm_sec = bcd2bin(alrm_data[0]); + alrm->time.tm_min = bcd2bin(alrm_data[1]); + alrm->time.tm_hour = bcd2bin(alrm_data[2]) -8; + alrm->time.tm_mday = bcd2bin(alrm_data[3]); + alrm->time.tm_mon = bcd2bin(alrm_data[4]) - 1; + alrm->time.tm_year = bcd2bin(alrm_data[5]) + 100; + + ret = rk808_reg_read(rk808_rtc->rk808, RK808_RTC_INT_REG); + if (ret < 0) { + dev_err(dev, "Failed to read RTC control: %d\n", ret); + return ret; + } + dev_dbg(dev,"alrm read RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + alrm->time.tm_year, alrm->time.tm_mon + 1, alrm->time.tm_mday, alrm->time.tm_wday, alrm->time.tm_hour +8, alrm->time.tm_min, alrm->time.tm_sec); + + + + if (ret & BIT_RTC_INTERRUPTS_REG_IT_ALARM_M) + alrm->enabled = 1; + else + alrm->enabled = 0; + + return 0; +} + +static int rk808_rtc_stop_alarm(struct rk808_rtc *rk808_rtc) +{ + rk808_rtc->alarm_enabled = 0; + + return rk808_clear_bits(rk808_rtc->rk808, RK808_RTC_INT_REG, + BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); + +} + +static int rk808_rtc_start_alarm(struct rk808_rtc *rk808_rtc) +{ + rk808_rtc->alarm_enabled = 1; + + return rk808_set_bits(rk808_rtc->rk808, RK808_RTC_INT_REG, + BIT_RTC_INTERRUPTS_REG_IT_ALARM_M,BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); + +} + +static int rk808_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + int ret; + unsigned char alrm_data[ALL_TIME_REGS + 1]; + + ret = rk808_rtc_stop_alarm(rk808_rtc); + if (ret < 0) { + dev_err(dev, "Failed to stop alarm: %d\n", ret); + return ret; + } + + printk("alrm set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + alrm->time.tm_year, alrm->time.tm_mon + 1, alrm->time.tm_mday, alrm->time.tm_wday, alrm->time.tm_hour +8, alrm->time.tm_min, alrm->time.tm_sec); + + alrm_data[0] = bin2bcd(alrm->time.tm_sec); + alrm_data[1] = bin2bcd(alrm->time.tm_min); + alrm_data[2] = bin2bcd(alrm->time.tm_hour + 8); + alrm_data[3] = bin2bcd(alrm->time.tm_mday); + alrm_data[4] = bin2bcd(alrm->time.tm_mon + 1); + alrm_data[5] = bin2bcd(alrm->time.tm_year - 100); +#if 0 + ret = rk808_bulk_write(rk808_rtc->rk808, RK808_ALARM_SECONDS_REG, + ALL_ALM_REGS, alrm_data); + if (ret != 0) { + dev_err(dev, "Failed to read alarm time: %d\n", ret); + return ret; + } +#else + rk808_reg_write(rk808_rtc->rk808,0x08,alrm_data[0]); + rk808_reg_write(rk808_rtc->rk808,0x09,alrm_data[1]); + rk808_reg_write(rk808_rtc->rk808,0x0a,alrm_data[2]); + rk808_reg_write(rk808_rtc->rk808,0x0b,alrm_data[3]); + rk808_reg_write(rk808_rtc->rk808,0x0c,alrm_data[4]); + rk808_reg_write(rk808_rtc->rk808,0x0d,alrm_data[5]); + +#endif + if (alrm->enabled) { + ret = rk808_rtc_start_alarm(rk808_rtc); + if (ret < 0) { + dev_err(dev, "Failed to start alarm: %d\n", ret); + return ret; + } + } + + return 0; +} + +static int rk808_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + + if (enabled) + return rk808_rtc_start_alarm(rk808_rtc); + else + return rk808_rtc_stop_alarm(rk808_rtc); +} + +static int rk808_rtc_update_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + + if (enabled) + return rk808_set_bits(rk808_rtc->rk808, RK808_RTC_INT_REG, + BIT_RTC_INTERRUPTS_REG_IT_TIMER_M,BIT_RTC_INTERRUPTS_REG_IT_TIMER_M); + else + return rk808_clear_bits(rk808_rtc->rk808, RK808_RTC_INT_REG, + BIT_RTC_INTERRUPTS_REG_IT_TIMER_M); +} + +/* + * We will just handle setting the frequency and make use the framework for + * reading the periodic interupts. + * + * @freq: Current periodic IRQ freq: + * bit 0: every second + * bit 1: every minute + * bit 2: every hour + * bit 3: every day + */ +static int rk808_rtc_irq_set_freq(struct device *dev, int freq) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + int ret; + u8 rtc_ctl; + + if (freq < 0 || freq > 3) + return -EINVAL; + + ret = rk808_reg_read(rk808_rtc->rk808, RK808_RTC_INT_REG); + if (ret < 0) { + dev_err(dev, "Failed to read RTC interrupt: %d\n", ret); + return ret; + } + + rtc_ctl = ret | freq; + + ret = rk808_reg_write(rk808_rtc->rk808, RK808_RTC_INT_REG, rtc_ctl); + if (ret < 0) { + dev_err(dev, "Failed to write RTC control: %d\n", ret); + return ret; + } + + return ret; +} + +static irqreturn_t rk808_alm_irq(int irq, void *data) +{ + struct rk808_rtc *rk808_rtc = data; + int ret; + u8 rtc_ctl; + + /*Dummy read -- mandatory for status register*/ + ret = rk808_reg_read(rk808_rtc->rk808, RK808_RTC_STATUS_REG); + if (ret < 0) { + printk("%s:Failed to read RTC status: %d\n", __func__, ret); + return ret; + } + + ret = rk808_reg_read(rk808_rtc->rk808, RK808_RTC_STATUS_REG); + if (ret < 0) { + printk("%s:Failed to read RTC status: %d\n", __func__, ret); + return ret; + } + rtc_ctl = ret&0xff; + + //The alarm interrupt keeps its low level, until the micro-controller write 1 in the ALARM bit of the RTC_STATUS_REG register. + ret = rk808_reg_write(rk808_rtc->rk808, RK808_RTC_STATUS_REG,rtc_ctl); + if (ret < 0) { + printk("%s:Failed to read RTC status: %d\n", __func__, ret); + return ret; + } + + rtc_update_irq(rk808_rtc->rtc, 1, RTC_IRQF | RTC_AF); + + printk("%s:irq=%d,rtc_ctl=0x%x\n",__func__,irq,rtc_ctl); + return IRQ_HANDLED; +} + +static irqreturn_t rk808_per_irq(int irq, void *data) +{ + struct rk808_rtc *rk808_rtc = data; + + rtc_update_irq(rk808_rtc->rtc, 1, RTC_IRQF | RTC_UF); + + //printk("%s:irq=%d\n",__func__,irq); + return IRQ_HANDLED; +} + +static const struct rtc_class_ops rk808_rtc_ops = { + .read_time = rk808_rtc_readtime, + //.set_mmss = rk808_rtc_set_mmss, + .set_time = rk808_rtc_set_time, + .read_alarm = rk808_rtc_readalarm, + .set_alarm = rk808_rtc_setalarm, + .alarm_irq_enable = rk808_rtc_alarm_irq_enable, + //.update_irq_enable = rk808_rtc_update_irq_enable, + //.irq_set_freq = rk808_rtc_irq_set_freq, +}; + +#ifdef CONFIG_PM +/* Turn off the alarm if it should not be a wake source. */ +static int rk808_rtc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rk808_rtc *rk808_rtc = dev_get_drvdata(&pdev->dev); + int ret; + + if (rk808_rtc->alarm_enabled && device_may_wakeup(&pdev->dev)) + ret = rk808_rtc_start_alarm(rk808_rtc); + else + ret = rk808_rtc_stop_alarm(rk808_rtc); + + if (ret != 0) + dev_err(&pdev->dev, "Failed to update RTC alarm: %d\n", ret); + + return 0; +} + +/* Enable the alarm if it should be enabled (in case it was disabled to + * prevent use as a wake source). + */ +static int rk808_rtc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rk808_rtc *rk808_rtc = dev_get_drvdata(&pdev->dev); + int ret; + + if (rk808_rtc->alarm_enabled) { + ret = rk808_rtc_start_alarm(rk808_rtc); + if (ret != 0) + dev_err(&pdev->dev, + "Failed to restart RTC alarm: %d\n", ret); + } + + return 0; +} + +/* Unconditionally disable the alarm */ +static int rk808_rtc_freeze(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rk808_rtc *rk808_rtc = dev_get_drvdata(&pdev->dev); + int ret; + + ret = rk808_rtc_stop_alarm(rk808_rtc); + if (ret != 0) + dev_err(&pdev->dev, "Failed to stop RTC alarm: %d\n", ret); + + return 0; +} +#else +#define rk808_rtc_suspend NULL +#define rk808_rtc_resume NULL +#define rk808_rtc_freeze NULL +#endif + +struct platform_device *rk808_pdev; +static int rk808_rtc_probe(struct platform_device *pdev) +{ + struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent); + struct rk808_rtc *rk808_rtc; + int per_irq; + int alm_irq; + int ret = 0; + u8 rtc_ctl; + + printk("%s,line=%d\n", __func__,__LINE__); + + + struct rtc_time tm; + struct rtc_time tm_def = { // 2012.1.1 12:00:00 Saturday + .tm_wday = 6, + .tm_year = 112, + .tm_mon = 0, + .tm_mday = 1, + .tm_hour = 12, + .tm_min = 0, + .tm_sec = 0, + }; + + rk808_rtc = kzalloc(sizeof(*rk808_rtc), GFP_KERNEL); + if (rk808_rtc == NULL) + return -ENOMEM; + + platform_set_drvdata(pdev, rk808_rtc); + rk808_rtc->rk808 = rk808; + per_irq = rk808->irq_base + RK808_IRQ_RTC_PERIOD; + alm_irq = rk808->irq_base + RK808_IRQ_RTC_ALARM; + + /* Take rtc out of reset */ + /* + ret = rk808_reg_read(rk808, RK808_DEVCTRL); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to read RK808_DEVCTRL: %d\n", ret); + return ret; + } + + if(ret & BIT_RTC_PWDN) + { + rtc_ctl = ret & (~BIT_RTC_PWDN); + + ret = rk808_reg_write(rk808, RK808_DEVCTRL, rtc_ctl); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to write RTC control: %d\n", ret); + return ret; + } + } + */ + /*start rtc default*/ + ret = rk808_reg_read(rk808, RK808_RTC_CTRL_REG); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to read RTC control: %d\n", ret); + return ret; + } + rtc_ctl = ret & (~BIT_RTC_CTRL_REG_STOP_RTC_M); + + ret = rk808_reg_write(rk808, RK808_RTC_CTRL_REG, rtc_ctl); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to write RTC control: %d\n", ret); + return ret; + } + + ret = rk808_reg_read(rk808, RK808_RTC_STATUS_REG); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to read RTC status: %d\n", ret); + return ret; + } + rk808_reg_write(rk808,RK808_RTC_STATUS_REG,0xfe); + /*set init time*/ + + ret = rk808_rtc_readtime(&pdev->dev, &tm); + if (ret<0) + { + dev_err(&pdev->dev, "Failed to read RTC time\n"); + return ret; + } + + ret = rtc_valid_tm(&tm); + if (ret) { + dev_err(&pdev->dev,"invalid date/time and init time\n"); + rk808_rtc_set_time(&pdev->dev, &tm_def); // 2012-01-01 12:00:00 +// DBG( "set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",1900 + tm_def.tm_year, tm_def.tm_mon + 1, tm_def.tm_mday, tm_def.tm_wday,tm_def.tm_hour, tm_def.tm_min, tm_def.tm_sec); + } + + device_init_wakeup(&pdev->dev, 1); + + rk808_rtc->rtc = rtc_device_register("rk808", &pdev->dev, + &rk808_rtc_ops, THIS_MODULE); + if (IS_ERR(rk808_rtc->rtc)) { + ret = PTR_ERR(rk808_rtc->rtc); + goto err; + } + + /*request rtc and alarm irq of rk808*/ + ret = request_threaded_irq(per_irq, NULL, rk808_per_irq, + IRQF_TRIGGER_RISING, "RTC period", + rk808_rtc); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request periodic IRQ %d: %d\n", + per_irq, ret); + } + + ret = request_threaded_irq(alm_irq, NULL, rk808_alm_irq, + IRQF_TRIGGER_RISING, "RTC alarm", + rk808_rtc); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n", + alm_irq, ret); + } + + //for rtc irq test + rk808_set_bits(rk808_rtc->rk808, RK808_RTC_STATUS_REG,(0x1<< 6),(0x1 <<6)); + rk808_set_bits(rk808_rtc->rk808, RK808_RTC_INT_REG,0x0c,0x0c); + rk808_set_bits(rk808_rtc->rk808,RK808_INT_STS_REG1,(0x3 << 5),(0x3 <<5)); + rk808_set_bits(rk808_rtc->rk808, RK808_INT_STS_MSK_REG1,(0x3 <<5),0); + + + enable_irq_wake(alm_irq); // so rk808 alarm irq can wake up system + rk808_pdev = pdev; + + printk("%s:ok\n",__func__); + + return 0; + +err: + kfree(rk808_rtc); + return ret; +} + +static int __devexit rk808_rtc_remove(struct platform_device *pdev) +{ + struct rk808_rtc *rk808_rtc = platform_get_drvdata(pdev); + int per_irq = rk808_rtc->rk808->irq_base + RK808_IRQ_RTC_PERIOD; + int alm_irq = rk808_rtc->rk808->irq_base + RK808_IRQ_RTC_ALARM; + + free_irq(alm_irq, rk808_rtc); + free_irq(per_irq, rk808_rtc); + rtc_device_unregister(rk808_rtc->rtc); + kfree(rk808_rtc); + + return 0; +} + +static const struct dev_pm_ops rk808_rtc_pm_ops = { + .suspend = rk808_rtc_suspend, + .resume = rk808_rtc_resume, + + .freeze = rk808_rtc_freeze, + .thaw = rk808_rtc_resume, + .restore = rk808_rtc_resume, + + .poweroff = rk808_rtc_suspend, +}; + +static struct platform_driver rk808_rtc_driver = { + .probe = rk808_rtc_probe, + .remove = __devexit_p(rk808_rtc_remove), + .driver = { + .name = "rk808-rtc", + .pm = &rk808_rtc_pm_ops, + }, +}; + +static ssize_t rtc_rk808_test_write(struct file *file, + const char __user *buf, size_t count, loff_t *offset) +{ + char nr_buf[8]; + int nr = 0, ret; + struct platform_device *pdev; + struct rtc_time tm; + struct rtc_wkalrm alrm; + struct rk808_rtc *rk808_rtc; + + if(count > 3) + return -EFAULT; + ret = copy_from_user(nr_buf, buf, count); + if(ret < 0) + return -EFAULT; + + sscanf(nr_buf, "%d", &nr); + if(nr > 5 || nr < 0) + { + printk("%s:data is error\n",__func__); + return -EFAULT; + } + + if(!rk808_pdev) + return -EFAULT; + else + pdev = rk808_pdev; + + + rk808_rtc = dev_get_drvdata(&pdev->dev); + + //test rtc time + if(nr == 0) + { + tm.tm_wday = 6; + tm.tm_year = 111; + tm.tm_mon = 0; + tm.tm_mday = 1; + tm.tm_hour = 12; + tm.tm_min = 0; + tm.tm_sec = 0; + + ret = rk808_rtc_set_time(&pdev->dev, &tm); // 2011-01-01 12:00:00 + if (ret) + { + dev_err(&pdev->dev, "Failed to set RTC time\n"); + return -EFAULT; + } + + } + + /*set init time*/ + ret = rk808_rtc_readtime(&pdev->dev, &tm); + if (ret) + dev_err(&pdev->dev, "Failed to read RTC time\n"); + else + dev_info(&pdev->dev, "RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + tm.tm_year, tm.tm_mon + 1, tm.tm_mday, tm.tm_wday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + + if(!ret) + printk("%s:ok\n",__func__); + else + printk("%s:error\n",__func__); + + + //test rtc alarm + if(nr == 2) + { + //2000-01-01 00:00:30 + if(tm.tm_sec < 30) + { + alrm.time.tm_sec = tm.tm_sec+30; + alrm.time.tm_min = tm.tm_min; + } + else + { + alrm.time.tm_sec = tm.tm_sec-30; + alrm.time.tm_min = tm.tm_min+1; + } + alrm.time.tm_hour = tm.tm_hour; + alrm.time.tm_mday = tm.tm_mday; + alrm.time.tm_mon = tm.tm_mon; + alrm.time.tm_year = tm.tm_year; + rk808_rtc_alarm_irq_enable(&pdev->dev, 1); + rk808_rtc_setalarm(&pdev->dev, &alrm); + + dev_info(&pdev->dev, "Set alarm %4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + alrm.time.tm_year, alrm.time.tm_mon + 1, alrm.time.tm_mday, alrm.time.tm_wday, + alrm.time.tm_hour, alrm.time.tm_min, alrm.time.tm_sec); + } + + + if(nr == 3) + { + ret = rk808_reg_read(rk808_rtc->rk808, RK808_RTC_STATUS_REG); + if (ret < 0) { + printk("%s:Failed to read RTC status: %d\n", __func__, ret); + return ret; + } + printk("%s:ret=0x%x\n",__func__,ret&0xff); + + ret = rk808_reg_write(rk808_rtc->rk808, RK808_RTC_STATUS_REG, ret&0xff); + if (ret < 0) { + printk("%s:Failed to read RTC status: %d\n", __func__, ret); + return ret; + } + } + + if(nr == 4) + rk808_rtc_update_irq_enable(&pdev->dev, 1); + + if(nr == 5) + rk808_rtc_update_irq_enable(&pdev->dev, 0); + + return count; +} + +static const struct file_operations rtc_rk808_test_fops = { + .write = rtc_rk808_test_write, +}; + +static struct miscdevice rtc_rk808_test_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "rtc_rk808_test", + .fops = &rtc_rk808_test_fops, +}; + + +static int __init rk808_rtc_init(void) +{ + misc_register(&rtc_rk808_test_misc); + return platform_driver_register(&rk808_rtc_driver); +} +subsys_initcall_sync(rk808_rtc_init); + +static void __exit rk808_rtc_exit(void) +{ + misc_deregister(&rtc_rk808_test_misc); + platform_driver_unregister(&rk808_rtc_driver); +} +module_exit(rk808_rtc_exit); + +MODULE_DESCRIPTION("RTC driver for the rk808 series PMICs"); +MODULE_AUTHOR("ZHANGQING "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rk808-rtc"); diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h new file mode 100755 index 000000000000..aff110a6f527 --- /dev/null +++ b/include/linux/mfd/rk808.h @@ -0,0 +1,157 @@ +/* include/linux/regulator/rk808.h + * + * Copyright (C) 2011 ROCKCHIP, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#ifndef __LINUX_REGULATOR_rk808_H +#define __LINUX_REGULATOR_rk808_H + +#include +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif + +//#define RK808_START 30 + +#define RK808_DCDC1 0 //(0+RK808_START) + +#define RK808_LDO1 4 //(4+RK808_START) + +#define RK808_SECONDS_REG 0x00 +#define RK808_MINUTES_REG 0x01 +#define RK808_HOURS_REG 0x02 +#define RK808_DAYS_REG 0x03 +#define RK808_MONTHS_REG 0x04 +#define RK808_YEARS_REG 0x05 +#define RK808_WEEKS_REG 0x06 +#define RK808_ALARM_SECONDS_REG 0x07 +#define RK808_ALARM_MINUTES_REG 0x08 +#define RK808_ALARM_HOURS_REG 0x09 +#define RK808_ALARM_DAYS_REG 0x0a +#define RK808_ALARM_MONTHS_REG 0x0b +#define RK808_ALARM_YEARS_REG 0x0c +#define RK808_RTC_CTRL_REG 0x10 +#define RK808_RTC_STATUS_REG 0x11 +#define RK808_RTC_INT_REG 0x12 +#define RK808_RTC_COMP_LSB_REG 0x13 +#define RK808_RTC_COMP_MSB_REG 0x14 +#define RK808_CLK32OUT_REG 0x20 +#define RK808_VB_MON_REG 0x21 +#define RK808_THERMAL_REG 0x22 +#define RK808_DCDC_EN_REG 0x23 +#define RK808_LDO_EN_REG 0x24 +#define RK808_SLEEP_SET_OFF_REG1 0x25 +#define RK808_SLEEP_SET_OFF_REG2 0x26 +#define RK808_DCDC_UV_STS_REG 0x27 +#define RK808_DCDC_UV_ACT_REG 0x28 +#define RK808_LDO_UV_STS_REG 0x29 +#define RK808_LDO_UV_ACT_REG 0x2a +#define RK808_DCDC_PG_REG 0x2b +#define RK808_LDO_PG_REG 0x2c +#define RK808_VOUT_MON_TDB_REG 0x2d +#define RK808_BUCK1_CONFIG_REG 0x2e +#define RK808_BUCK1_ON_REG 0x2f +#define RK808_BUCK1_SLP_REG 0x30 +#define RK808_BUCK1_DVS_REG 0x31 +#define RK808_BUCK2_CONFIG_REG 0x32 +#define RK808_BUCK2_ON_REG 0x33 +#define RK808_BUCK2_SLP_REG 0x34 +#define RK808_BUCK2_DVS_REG 0x35 +#define RK808_BUCK3_CONFIG_REG 0x36 +#define RK808_BUCK4_CONFIG_REG 0x37 +#define RK808_BUCK4_ON_REG 0x38 +#define RK808_BUCK4_SLP_VSEL_REG 0x39 +#define RK808_BOOST_CONFIG_REG 0x3a +#define RK808_LDO1_ON_VSEL_REG 0x3b +#define RK808_LDO1_SLP_VSEL_REG 0x3c +#define RK808_LDO2_ON_VSEL_REG 0x3d +#define RK808_LDO2_SLP_VSEL_REG 0x3e +#define RK808_LDO3_ON_VSEL_REG 0x3f +#define RK808_LDO3_SLP_VSEL_REG 0x40 +#define RK808_LDO4_ON_VSEL_REG 0x41 +#define RK808_LDO4_SLP_VSEL_REG 0x42 +#define RK808_LDO5_ON_VSEL_REG 0x43 +#define RK808_LDO5_SLP_VSEL_REG 0x44 +#define RK808_LDO6_ON_VSEL_REG 0x45 +#define RK808_LDO6_SLP_VSEL_REG 0x46 +#define RK808_LDO7_ON_VSEL_REG 0x47 +#define RK808_LDO7_SLP_VSEL_REG 0x48 +#define RK808_LDO8_ON_VSEL_REG 0x49 +#define RK808_LDO8_SLP_VSEL_REG 0x4a +#define RK808_DEVCTRL_REG 0x4b +#define RK808_INT_STS_REG1 0X4c +#define RK808_INT_STS_MSK_REG1 0X4d +#define RK808_INT_STS_REG2 0X4e +#define RK808_INT_STS_MSK_REG2 0X4d +#define RK808_IO_POL_REG 0X50 + +/* IRQ Definitions */ +#define RK808_IRQ_VOUT_LO 0 +#define RK808_IRQ_VB_LO 1 +#define RK808_IRQ_PWRON 2 +#define RK808_IRQ_PWRON_LP 3 +#define RK808_IRQ_HOTDIE 4 +#define RK808_IRQ_RTC_ALARM 5 +#define RK808_IRQ_RTC_PERIOD 6 + +#define RK808_NUM_IRQ 9 + +#define rk808_NUM_REGULATORS 12 +struct rk808; + +struct rk808_regulator_subdev { + int id; + struct regulator_init_data *initdata; +}; + +struct rk808 { + struct device *dev; + struct mutex io_lock; + struct i2c_client *i2c; + int num_regulators; + struct regulator_dev **rdev; + struct wake_lock irq_wake; + struct early_suspend rk808_suspend; + struct mutex irq_lock; + int irq_base; + int irq_num; + int chip_irq; + u32 irq_mask; +}; + +struct rk808_platform_data { + int num_regulators; + int (*pre_init)(struct rk808 *rk808); + int (*set_init)(struct rk808 *rk808); + struct rk808_regulator_subdev *regulators; + int irq; + int irq_base; +}; + +int rk808_irq_init(struct rk808 *rk808, int irq,struct rk808_platform_data *pdata); + int rk808_i2c_read(struct rk808 *rk808, char reg, int count,u16 *dest); +//int rk808_i2c_read(struct i2c_client *i2c, char reg, int count,u16 *dest); +// int rk808_i2c_read(struct rk808 *rk808 , u8 reg, int bytes,void *dest); +int rk808_i2c_write(struct rk808 *rk808, char reg, int count, const u16 src); +int rk808_set_bits(struct rk808 *rk808, u8 reg, u16 mask, u16 val); +int rk808_clear_bits(struct rk808 *rk808, u8 reg, u8 mask); +u8 rk808_reg_read(struct rk808 *rk808, u8 reg); +int rk808_reg_write(struct rk808 *rk808, u8 reg, u8 val); +int rk808_bulk_read(struct rk808 *rk808, u8 reg, + int count, u8 *buf); +int rk808_bulk_write(struct rk808 *rk808, u8 reg, + int count, u8 *buf); +int rk808_device_shutdown(void); + +#endif + -- 2.34.1