--- /dev/null
+#include <linux/regulator/machine.h>
+#include <linux/mfd/rk808.h>
+#include <mach/sram.h>
+#include <linux/platform_device.h>
+
+#include <mach/gpio.h>
+#include <mach/iomux.h>
+#include <mach/board.h>
+
+#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
+
+
+
+
#include <linux/sensor-dev.h>
#include <linux/mfd/tps65910.h>
#include <linux/regulator/act8846.h>
+#include <linux/mfd/rk808.h>
#include <linux/regulator/rk29-pwm-regulator.h>
#ifdef CONFIG_CW2015_BATTERY
#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)
.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",
if(pmic_is_act8846())
board_pmu_act8846_suspend();
#endif
+ #if defined (CONFIG_MFD_RK808)
+ if(pmic_is_rk808())
+ board_pmu_rk808_suspend();
+ #endif
}
#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;
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);
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;
#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;
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
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
--- /dev/null
+/*
+ * rk808-irq.c -- TI TPS6591x
+ *
+ * Copyright 2010 Texas Instruments Inc.
+ *
+ * Author: Graeme Gregory <gg@slimlogic.co.uk>
+ * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/mfd/rk808.h>
+#include <linux/wakelock.h>
+#include <linux/kthread.h>
+
+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;
+}
--- /dev/null
+/*
+ * 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<zhangqing@rock-chips.com>
+ *
+ * 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 <linux/bug.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/rk808.h>
+#include <linux/mfd/core.h>
+#include <mach/gpio.h>
+#include <linux/delay.h>
+#include <mach/iomux.h>
+#include <linux/slab.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/interrupt.h>
+
+
+#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; i<count; i++){
+ ret = rk808_reg_read(rk808, reg+i);
+ if(ret < 0){
+ printk("%s: failed read reg 0x%0x, ret = %d\n", __FUNCTION__, reg+i, ret);
+ mutex_unlock(&rk808->io_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; i<count; i++){
+ ret = rk808_reg_write(rk808, reg+i, buf[i]);
+ if(ret < 0){
+ printk("%s: failed write reg=0x%0x, val=0x%0x, ret = %d\n", __FUNCTION__, reg+i, buf[i], ret);
+ mutex_unlock(&rk808->io_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 <zhangqing@rock-chips.com>");
+MODULE_DESCRIPTION("rk808 PMIC driver");
+
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
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
--- /dev/null
+/*
+ * Real Time Clock driver for Wolfson Microelectronics rk808
+ *
+ * Copyright (C) 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/bcd.h>
+#include <linux/interrupt.h>
+#include <linux/ioctl.h>
+#include <linux/completion.h>
+#include <linux/mfd/rk808.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+
+
+/* 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 <zhanqging@rock-chips.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rk808-rtc");
--- /dev/null
+/* 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 <linux/regulator/machine.h>
+#include <linux/wakelock.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#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
+