From e35e3fe4e468d393d84967ebc27a10cdd0eb844d Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E5=BC=A0=E6=99=B4?= Date: Sat, 27 Apr 2013 15:45:44 +0800 Subject: [PATCH] ricoh619:support pmic ricoh619 --- arch/arm/mach-rk30/board-pmu-ricoh619.c | 556 +++++ arch/arm/mach-rk30/board-rk3168-tb.c | 185 +- arch/arm/plat-rk/include/plat/board.h | 2 + drivers/input/misc/Kconfig | 10 + drivers/input/misc/Makefile | 2 + drivers/input/misc/ricoh619-pwrkey.c | 332 +++ drivers/mfd/Kconfig | 12 + drivers/mfd/Makefile | 1 + drivers/mfd/ricoh619-irq.c | 534 ++++ drivers/mfd/ricoh619.c | 852 +++++++ drivers/power/Kconfig | 7 + drivers/power/Makefile | 2 +- drivers/power/ricoh619-battery.c | 2338 ++++++++++++++++++ drivers/regulator/Kconfig | 7 + drivers/regulator/Makefile | 1 + drivers/regulator/ricoh619-regulator.c | 578 +++++ drivers/rtc/Kconfig | 10 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-ricoh619.c | 413 ++++ include/linux/mfd/ricoh619.h | 354 +++ include/linux/power/ricoh619_battery.h | 121 + include/linux/power/ricoh61x_battery_init.h | 54 + include/linux/regulator/ricoh619-regulator.h | 72 + include/linux/rtc/rtc-ricoh619.h | 57 + 24 files changed, 6468 insertions(+), 33 deletions(-) create mode 100644 arch/arm/mach-rk30/board-pmu-ricoh619.c mode change 100755 => 100644 arch/arm/mach-rk30/board-rk3168-tb.c mode change 100755 => 100644 drivers/input/misc/Kconfig mode change 100755 => 100644 drivers/input/misc/Makefile create mode 100644 drivers/input/misc/ricoh619-pwrkey.c create mode 100644 drivers/mfd/ricoh619-irq.c create mode 100644 drivers/mfd/ricoh619.c mode change 100755 => 100644 drivers/power/Kconfig create mode 100644 drivers/power/ricoh619-battery.c mode change 100755 => 100644 drivers/regulator/Kconfig mode change 100755 => 100644 drivers/regulator/Makefile create mode 100644 drivers/regulator/ricoh619-regulator.c create mode 100644 drivers/rtc/rtc-ricoh619.c create mode 100644 include/linux/mfd/ricoh619.h create mode 100644 include/linux/power/ricoh619_battery.h create mode 100644 include/linux/power/ricoh61x_battery_init.h create mode 100755 include/linux/regulator/ricoh619-regulator.h create mode 100755 include/linux/rtc/rtc-ricoh619.h diff --git a/arch/arm/mach-rk30/board-pmu-ricoh619.c b/arch/arm/mach-rk30/board-pmu-ricoh619.c new file mode 100644 index 000000000000..bc9ffb402956 --- /dev/null +++ b/arch/arm/mach-rk30/board-pmu-ricoh619.c @@ -0,0 +1,556 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_MFD_RICOH619 + +static struct ricoh619 *Ricoh619; +static int ricoh619_pre_init(struct ricoh619 *ricoh619){ + + Ricoh619 = ricoh619; + printk("%s,line=%d\n", __func__,__LINE__); + uint8_t cont; + int ret; + + ret = ricoh619_clr_bits(ricoh619->dev,RICOH619_PWR_REP_CNT,(1 << 0)); //set restart when power off + + /**********set dcdc mode when in sleep mode **************/ + + /*****************************************************/ + + return 0; + } +static int ricoh619_post_init(struct ricoh619 *ricoh619) +{ + struct regulator *dcdc; + struct regulator *ldo; + int i = 0,ret=0; + printk("%s,line=%d\n", __func__,__LINE__); + + #ifndef CONFIG_RK_CONFIG + g_pmic_type = PMIC_TYPE_RICOH619; + #endif + printk("%s:g_pmic_type=%d\n",__func__,g_pmic_type); + + for(i = 0; i < ARRAY_SIZE(ricoh619_dcdc_info); i++) + { + + if(ricoh619_dcdc_info[i].min_uv == 0 && ricoh619_dcdc_info[i].max_uv == 0) + continue; + dcdc =regulator_get(NULL, ricoh619_dcdc_info[i].name); + regulator_set_voltage(dcdc, ricoh619_dcdc_info[i].min_uv, ricoh619_dcdc_info[i].max_uv); + regulator_set_suspend_voltage(dcdc, ricoh619_dcdc_info[i].suspend_vol); + regulator_set_mode(dcdc, REGULATOR_MODE_NORMAL); + regulator_enable(dcdc); + printk("%s %s =%duV end\n", __func__,ricoh619_dcdc_info[i].name, regulator_get_voltage(dcdc)); + regulator_put(dcdc); + udelay(100); + } + + for(i = 0; i < ARRAY_SIZE(ricoh619_ldo_info); i++) + { + if(ricoh619_ldo_info[i].min_uv == 0 && ricoh619_ldo_info[i].max_uv == 0) + continue; + ldo =regulator_get(NULL, ricoh619_ldo_info[i].name); + regulator_set_voltage(ldo, ricoh619_ldo_info[i].min_uv, ricoh619_ldo_info[i].max_uv); + regulator_enable(ldo); + printk("%s %s =%duV end\n", __func__,ricoh619_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 + + ret = ricoh619_clr_bits(ricoh619->dev,0xb1,(7<< 0)); //set vbatdec voltage 3.0v + ret = ricoh619_set_bits(ricoh619->dev,0xb1,(3<< 0)); //set vbatdec voltage 3.0v + + printk("%s,line=%d END\n", __func__,__LINE__); + + return 0; +} +static struct regulator_consumer_supply ricoh619_dcdc1_supply[] = { + { + .supply = "ricoh_dc1", + }, + { + .supply = "vdd_cpu", + }, + +}; +static struct regulator_consumer_supply ricoh619_dcdc2_supply[] = { + { + .supply = "ricoh_dc2", + }, + { + .supply = "vdd_core", + }, + +}; +static struct regulator_consumer_supply ricoh619_dcdc3_supply[] = { + { + .supply = "ricoh_dc3", + }, +}; + +static struct regulator_consumer_supply ricoh619_dcdc4_supply[] = { + { + .supply = "ricoh_dc4", + }, +}; + +static struct regulator_consumer_supply ricoh619_dcdc5_supply[] = { + { + .supply = "ricoh_dc5", + }, +}; + +static struct regulator_consumer_supply ricoh619_ldo1_supply[] = { + { + .supply = "ricoh_ldo1", + }, +}; +static struct regulator_consumer_supply ricoh619_ldo2_supply[] = { + { + .supply = "ricoh_ldo2", + }, +}; + +static struct regulator_consumer_supply ricoh619_ldo3_supply[] = { + { + .supply = "ricoh_ldo3", + }, +}; +static struct regulator_consumer_supply ricoh619_ldo4_supply[] = { + { + .supply = "ricoh_ldo4", + }, +}; +static struct regulator_consumer_supply ricoh619_ldo5_supply[] = { + { + .supply = "ricoh_ldo5", + }, +}; +static struct regulator_consumer_supply ricoh619_ldo6_supply[] = { + { + .supply = "ricoh_ldo6", + }, +}; +static struct regulator_consumer_supply ricoh619_ldo7_supply[] = { + { + .supply = "ricoh_ldo7", + }, +}; +static struct regulator_consumer_supply ricoh619_ldo8_supply[] = { + { + .supply = "ricoh_ldo8", + }, +}; +static struct regulator_consumer_supply ricoh619_ldo9_supply[] = { + { + .supply = "ricoh_ldo9", + }, +}; +static struct regulator_consumer_supply ricoh619_ldo10_supply[] = { + { + .supply = "ricoh_ldo10", + }, +}; +static struct regulator_consumer_supply ricoh619_ldortc1_supply[] = { + { + .supply = "ricoh_ldortc1", + }, +}; + +static struct regulator_init_data ricoh619_dcdc1 = { + .constraints = { + .name = "RICOH_DC1", + .min_uV = 600000, + .max_uV = 3500000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL | REGULATOR_MODE_FAST, + + }, + .num_consumer_supplies = ARRAY_SIZE(ricoh619_dcdc1_supply), + .consumer_supplies = ricoh619_dcdc1_supply, +}; + +/* */ +static struct regulator_init_data ricoh619_dcdc2 = { + .constraints = { + .name = "RICOH_DC2", + .min_uV = 600000, + .max_uV = 3500000, + .apply_uV = 1, + + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL | REGULATOR_MODE_FAST, + + }, + .num_consumer_supplies = ARRAY_SIZE(ricoh619_dcdc2_supply), + .consumer_supplies = ricoh619_dcdc2_supply, +}; + +/* */ +static struct regulator_init_data ricoh619_dcdc3 = { + .constraints = { + .name = "RICOH_DC3", + .min_uV = 600000, + .max_uV = 3500000, + .apply_uV = 1, + + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL | REGULATOR_MODE_FAST, + + }, + .num_consumer_supplies = ARRAY_SIZE(ricoh619_dcdc3_supply), + .consumer_supplies = ricoh619_dcdc3_supply, +}; + +static struct regulator_init_data ricoh619_dcdc4 = { + .constraints = { + .name = "RICOH_DC4", + .min_uV = 600000, + .max_uV = 3500000, + .apply_uV = 1, + + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL | REGULATOR_MODE_FAST, + + }, + .num_consumer_supplies = ARRAY_SIZE(ricoh619_dcdc4_supply), + .consumer_supplies = ricoh619_dcdc4_supply, +}; +static struct regulator_init_data ricoh619_dcdc5 = { + .constraints = { + .name = "RICOH_DC5", + .min_uV = 600000, + .max_uV = 3500000, + .apply_uV = 1, + + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL | REGULATOR_MODE_FAST, + + }, + .num_consumer_supplies = ARRAY_SIZE(ricoh619_dcdc5_supply), + .consumer_supplies = ricoh619_dcdc5_supply, +}; + +static struct regulator_init_data ricoh619_ldo1 = { + .constraints = { + .name = "RICOH_LDO1", + .min_uV = 900000, + .max_uV = 3500000, + .apply_uV = 1, + + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL, + + }, + .num_consumer_supplies = ARRAY_SIZE(ricoh619_ldo1_supply), + .consumer_supplies = ricoh619_ldo1_supply, +}; + +/* */ +static struct regulator_init_data ricoh619_ldo2 = { + .constraints = { + .name = "RICOH_LDO2", + .min_uV = 900000, + .max_uV = 3500000, + .apply_uV = 1, + + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL, + + }, + .num_consumer_supplies = ARRAY_SIZE(ricoh619_ldo2_supply), + .consumer_supplies = ricoh619_ldo2_supply, +}; + +/* */ +static struct regulator_init_data ricoh619_ldo3 = { + .constraints = { + .name = "RICOH_LDO3", + .min_uV = 900000, + .max_uV = 3500000, + .apply_uV = 1, + + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL, + + }, + .num_consumer_supplies = ARRAY_SIZE(ricoh619_ldo3_supply), + .consumer_supplies = ricoh619_ldo3_supply, +}; + +/* */ +static struct regulator_init_data ricoh619_ldo4 = { + .constraints = { + .name = "RICOH_LDO4", + .min_uV = 900000, + .max_uV = 3500000, + .apply_uV = 1, + + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL, + + }, + .num_consumer_supplies = ARRAY_SIZE(ricoh619_ldo4_supply), + .consumer_supplies = ricoh619_ldo4_supply, +}; + +/* */ +static struct regulator_init_data ricoh619_ldo5 = { + .constraints = { + .name = "RICOH_LDO5", + .min_uV = 900000, + .max_uV = 3500000, + .apply_uV = 1, + + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL, + + }, + .num_consumer_supplies = ARRAY_SIZE(ricoh619_ldo5_supply), + .consumer_supplies = ricoh619_ldo5_supply, +}; + +static struct regulator_init_data ricoh619_ldo6 = { + .constraints = { + .name = "RICOH_LDO6", + .min_uV = 900000, + .max_uV = 3500000, + .apply_uV = 1, + + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL, + + }, + .num_consumer_supplies = ARRAY_SIZE(ricoh619_ldo6_supply), + .consumer_supplies = ricoh619_ldo6_supply, +}; + +static struct regulator_init_data ricoh619_ldo7 = { + .constraints = { + .name = "RICOH_LDO7", + .min_uV = 900000, + .max_uV = 3500000, + .apply_uV = 1, + + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL, + + }, + .num_consumer_supplies = ARRAY_SIZE(ricoh619_ldo7_supply), + .consumer_supplies = ricoh619_ldo7_supply, +}; + +static struct regulator_init_data ricoh619_ldo8 = { + .constraints = { + .name = "RICOH_LDO8", + .min_uV = 900000, + .max_uV = 3500000, + .apply_uV = 1, + + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL, + + }, + .num_consumer_supplies = ARRAY_SIZE(ricoh619_ldo8_supply), + .consumer_supplies = ricoh619_ldo8_supply, +}; + +static struct regulator_init_data ricoh619_ldo9 = { + .constraints = { + .name = "RICOH_LDO9", + .min_uV = 900000, + .max_uV = 3500000, + .apply_uV = 1, + + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL, + + }, + .num_consumer_supplies = ARRAY_SIZE(ricoh619_ldo9_supply), + .consumer_supplies = ricoh619_ldo9_supply, +}; + +static struct regulator_init_data ricoh619_ldo10 = { + .constraints = { + .name = "RICOH_LDO10", + .min_uV = 900000, + .max_uV = 3500000, + .apply_uV = 1, + + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL, + + }, + .num_consumer_supplies = ARRAY_SIZE(ricoh619_ldo10_supply), + .consumer_supplies = ricoh619_ldo10_supply, +}; + +/* */ +static struct regulator_init_data ricoh619_ldortc1 = { + .constraints = { + .name = "RICOH_LDORTC1", + .min_uV = 1700000, + .max_uV = 3500000, + .apply_uV = 1, + + .valid_ops_mask = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_STANDBY | REGULATOR_MODE_NORMAL, + + }, + .num_consumer_supplies = ARRAY_SIZE(ricoh619_ldortc1_supply), + .consumer_supplies = ricoh619_ldortc1_supply, +}; + +static struct ricoh619_battery_platform_data ricoh619_power_battery = { + .irq = IRQ_BOARD_BASE, + .alarm_vol_mv = 3300, + .multiple =0, + .monitor_time = 1, +}; + +static struct rtc_time rk_time = { // 2012.1.1 12:00:00 Saturday + .tm_wday = 6, + .tm_year = 111, + .tm_mon = 0, + .tm_mday = 1, + .tm_hour = 12, + .tm_min = 0, + .tm_sec = 0, +}; + +static struct ricoh619_rtc_platform_data ricoh619_rtc_data = { + .irq = IRQ_BOARD_BASE , + .time = &rk_time, +}; + +#define RICOH_REG(_id, _data) \ +{ \ + .id = RICOH619_ID_##_id, \ + .name = "ricoh619-regulator", \ + .platform_data = _data, \ +} \ + +static struct ricoh619_pwrkey_platform_data ricoh619_pwrkey_data= { + .irq = IRQ_BOARD_BASE + RICOH619_IRQ_POWER_ON, + .delay_ms = 20, +}; + +static struct ricoh619_subdev_info ricoh619_devs[] = { + RICOH_REG(DC1, &ricoh619_dcdc1), + RICOH_REG(DC2, &ricoh619_dcdc2), + RICOH_REG(DC3, &ricoh619_dcdc3), + RICOH_REG(DC4, &ricoh619_dcdc4), + RICOH_REG(DC5, &ricoh619_dcdc5), + + RICOH_REG(LDO1, &ricoh619_ldo1), + RICOH_REG(LDO2, &ricoh619_ldo2), + RICOH_REG(LDO3, &ricoh619_ldo3), + RICOH_REG(LDO4, &ricoh619_ldo4), + RICOH_REG(LDO5, &ricoh619_ldo5), + RICOH_REG(LDO6, &ricoh619_ldo6), + RICOH_REG(LDO7, &ricoh619_ldo7), + RICOH_REG(LDO8, &ricoh619_ldo8), + RICOH_REG(LDO9, &ricoh619_ldo9), + RICOH_REG(LDO10, &ricoh619_ldo10), + RICOH_REG(LDORTC1, &ricoh619_ldortc1), + + { + .id = 16, + .name ="ricoh619-battery", + .platform_data = &ricoh619_power_battery, + }, + + { + .id = 17, + .name ="rtc_ricoh619", + .platform_data = &ricoh619_rtc_data, + }, + + { + .id = 18, + .name ="ricoh619-pwrkey", + .platform_data = &ricoh619_pwrkey_data, + }, + +}; + +#define RICOH_GPIO_INIT(_init_apply, _output_mode, _output_val, _led_mode, _led_func) \ +{ \ + .output_mode_en = _output_mode, \ + .output_val = _output_val, \ + .init_apply = _init_apply, \ + .led_mode = _led_mode, \ + .led_func = _led_func, \ +} \ + +struct ricoh619_gpio_init_data ricoh_gpio_data[] = { + RICOH_GPIO_INIT(0, 1, 0, 0, 1), + RICOH_GPIO_INIT(0, 0, 0, 0, 0), + RICOH_GPIO_INIT(0, 0, 0, 0, 0), + RICOH_GPIO_INIT(0, 0, 0, 0, 0), + RICOH_GPIO_INIT(0, 0, 0, 0, 0), +}; + +static struct ricoh619_platform_data ricoh619_data={ + .irq_base = IRQ_BOARD_BASE, +// .init_port = RICOH619_HOST_IRQ, + .num_subdevs = ARRAY_SIZE(ricoh619_devs), + .subdevs = ricoh619_devs, + .pre_init = ricoh619_pre_init, + .post_init = ricoh619_post_init, + //.gpio_base = RICOH619_GPIO_EXPANDER_BASE, + .gpio_init_data = ricoh_gpio_data, + .num_gpioinit_data = ARRAY_SIZE(ricoh_gpio_data), + .enable_shutdown_pin = 0, +}; + +void __sramfunc board_pmu_ricoh619_suspend(void) +{ + #ifdef CONFIG_CLK_SWITCH_TO_32K + sram_gpio_set_value(pmic_sleep, GPIO_HIGH); + #endif +} +void __sramfunc board_pmu_ricoh619_resume(void) +{ + #ifdef CONFIG_CLK_SWITCH_TO_32K + sram_gpio_set_value(pmic_sleep, GPIO_LOW); + sram_udelay(2000); + #endif +} + + +#endif + + + + diff --git a/arch/arm/mach-rk30/board-rk3168-tb.c b/arch/arm/mach-rk30/board-rk3168-tb.c old mode 100755 new mode 100644 index 623265cd2c43..ede01b60625b --- a/arch/arm/mach-rk30/board-rk3168-tb.c +++ b/arch/arm/mach-rk30/board-rk3168-tb.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #ifdef CONFIG_CW2015_BATTERY @@ -1932,6 +1933,103 @@ static struct pmu_info rk808_ldo_info[] = { #include "board-pmu-rk808.c" #endif +#ifdef CONFIG_MFD_RICOH619 +#define PMU_POWER_SLEEP RK30_PIN0_PA1 +#define RICOH619_HOST_IRQ RK30_PIN0_PB3 + +static struct pmu_info ricoh619_dcdc_info[] = { + { + .name = "vdd_cpu", //arm + .min_uv = 1000000, + .max_uv = 1000000, + .suspend_vol = 900000, + }, + { + .name = "vdd_core", //logic + .min_uv = 1000000, + .max_uv = 1000000, + .suspend_vol = 900000, + }, + + { + .name = "ricoh_dc3", //vcc18 + .min_uv = 1800000, + .max_uv = 1800000, + .suspend_vol = 1800000, + }, + + { + .name = "ricoh_dc4", //vccio + .min_uv = 3300000, + .max_uv = 3300000, + .suspend_vol = 3300000, + }, + + { + .name = "ricoh_dc5", //ddr + .min_uv = 1200000, + .max_uv = 1200000, + .suspend_vol = 1200000, + }, + +}; +static struct pmu_info ricoh619_ldo_info[] = { + { + .name = "ricoh_ldo1", //vcc30 + .min_uv = 3000000, + .max_uv = 3000000, + }, + { + .name = "ricoh_ldo2", //vcca33 + .min_uv = 3300000, + .max_uv = 3300000, + }, + { + .name = "ricoh_ldo3", //vcctp + .min_uv = 3300000, + .max_uv = 3300000, + }, + { + .name = "ricoh_ldo4", //vccsd + .min_uv = 3300000, + .max_uv = 3300000, + }, + { + .name = "ricoh_ldo5", //vcc18_cif + .min_uv = 1800000, + .max_uv = 1800000, + }, + { + .name = "ricoh_ldo6", //vdd12 + .min_uv = 1200000, + .max_uv = 1200000, + }, + { + .name = "ricoh_ldo7", //vcc28_cif + .min_uv = 2800000, + .max_uv = 2800000, + }, + { + .name = "ricoh_ldo8", //vcc25 + .min_uv = 2500000, + .max_uv = 2500000, + }, + { + .name = "ricoh_ldo9", //vdd10 + .min_uv = 1000000, + .max_uv = 1000000, + }, + { + .name = "ricoh_ldo10", //vcca18 + .min_uv = 1800000, + .max_uv = 1800000, + }, + + }; + +#include "board-pmu-ricoh619.c" +#endif + static struct i2c_board_info __initdata i2c1_info[] = { @@ -1973,6 +2071,16 @@ static struct i2c_board_info __initdata i2c1_info[] = { }, #endif +#if defined (CONFIG_MFD_RICOH619) + { + .type = "ricoh619", + .addr = 0x32, + .flags = 0, + .irq = RICOH619_HOST_IRQ, + .platform_data=&ricoh619_data, + }, +#endif + #if defined (CONFIG_RTC_HYM8563) { .type = "rtc_hym8563", @@ -1995,43 +2103,51 @@ static struct i2c_board_info __initdata i2c1_info[] = { void __sramfunc board_pmu_suspend(void) { - #if defined (CONFIG_MFD_WM831X_I2C) - if(pmic_is_wm8326()) - board_pmu_wm8326_suspend(); - #endif - #if defined (CONFIG_MFD_TPS65910) - if(pmic_is_tps65910()) - board_pmu_tps65910_suspend(); - #endif - #if defined (CONFIG_REGULATOR_ACT8846) - 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_MFD_WM831X_I2C) + if(pmic_is_wm8326()) + board_pmu_wm8326_suspend(); +#endif +#if defined (CONFIG_MFD_TPS65910) + if(pmic_is_tps65910()) + board_pmu_tps65910_suspend(); +#endif +#if defined (CONFIG_REGULATOR_ACT8846) + 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_MFD_RICOH619) + if(pmic_is_ricoh619()) + board_pmu_ricoh619_suspend(); +#endif } void __sramfunc board_pmu_resume(void) { - #if defined (CONFIG_MFD_WM831X_I2C) - if(pmic_is_wm8326()) - board_pmu_wm8326_resume(); - #endif - #if defined (CONFIG_MFD_TPS65910) - if(pmic_is_tps65910()) - board_pmu_tps65910_resume(); - #endif - #if defined (CONFIG_REGULATOR_ACT8846) - if(pmic_is_act8846()) - board_pmu_act8846_resume(); - #endif - #if defined (CONFIG_MFD_RK808) - if(pmic_is_rk808()) - board_pmu_rk808_resume(); - #endif +#if defined (CONFIG_MFD_WM831X_I2C) + if(pmic_is_wm8326()) + board_pmu_wm8326_resume(); +#endif +#if defined (CONFIG_MFD_TPS65910) + if(pmic_is_tps65910()) + board_pmu_tps65910_resume(); +#endif +#if defined (CONFIG_REGULATOR_ACT8846) + if(pmic_is_act8846()) + board_pmu_act8846_resume(); +#endif +#if defined (CONFIG_MFD_RK808) + if(pmic_is_rk808()) + board_pmu_rk808_resume(); +#endif +#if defined (CONFIG_MFD_RICOH619) + if(pmic_is_ricoh619()) + board_pmu_ricoh619_resume(); +#endif } @@ -2267,6 +2383,11 @@ static void rk30_pm_power_off(void) rk808_device_shutdown();//rk808 shutdown } #endif + #if defined(CONFIG_MFD_RICOH619) + if(pmic_is_ricoh619()){ + ricoh619_power_off(); //ricoh619 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 8b41993ba5e4..6bd1ec3f5d57 100755 --- a/arch/arm/plat-rk/include/plat/board.h +++ b/arch/arm/plat-rk/include/plat/board.h @@ -90,6 +90,7 @@ enum { PMIC_TYPE_ACT8931 =3, PMIC_TYPE_ACT8846 =3, PMIC_TYPE_RK808 =4, + PMIC_TYPE_RICOH619 =5, PMIC_TYPE_MAX, }; extern __sramdata int g_pmic_type; @@ -98,6 +99,7 @@ extern __sramdata int g_pmic_type; #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) +#define pmic_is_ricoh619() (g_pmic_type == PMIC_TYPE_RICOH619) struct pmu_info { char *name; diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig old mode 100755 new mode 100644 index b2d165feab47..7be2f09d8862 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -294,6 +294,16 @@ config INPUT_TWL6030_PWRBUTTON To compile this driver as a module, choose M here. The module will be called twl6030_pwrbutton. +config INPUT_RICOH619_PWRKEY + tristate "RICOH RC5T619 PMU PWRKEY driver" + depends on MFD_RICOH619 + default n + help + If you say yes here you get support for the RICOH RC5T619 PWRKEY module. + + This driver can also be built as a module. If so, the module + will be called rc5t619-pwrkey. + config INPUT_TWL4030_VIBRA tristate "Support for TWL4030 Vibrator" depends on TWL4030_CORE diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile old mode 100755 new mode 100644 index 59575af2c53d..5f331355adb6 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -52,3 +52,5 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o obj-$(CONFIG_INPUT_LPSENSOR_AL3006) += al3006.o +obj-$(CONFIG_INPUT_RICOH619_PWRKEY) += ricoh619-pwrkey.o + diff --git a/drivers/input/misc/ricoh619-pwrkey.c b/drivers/input/misc/ricoh619-pwrkey.c new file mode 100644 index 000000000000..1663c3965780 --- /dev/null +++ b/drivers/input/misc/ricoh619-pwrkey.c @@ -0,0 +1,332 @@ +/* +* driver/input/misc/ricoh619-pwrkey.c +* +* Power Key driver for RICOH RC5T619 power management chip. +* +* Copyright (C) 2012-2013 RICOH COMPANY,LTD +* +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define RICOH619_ONKEY_TRIGGER_LEVEL 0 +#define RICOH619_ONKEY_OFF_IRQ 0 + +struct ricoh619_pwrkey { + struct device * dev; + struct input_dev *pwr; + #if RICOH619_ONKEY_TRIGGER_LEVEL + struct timer_list timer; + #endif + struct workqueue_struct * workqueue; + struct work_struct work; + unsigned long delay; + int key_irq; + bool pressed_first; + struct ricoh619_pwrkey_platform_data *pdata; + spinlock_t lock; +}; + +struct ricoh619_pwrkey *g_pwrkey; +//static int test_set = 0; + +#if RICOH619_ONKEY_TRIGGER_LEVEL +void ricoh619_pwrkey_timer(unsigned long t) +{ + queue_work(g_pwrkey->workqueue, &g_pwrkey->work); +} +#endif + +extern int pwrkey_wakeup; +static void ricoh619_irq_work(struct work_struct * work) +{ + unsigned long flags; + uint8_t val; + +// printk("PMU: %s: \n",__func__); + //spin_lock_irqsave(&g_pwrkey->lock, flags); + + if(pwrkey_wakeup){ +// printk("PMU: %s: pwrkey_wakeup\n",__func__); + pwrkey_wakeup = 0; + input_event(g_pwrkey->pwr, EV_KEY, KEY_POWER, 1); + input_event(g_pwrkey->pwr, EV_SYN, 0, 0); + input_event(g_pwrkey->pwr, EV_KEY, KEY_POWER, 0); + input_event(g_pwrkey->pwr, EV_SYN, 0, 0); + + return; + } + ricoh619_read(g_pwrkey->dev->parent, RICOH619_INT_MON_SYS, &val); + dev_dbg(g_pwrkey->dev, "pwrkey is pressed?(0x%x): 0x%x\n", RICOH619_INT_MON_SYS, val); +// printk("PMU: %s: val=0x%x\n",__func__,val); + val &= 0x1; + if(val){ + #if (RICOH619_ONKEY_TRIGGER_LEVEL) + g_pwrkey->timer.expires = jiffies + g_pwrkey->delay; + dd_timer(&g_pwrkey->timer); + #endif + if (!g_pwrkey->pressed_first){ + g_pwrkey->pressed_first = true; +// printk("PMU1: %s: Power Key!!!\n",__func__); + //input_report_key(g_pwrkey->pwr, KEY_POWER, 1); + //input_sync(g_pwrkey->pwr); + input_event(g_pwrkey->pwr, EV_KEY, KEY_POWER, 1); + input_event(g_pwrkey->pwr, EV_SYN, 0, 0); + } + } + else{ + if (g_pwrkey->pressed_first){ +// printk("PMU2: %s: Power Key!!!\n",__func__); + //input_report_key(g_pwrkey->pwr, KEY_POWER, 0); + //input_sync(g_pwrkey->pwr); + input_event(g_pwrkey->pwr, EV_KEY, KEY_POWER, 0); + input_event(g_pwrkey->pwr, EV_SYN, 0, 0); + } + g_pwrkey->pressed_first = false; + } + + //spin_unlock_irqrestore(&g_pwrkey->lock, flags); +} +extern struct ricoh619 *g_ricoh619; +static irqreturn_t pwrkey_irq(int irq, void *_pwrkey) +{ +// printk("PMU: %s: \n",__func__); + struct ricoh619 *ricoh619 = g_ricoh619; + #if (RICOH619_ONKEY_TRIGGER_LEVEL) + g_pwrkey->timer.expires = jiffies + g_pwrkey->delay; + add_timer(&g_pwrkey->timer); + #else + queue_work(g_pwrkey->workqueue, &g_pwrkey->work); + #endif + ricoh619_clr_bits(g_ricoh619->dev, RICOH619_INT_IR_SYS, 0x1); //clr power-on interrupt + return IRQ_HANDLED; +} + +#if RICOH619_ONKEY_OFF_IRQ +static irqreturn_t pwrkey_irq_off(int irq, void *_pwrkey) +{ + dev_warn(g_pwrkey->dev, "ONKEY is pressed long time!\n"); + return IRQ_HANDLED; +} +#endif + +static int __devinit ricoh619_pwrkey_probe(struct platform_device *pdev) +{ + struct input_dev *pwr; + int key_irq; + int err; + struct ricoh619_pwrkey *pwrkey; + struct ricoh619_pwrkey_platform_data *pdata = pdev->dev.platform_data; + uint8_t val; + +// printk("PMU: %s: \n",__func__); + + if (!pdata) { + dev_err(&pdev->dev, "power key platform data not supplied\n"); + return -EINVAL; + } + key_irq = (pdata->irq + RICOH619_IRQ_POWER_ON); + printk(KERN_INFO "PMU1: %s: key_irq=%d\n", __func__, key_irq); + pwrkey = kzalloc(sizeof(*pwrkey), GFP_KERNEL); + if (!pwrkey) + return -ENOMEM; + + pwrkey->dev = &pdev->dev; + pwrkey->pdata = pdata; + pwrkey->pressed_first = false; + pwrkey->delay = HZ / 1000 * pdata->delay_ms; + g_pwrkey = pwrkey; + pwr = input_allocate_device(); + if (!pwr) { + dev_dbg(&pdev->dev, "Can't allocate power button\n"); + err = -ENOMEM; + goto free_pwrkey; + } + input_set_capability(pwr, EV_KEY, KEY_POWER); + pwr->name = "ricoh619_pwrkey"; + pwr->phys = "ricoh619_pwrkey/input0"; + pwr->dev.parent = &pdev->dev; + + #if RICOH619_ONKEY_TRIGGER_LEVEL + init_timer(&pwrkey->timer); + pwrkey->timer.function = ricoh619_pwrkey_timer; + #endif + + spin_lock_init(&pwrkey->lock); + err = input_register_device(pwr); + if (err) { + dev_dbg(&pdev->dev, "Can't register power key: %d\n", err); + goto free_input_dev; + } + pwrkey->key_irq = key_irq; + pwrkey->pwr = pwr; + platform_set_drvdata(pdev, pwrkey); + + // Check if power-key is pressed at boot up + err = ricoh619_read(pwrkey->dev->parent, RICOH619_INT_MON_SYS, &val); + if (err < 0) { + dev_err(&pdev->dev, "Key-press status at boot failed rc=%d\n", + err); + goto unreg_input_dev; + } + val &= 0x1; + if (val) { + input_report_key(pwrkey->pwr, KEY_POWER, 1); +// printk(KERN_INFO "******KEY_POWER:1\n"); + input_sync(pwrkey->pwr); + pwrkey->pressed_first = true; + } + + #if !(RICOH619_ONKEY_TRIGGER_LEVEL) + ricoh619_set_bits(pwrkey->dev->parent, RICOH619_PWR_IRSEL, 0x1); //trigger both edge + #endif + + err = request_threaded_irq(key_irq, NULL, pwrkey_irq, + IRQF_ONESHOT, "ricoh619_pwrkey", pwrkey); + if (err < 0) { + dev_err(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n", + key_irq, err); + goto unreg_input_dev; + } + + #if RICOH619_ONKEY_OFF_IRQ + err = request_threaded_irq(key_irq + RICOH619_IRQ_ONKEY_OFF, NULL, + pwrkey_irq_off, IRQF_ONESHOT, + "ricoh619_pwrkey_off", pwrkey); + if (err < 0) { + dev_err(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n", + key_irq + RICOH619_IRQ_ONKEY_OFF, err); + free_irq(key_irq, pwrkey); + goto unreg_input_dev; + } + #endif + + pwrkey->workqueue = create_singlethread_workqueue("ricoh619_pwrkey"); + INIT_WORK(&pwrkey->work, ricoh619_irq_work); + + /* Enable power key IRQ */ + /* trigger both edge */ + ricoh619_set_bits(pwrkey->dev->parent, RICOH619_PWR_IRSEL, 0x1); + /* Enable system interrupt */ + ricoh619_set_bits(pwrkey->dev->parent, RICOH619_INTC_INTEN, 0x1); + /* Enable power-on interrupt */ + ricoh619_set_bits(pwrkey->dev->parent, RICOH619_INT_EN_SYS, 0x1); +// printk(KERN_INFO "PMU: %s is OK!\n", __func__); + return 0; + +unreg_input_dev: + input_unregister_device(pwr); + pwr = NULL; + +free_input_dev: + input_free_device(pwr); + free_pwrkey: + kfree(pwrkey); + + return err; +} + +static int __devexit ricoh619_pwrkey_remove(struct platform_device *pdev) +{ + struct ricoh619_pwrkey *pwrkey = platform_get_drvdata(pdev); + + flush_workqueue(pwrkey->workqueue); + destroy_workqueue(pwrkey->workqueue); + free_irq(pwrkey->key_irq, pwrkey); + input_unregister_device(pwrkey->pwr); + kfree(pwrkey); + + return 0; +} + +#ifdef CONFIG_PM +static int ricoh619_pwrkey_suspend(struct device *dev) +{ + struct ricoh619_pwrkey *info = dev_get_drvdata(dev); + +// printk(KERN_INFO "PMU: %s\n", __func__); + + if (info->key_irq) + disable_irq(info->key_irq); + cancel_work_sync(&info->work); + flush_workqueue(info->workqueue); + + return 0; +} + +static int ricoh619_pwrkey_resume(struct device *dev) +{ + struct ricoh619_pwrkey *info = dev_get_drvdata(dev); + +// printk(KERN_INFO "PMU: %s\n", __func__); + queue_work(info->workqueue, &info->work); + if (info->key_irq) + enable_irq(info->key_irq); + + return 0; +} + +static const struct dev_pm_ops ricoh619_pwrkey_pm_ops = { + .suspend = ricoh619_pwrkey_suspend, + .resume = ricoh619_pwrkey_resume, +}; +#endif + +static struct platform_driver ricoh619_pwrkey_driver = { + .probe = ricoh619_pwrkey_probe, + .remove = __devexit_p(ricoh619_pwrkey_remove), + .driver = { + .name = "ricoh619-pwrkey", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &ricoh619_pwrkey_pm_ops, +#endif + }, +}; + +static int __init ricoh619_pwrkey_init(void) +{ + return platform_driver_register(&ricoh619_pwrkey_driver); +} +subsys_initcall_sync(ricoh619_pwrkey_init); + +static void __exit ricoh619_pwrkey_exit(void) +{ + platform_driver_unregister(&ricoh619_pwrkey_driver); +} +module_exit(ricoh619_pwrkey_exit); + + +MODULE_ALIAS("platform:ricoh619-pwrkey"); +MODULE_AUTHOR("zhangqing "); +MODULE_DESCRIPTION("ricoh619 Power Key"); +MODULE_LICENSE("GPL v2"); \ No newline at end of file diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 6aa29681a06d..5f515c44e69e 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -307,6 +307,18 @@ config MFD_RK808 if you say yes here you get support for the RK808 series of Power Management chips. +config MFD_RICOH619 + bool "Ricoh RC5T619 Power Management system device" + depends on I2C && GPIOLIB && GENERIC_HARDIRQS + select MFD_CORE + default n + help + If you say yes here you get support for the RICOH619 Power + Management system device. + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the + functionality of the device. + config AIC3262_CODEC bool "Support TI Codec Aic3262" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index b810c78978c7..f076b63518ea 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -110,3 +110,4 @@ obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o obj-$(CONFIG_MFD_RK610) += rk610-core.o obj-$(CONFIG_MFD_RK808) += rk808.o rk808-irq.o obj-$(CONFIG_MFD_RK616) += rk616-core.o +obj-$(CONFIG_MFD_RICOH619) += ricoh619.o ricoh619-irq.o diff --git a/drivers/mfd/ricoh619-irq.c b/drivers/mfd/ricoh619-irq.c new file mode 100644 index 000000000000..60e7bb0c3e35 --- /dev/null +++ b/drivers/mfd/ricoh619-irq.c @@ -0,0 +1,534 @@ +/* + * driver/mfd/ricoh619-irq.c + * + * Interrupt driver for RICOH RC5T619 power management chip. + * + * Copyright (C) 2012-2013 RICOH COMPANY,LTD + * + * Based on code + * Copyright (C) 2011 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#include +#include +#include +#include +#include +#include +#include + + +enum int_type { + SYS_INT = 0x1, + DCDC_INT = 0x2, + RTC_INT = 0x4, + ADC_INT = 0x8, + GPIO_INT = 0x10, + CHG_INT = 0x40, +}; + +static int gpedge_add[] = { + RICOH619_GPIO_GPEDGE1, + RICOH619_GPIO_GPEDGE2 +}; + +static int irq_en_add[] = { + RICOH619_INT_EN_SYS, + RICOH619_INT_EN_DCDC, + RICOH619_INT_EN_RTC, + RICOH619_INT_EN_ADC1, + RICOH619_INT_EN_ADC2, + RICOH619_INT_EN_ADC3, + RICOH619_INT_EN_GPIO, + RICOH619_INT_EN_GPIO2, + RICOH619_INT_MSK_CHGCTR, + RICOH619_INT_MSK_CHGSTS1, + RICOH619_INT_MSK_CHGSTS2, + RICOH619_INT_MSK_CHGERR, + RICOH619_INT_MSK_CHGEXTIF +}; + +static int irq_mon_add[] = { + RICOH619_INT_IR_SYS, //RICOH619_INT_MON_SYS, + RICOH619_INT_IR_DCDC, //RICOH619_INT_MON_DCDC, + RICOH619_INT_IR_RTC, //RICOH619_INT_MON_RTC, + RICOH619_INT_IR_ADCL, + RICOH619_INT_IR_ADCH, + RICOH619_INT_IR_ADCEND, + RICOH619_INT_IR_GPIOR, + RICOH619_INT_IR_GPIOF, + RICOH619_INT_IR_CHGCTR, //RICOH619_INT_MON_CHGCTR, + RICOH619_INT_IR_CHGSTS1, //RICOH619_INT_MON_CHGSTS1, + RICOH619_INT_IR_CHGSTS2, //RICOH619_INT_MON_CHGSTS2, + RICOH619_INT_IR_CHGERR, //RICOH619_INT_MON_CHGERR + RICOH619_INT_IR_CHGEXTIF //RICOH619_INT_MON_CHGEXTIF +}; + +static int irq_clr_add[] = { + RICOH619_INT_IR_SYS, + RICOH619_INT_IR_DCDC, + RICOH619_INT_IR_RTC, + RICOH619_INT_IR_ADCL, + RICOH619_INT_IR_ADCH, + RICOH619_INT_IR_ADCEND, + RICOH619_INT_IR_GPIOR, + RICOH619_INT_IR_GPIOF, + RICOH619_INT_IR_CHGCTR, + RICOH619_INT_IR_CHGSTS1, + RICOH619_INT_IR_CHGSTS2, + RICOH619_INT_IR_CHGERR, + RICOH619_INT_IR_CHGEXTIF +}; + +static int main_int_type[] = { + SYS_INT, + DCDC_INT, + RTC_INT, + ADC_INT, + ADC_INT, + ADC_INT, + GPIO_INT, + GPIO_INT, + CHG_INT, + CHG_INT, + CHG_INT, + CHG_INT, + CHG_INT, +}; + +struct ricoh619_irq_data { + u8 int_type; + u8 master_bit; + u8 int_en_bit; + u8 mask_reg_index; + int grp_index; +}; + +#define RICOH619_IRQ(_int_type, _master_bit, _grp_index, _int_bit, _mask_ind) \ + { \ + .int_type = _int_type, \ + .master_bit = _master_bit, \ + .grp_index = _grp_index, \ + .int_en_bit = _int_bit, \ + .mask_reg_index = _mask_ind, \ + } + +static const struct ricoh619_irq_data ricoh619_irqs[RICOH619_NR_IRQS] = { + [RICOH619_IRQ_POWER_ON] = RICOH619_IRQ(SYS_INT, 0, 0, 0, 0), + [RICOH619_IRQ_EXTIN] = RICOH619_IRQ(SYS_INT, 0, 1, 1, 0), + [RICOH619_IRQ_PRE_VINDT] = RICOH619_IRQ(SYS_INT, 0, 2, 2, 0), + [RICOH619_IRQ_PREOT] = RICOH619_IRQ(SYS_INT, 0, 3, 3, 0), + [RICOH619_IRQ_POWER_OFF] = RICOH619_IRQ(SYS_INT, 0, 4, 4, 0), + [RICOH619_IRQ_NOE_OFF] = RICOH619_IRQ(SYS_INT, 0, 5, 5, 0), + [RICOH619_IRQ_WD] = RICOH619_IRQ(SYS_INT, 0, 6, 6, 0), + + [RICOH619_IRQ_DC1LIM] = RICOH619_IRQ(DCDC_INT, 1, 0, 0, 1), + [RICOH619_IRQ_DC2LIM] = RICOH619_IRQ(DCDC_INT, 1, 1, 1, 1), + [RICOH619_IRQ_DC3LIM] = RICOH619_IRQ(DCDC_INT, 1, 2, 2, 1), + [RICOH619_IRQ_DC4LIM] = RICOH619_IRQ(DCDC_INT, 1, 3, 3, 1), + [RICOH619_IRQ_DC5LIM] = RICOH619_IRQ(DCDC_INT, 1, 4, 4, 1), + + [RICOH619_IRQ_CTC] = RICOH619_IRQ(RTC_INT, 2, 0, 0, 2), + [RICOH619_IRQ_DALE] = RICOH619_IRQ(RTC_INT, 2, 1, 6, 2), + + [RICOH619_IRQ_ILIMLIR] = RICOH619_IRQ(ADC_INT, 3, 0, 0, 3), + [RICOH619_IRQ_VBATLIR] = RICOH619_IRQ(ADC_INT, 3, 1, 1, 3), + [RICOH619_IRQ_VADPLIR] = RICOH619_IRQ(ADC_INT, 3, 2, 2, 3), + [RICOH619_IRQ_VUSBLIR] = RICOH619_IRQ(ADC_INT, 3, 3, 3, 3), + [RICOH619_IRQ_VSYSLIR] = RICOH619_IRQ(ADC_INT, 3, 4, 4, 3), + [RICOH619_IRQ_VTHMLIR] = RICOH619_IRQ(ADC_INT, 3, 5, 5, 3), + [RICOH619_IRQ_AIN1LIR] = RICOH619_IRQ(ADC_INT, 3, 6, 6, 3), + [RICOH619_IRQ_AIN0LIR] = RICOH619_IRQ(ADC_INT, 3, 7, 7, 3), + + [RICOH619_IRQ_ILIMHIR] = RICOH619_IRQ(ADC_INT, 3, 8, 0, 4), + [RICOH619_IRQ_VBATHIR] = RICOH619_IRQ(ADC_INT, 3, 9, 1, 4), + [RICOH619_IRQ_VADPHIR] = RICOH619_IRQ(ADC_INT, 3, 10, 2, 4), + [RICOH619_IRQ_VUSBHIR] = RICOH619_IRQ(ADC_INT, 3, 11, 3, 4), + [RICOH619_IRQ_VSYSHIR] = RICOH619_IRQ(ADC_INT, 3, 12, 4, 4), + [RICOH619_IRQ_VTHMHIR] = RICOH619_IRQ(ADC_INT, 3, 13, 5, 4), + [RICOH619_IRQ_AIN1HIR] = RICOH619_IRQ(ADC_INT, 3, 14, 6, 4), + [RICOH619_IRQ_AIN0HIR] = RICOH619_IRQ(ADC_INT, 3, 15, 7, 4), + + [RICOH619_IRQ_ADC_ENDIR] = RICOH619_IRQ(ADC_INT, 3, 16, 0, 5), + + [RICOH619_IRQ_GPIO0] = RICOH619_IRQ(GPIO_INT, 4, 0, 0, 6), + [RICOH619_IRQ_GPIO1] = RICOH619_IRQ(GPIO_INT, 4, 1, 1, 6), + [RICOH619_IRQ_GPIO2] = RICOH619_IRQ(GPIO_INT, 4, 2, 2, 6), + [RICOH619_IRQ_GPIO3] = RICOH619_IRQ(GPIO_INT, 4, 3, 3, 6), + [RICOH619_IRQ_GPIO4] = RICOH619_IRQ(GPIO_INT, 4, 4, 4, 6), + + [RICOH619_IRQ_FVADPDETSINT] = RICOH619_IRQ(CHG_INT, 6, 0, 0, 8), + [RICOH619_IRQ_FVUSBDETSINT] = RICOH619_IRQ(CHG_INT, 6, 1, 1, 8), + [RICOH619_IRQ_FVADPLVSINT] = RICOH619_IRQ(CHG_INT, 6, 2, 2, 8), + [RICOH619_IRQ_FVUSBLVSINT] = RICOH619_IRQ(CHG_INT, 6, 3, 3, 8), + [RICOH619_IRQ_FWVADPSINT] = RICOH619_IRQ(CHG_INT, 6, 4, 4, 8), + [RICOH619_IRQ_FWVUSBSINT] = RICOH619_IRQ(CHG_INT, 6, 5, 5, 8), + + [RICOH619_IRQ_FONCHGINT] = RICOH619_IRQ(CHG_INT, 6, 6, 0, 9), + [RICOH619_IRQ_FCHGCMPINT] = RICOH619_IRQ(CHG_INT, 6, 7, 1, 9), + [RICOH619_IRQ_FBATOPENINT] = RICOH619_IRQ(CHG_INT, 6, 8, 2, 9), + [RICOH619_IRQ_FSLPMODEINT] = RICOH619_IRQ(CHG_INT, 6, 9, 3, 9), + [RICOH619_IRQ_FBTEMPJTA1INT] = RICOH619_IRQ(CHG_INT, 6, 10, 4, 9), + [RICOH619_IRQ_FBTEMPJTA2INT] = RICOH619_IRQ(CHG_INT, 6, 11, 5, 9), + [RICOH619_IRQ_FBTEMPJTA3INT] = RICOH619_IRQ(CHG_INT, 6, 12, 6, 9), + [RICOH619_IRQ_FBTEMPJTA4INT] = RICOH619_IRQ(CHG_INT, 6, 13, 7, 9), + + [RICOH619_IRQ_FCURTERMINT] = RICOH619_IRQ(CHG_INT, 6, 14, 0, 10), + [RICOH619_IRQ_FVOLTERMINT] = RICOH619_IRQ(CHG_INT, 6, 15, 1, 10), + [RICOH619_IRQ_FICRVSINT] = RICOH619_IRQ(CHG_INT, 6, 16, 2, 10), + [RICOH619_IRQ_FPOOR_CHGCURINT] = RICOH619_IRQ(CHG_INT, 6, 17, 3, 10), + [RICOH619_IRQ_FOSCFDETINT1] = RICOH619_IRQ(CHG_INT, 6, 18, 4, 10), + [RICOH619_IRQ_FOSCFDETINT2] = RICOH619_IRQ(CHG_INT, 6, 19, 5, 10), + [RICOH619_IRQ_FOSCFDETINT3] = RICOH619_IRQ(CHG_INT, 6, 20, 6, 10), + [RICOH619_IRQ_FOSCMDETINT] = RICOH619_IRQ(CHG_INT, 6, 21, 7, 10), + + [RICOH619_IRQ_FDIEOFFINT] = RICOH619_IRQ(CHG_INT, 6, 22, 0, 11), + [RICOH619_IRQ_FDIEERRINT] = RICOH619_IRQ(CHG_INT, 6, 23, 1, 11), + [RICOH619_IRQ_FBTEMPERRINT] = RICOH619_IRQ(CHG_INT, 6, 24, 2, 11), + [RICOH619_IRQ_FVBATOVINT] = RICOH619_IRQ(CHG_INT, 6, 25, 3, 11), + [RICOH619_IRQ_FTTIMOVINT] = RICOH619_IRQ(CHG_INT, 6, 26, 4, 11), + [RICOH619_IRQ_FRTIMOVINT] = RICOH619_IRQ(CHG_INT, 6, 27, 5, 11), + [RICOH619_IRQ_FVADPOVSINT] = RICOH619_IRQ(CHG_INT, 6, 28, 6, 11), + [RICOH619_IRQ_FVUSBOVSINT] = RICOH619_IRQ(CHG_INT, 6, 29, 7, 11), + + [RICOH619_IRQ_FGCDET] = RICOH619_IRQ(CHG_INT, 6, 30, 0, 12), + [RICOH619_IRQ_FPCDET] = RICOH619_IRQ(CHG_INT, 6, 31, 1, 12), + [RICOH619_IRQ_FWARN_ADP] = RICOH619_IRQ(CHG_INT, 6, 32, 3, 12), + +}; + +static void ricoh619_irq_lock(struct irq_data *irq_data) +{ + struct ricoh619 *ricoh619 = irq_data_get_irq_chip_data(irq_data); + + mutex_lock(&ricoh619->irq_lock); +} + +static void ricoh619_irq_unmask(struct irq_data *irq_data) +{ + struct ricoh619 *ricoh619 = irq_data_get_irq_chip_data(irq_data); + unsigned int __irq = irq_data->irq - ricoh619->irq_base; + const struct ricoh619_irq_data *data = &ricoh619_irqs[__irq]; + + ricoh619->group_irq_en[data->master_bit] |= (1 << data->grp_index); + if (ricoh619->group_irq_en[data->master_bit]) + ricoh619->intc_inten_reg |= 1 << data->master_bit; + + if (data->master_bit == 6) /* if Charger */ + ricoh619->irq_en_reg[data->mask_reg_index] + &= ~(1 << data->int_en_bit); + else + ricoh619->irq_en_reg[data->mask_reg_index] + |= 1 << data->int_en_bit; +} + +static void ricoh619_irq_mask(struct irq_data *irq_data) +{ + struct ricoh619 *ricoh619 = irq_data_get_irq_chip_data(irq_data); + unsigned int __irq = irq_data->irq - ricoh619->irq_base; + const struct ricoh619_irq_data *data = &ricoh619_irqs[__irq]; + + ricoh619->group_irq_en[data->master_bit] &= ~(1 << data->grp_index); + if (!ricoh619->group_irq_en[data->master_bit]) + ricoh619->intc_inten_reg &= ~(1 << data->master_bit); + + if (data->master_bit == 6) /* if Charger */ + ricoh619->irq_en_reg[data->mask_reg_index] + |= 1 << data->int_en_bit; + else + ricoh619->irq_en_reg[data->mask_reg_index] + &= ~(1 << data->int_en_bit); +} + +static void ricoh619_irq_sync_unlock(struct irq_data *irq_data) +{ + struct ricoh619 *ricoh619 = irq_data_get_irq_chip_data(irq_data); + int i; + + for (i = 0; i < ARRAY_SIZE(ricoh619->gpedge_reg); i++) { + if (ricoh619->gpedge_reg[i] != ricoh619->gpedge_cache[i]) { + if (!WARN_ON(ricoh619_write(ricoh619->dev, + gpedge_add[i], + ricoh619->gpedge_reg[i]))) + ricoh619->gpedge_cache[i] = + ricoh619->gpedge_reg[i]; + } + } + + for (i = 0; i < ARRAY_SIZE(ricoh619->irq_en_reg); i++) { + if (ricoh619->irq_en_reg[i] != ricoh619->irq_en_cache[i]) { + if (!WARN_ON(ricoh619_write(ricoh619->dev, + irq_en_add[i], + ricoh619->irq_en_reg[i]))) + ricoh619->irq_en_cache[i] = + ricoh619->irq_en_reg[i]; + } + } + + if (ricoh619->intc_inten_reg != ricoh619->intc_inten_cache) { + if (!WARN_ON(ricoh619_write(ricoh619->dev, + RICOH619_INTC_INTEN, ricoh619->intc_inten_reg))) + ricoh619->intc_inten_cache = ricoh619->intc_inten_reg; + } + + mutex_unlock(&ricoh619->irq_lock); +} + +static int ricoh619_irq_set_type(struct irq_data *irq_data, unsigned int type) +{ + struct ricoh619 *ricoh619 = irq_data_get_irq_chip_data(irq_data); + unsigned int __irq = irq_data->irq - ricoh619->irq_base; + const struct ricoh619_irq_data *data = &ricoh619_irqs[__irq]; + int val = 0; + int gpedge_index; + int gpedge_bit_pos; + + if (data->int_type & GPIO_INT) { + gpedge_index = data->int_en_bit / 4; + gpedge_bit_pos = data->int_en_bit % 4; + + if (type & IRQ_TYPE_EDGE_FALLING) + val |= 0x2; + + if (type & IRQ_TYPE_EDGE_RISING) + val |= 0x1; + + ricoh619->gpedge_reg[gpedge_index] &= ~(3 << gpedge_bit_pos); + ricoh619->gpedge_reg[gpedge_index] |= (val << gpedge_bit_pos); + ricoh619_irq_unmask(irq_data); + } + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int ricoh619_irq_set_wake(struct irq_data *irq_data, unsigned int on) +{ + struct ricoh619 *ricoh619 = irq_data_get_irq_chip_data(irq_data); + return irq_set_irq_wake(ricoh619->chip_irq, on); //i2c->irq +} +#else +#define ricoh619_irq_set_wake NULL +#endif + +static irqreturn_t ricoh619_irq(int irq, void *data) +{ + struct ricoh619 *ricoh619 = data; + u8 int_sts[MAX_INTERRUPT_MASKS]; + u8 master_int; + int i; + int ret; + unsigned int rtc_int_sts = 0; + + /* Clear the status */ + for (i = 0; i < MAX_INTERRUPT_MASKS; i++) + int_sts[i] = 0; + + ret = ricoh619_read(ricoh619->dev, RICOH619_INTC_INTMON, + &master_int); +// printk("PMU1: %s: master_int=0x%x\n", __func__, master_int); + if (ret < 0) { + dev_err(ricoh619->dev, "Error in reading reg 0x%02x " + "error: %d\n", RICOH619_INTC_INTMON, ret); + return IRQ_HANDLED; + } + +/* Mask Charger Interrupt */ + if((CHG_INT | ADC_INT) & master_int){ + for (i=8; i < MAX_INTERRUPT_MASKS; i++) { + ret = ricoh619_write(ricoh619->dev, + irq_en_add[i], 0xff); + if (ret < 0) { + dev_err(ricoh619->dev, + "Error in write reg 0x%02x " + "error: %d\n", + irq_en_add[i], ret); + } + } + //Disable ADC interrupt + for (i=3; i < 6; i++) { + ret = ricoh619_write(ricoh619->dev, irq_en_add[i], 0x0); + if (ret < 0) { + dev_err(ricoh619->dev, "Error in write reg 0x%02x " + "error: %d\n", irq_en_add[i], + ret); + } + } + } + + for (i = 0; i < MAX_INTERRUPT_MASKS; ++i) { + /* Even if INTC_INTMON register = 1, INT signal might not output + because INTC_INTMON register indicates only interrupt facter level. + So remove the following procedure */ +// if (!(master_int & main_int_type[i])) +// continue; + + ret = ricoh619_read(ricoh619->dev, + irq_mon_add[i], &int_sts[i]); +// printk("PMU2: %s: int_sts[%d]=0x%x\n", __func__,i, int_sts[i]); + if (ret < 0) { + dev_err(ricoh619->dev, "Error in reading reg 0x%02x " + "error: %d\n", irq_mon_add[i], ret); + int_sts[i] = 0; + continue; + } + + if (main_int_type[i] & RTC_INT) { + // Changes status bit position from RTCCNT2 to RTCCNT1 + rtc_int_sts = 0; + if (int_sts[i] & 0x1) + rtc_int_sts |= BIT(6); + if (int_sts[i] & 0x4) + rtc_int_sts |= BIT(0); + } + + ret = ricoh619_write(ricoh619->dev, + irq_clr_add[i], ~int_sts[i]); + if (ret < 0) { + dev_err(ricoh619->dev, "Error in reading reg 0x%02x " + "error: %d\n", irq_clr_add[i], ret); + } + + if (main_int_type[i] & RTC_INT) + int_sts[i] = rtc_int_sts; + + } + + /* Merge gpio interrupts for rising and falling case*/ + int_sts[6] |= int_sts[7]; + + /* Call interrupt handler if enabled */ + for (i = 0; i < RICOH619_NR_IRQS; ++i) { + const struct ricoh619_irq_data *data = &ricoh619_irqs[i]; + if ((int_sts[data->mask_reg_index] & (1 << data->int_en_bit)) && + (ricoh619->group_irq_en[data->master_bit] & + (1 << data->grp_index))) + handle_nested_irq(ricoh619->irq_base + i); + } + +// printk(KERN_INFO "PMU: %s: out\n", __func__); + return IRQ_HANDLED; +} + +static struct irq_chip ricoh619_irq_chip = { + .name = "ricoh619", + .irq_mask = ricoh619_irq_mask, + .irq_unmask = ricoh619_irq_unmask, + .irq_bus_lock = ricoh619_irq_lock, + .irq_bus_sync_unlock = ricoh619_irq_sync_unlock, + .irq_set_type = ricoh619_irq_set_type, + .irq_set_wake = ricoh619_irq_set_wake, +}; + +int ricoh619_irq_init(struct ricoh619 *ricoh619, int irq, + int irq_base) +{ + int i, ret; + + if (!irq_base) { + dev_warn(ricoh619->dev, "No interrupt support on IRQ base\n"); + return -EINVAL; + } + + mutex_init(&ricoh619->irq_lock); + + /* Initialize all locals to 0 */ + for (i = 0; i < 8; i++) { + ricoh619->irq_en_cache[i] = 0; + ricoh619->irq_en_reg[i] = 0; + } + // Charger Mask register must be set to 1 for masking Int output. + for (i = 8; i < MAX_INTERRUPT_MASKS; i++) { + ricoh619->irq_en_cache[i] = 0xff; + ricoh619->irq_en_reg[i] = 0xff; + } + + ricoh619->intc_inten_cache = 0; + ricoh619->intc_inten_reg = 0; + for (i = 0; i < MAX_GPEDGE_REG; i++) { + ricoh619->gpedge_cache[i] = 0; + ricoh619->gpedge_reg[i] = 0; + } + + /* Initailize all int register to 0 */ + for (i = 0; i < MAX_INTERRUPT_MASKS; i++) { + ret = ricoh619_write(ricoh619->dev, + irq_en_add[i], + ricoh619->irq_en_reg[i]); + if (ret < 0) + dev_err(ricoh619->dev, "Error in writing reg 0x%02x " + "error: %d\n", irq_en_add[i], ret); + } + + for (i = 0; i < MAX_GPEDGE_REG; i++) { + ret = ricoh619_write(ricoh619->dev, + gpedge_add[i], + ricoh619->gpedge_reg[i]); + if (ret < 0) + dev_err(ricoh619->dev, "Error in writing reg 0x%02x " + "error: %d\n", gpedge_add[i], ret); + } + + ret = ricoh619_write(ricoh619->dev, RICOH619_INTC_INTEN, 0x0); + if (ret < 0) + dev_err(ricoh619->dev, "Error in writing reg 0x%02x " + "error: %d\n", RICOH619_INTC_INTEN, ret); + + /* Clear all interrupts in case they woke up active. */ + for (i = 0; i < MAX_INTERRUPT_MASKS; i++) { + ret = ricoh619_write(ricoh619->dev, + irq_clr_add[i], 0); + if (ret < 0) + dev_err(ricoh619->dev, "Error in writing reg 0x%02x " + "error: %d\n", irq_clr_add[i], ret); + } + + ricoh619->irq_base = irq_base; + ricoh619->chip_irq = irq; + + for (i = 0; i < RICOH619_NR_IRQS; i++) { + int __irq = i + ricoh619->irq_base; + irq_set_chip_data(__irq, ricoh619); + irq_set_chip_and_handler(__irq, &ricoh619_irq_chip, + handle_simple_irq); + irq_set_nested_thread(__irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(__irq, IRQF_VALID); +#endif + } + + ret = request_threaded_irq(irq, NULL, ricoh619_irq, + IRQ_TYPE_EDGE_FALLING|IRQF_DISABLED|IRQF_ONESHOT, + "ricoh619", ricoh619); + if (ret < 0) + dev_err(ricoh619->dev, "Error in registering interrupt " + "error: %d\n", ret); +/* + if (!ret) { + device_init_wakeup(ricoh619->dev, 1); + enable_irq_wake(irq); + } +*/ + + return ret; +} + +int ricoh619_irq_exit(struct ricoh619 *ricoh619) +{ + if (ricoh619->chip_irq) + free_irq(ricoh619->chip_irq, ricoh619); + return 0; +} + diff --git a/drivers/mfd/ricoh619.c b/drivers/mfd/ricoh619.c new file mode 100644 index 000000000000..70a1ef08b6fb --- /dev/null +++ b/drivers/mfd/ricoh619.c @@ -0,0 +1,852 @@ +/* + * driver/mfd/ricoh619.c + * + * Core driver implementation to access RICOH RC5T619 power management chip. + * + * Copyright (C) 2012-2013 RICOH COMPANY,LTD + * + * Based on code + * Copyright (C) 2011 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +/*#define DEBUG 1*/ +/*#define VERBOSE_DEBUG 1*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +struct ricoh619 *g_ricoh619; +struct sleep_control_data { + u8 reg_add; +}; + +#define SLEEP_INIT(_id, _reg) \ + [RICOH619_DS_##_id] = {.reg_add = _reg} + +static struct sleep_control_data sleep_data[] = { + SLEEP_INIT(DC1, 0x16), + SLEEP_INIT(DC2, 0x17), + SLEEP_INIT(DC3, 0x18), + SLEEP_INIT(DC4, 0x19), + SLEEP_INIT(DC5, 0x1A), + SLEEP_INIT(LDO1, 0x1B), + SLEEP_INIT(LDO2, 0x1C), + SLEEP_INIT(LDO3, 0x1D), + SLEEP_INIT(LDO4, 0x1E), + SLEEP_INIT(LDO5, 0x1F), + SLEEP_INIT(LDO6, 0x20), + SLEEP_INIT(LDO7, 0x21), + SLEEP_INIT(LDO8, 0x22), + SLEEP_INIT(LDO9, 0x23), + SLEEP_INIT(LDO10, 0x24), + SLEEP_INIT(PSO0, 0x25), + SLEEP_INIT(PSO1, 0x26), + SLEEP_INIT(PSO2, 0x27), + SLEEP_INIT(PSO3, 0x28), + SLEEP_INIT(PSO4, 0x29), + SLEEP_INIT(LDORTC1, 0x2A), +}; + +static inline int __ricoh619_read(struct i2c_client *client, + u8 reg, uint8_t *val) +{ + int ret; + + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) { + dev_err(&client->dev, "failed reading at 0x%02x\n", reg); + return ret; + } + + *val = (uint8_t)ret; + dev_dbg(&client->dev, "ricoh619: reg read reg=%x, val=%x\n", + reg, *val); + return 0; +} + +static inline int __ricoh619_bulk_reads(struct i2c_client *client, u8 reg, + int len, uint8_t *val) +{ + int ret; + int i; + + ret = i2c_smbus_read_i2c_block_data(client, reg, len, val); + if (ret < 0) { + dev_err(&client->dev, "failed reading from 0x%02x\n", reg); + return ret; + } + for (i = 0; i < len; ++i) { + dev_dbg(&client->dev, "ricoh619: reg read reg=%x, val=%x\n", + reg + i, *(val + i)); + } + return 0; +} + +static inline int __ricoh619_write(struct i2c_client *client, + u8 reg, uint8_t val) +{ + int ret; + + dev_dbg(&client->dev, "ricoh619: reg write reg=%x, val=%x\n", + reg, val); + ret = i2c_smbus_write_byte_data(client, reg, val); + if (ret < 0) { + dev_err(&client->dev, "failed writing 0x%02x to 0x%02x\n", + val, reg); + return ret; + } + + return 0; +} + +static inline int __ricoh619_bulk_writes(struct i2c_client *client, u8 reg, + int len, uint8_t *val) +{ + int ret; + int i; + + for (i = 0; i < len; ++i) { + dev_dbg(&client->dev, "ricoh619: reg write reg=%x, val=%x\n", + reg + i, *(val + i)); + } + + ret = i2c_smbus_write_i2c_block_data(client, reg, len, val); + if (ret < 0) { + dev_err(&client->dev, "failed writings to 0x%02x\n", reg); + return ret; + } + + return 0; +} + +static inline int set_bank_ricoh619(struct device *dev, int bank) +{ + struct ricoh619 *ricoh619 = dev_get_drvdata(dev); + int ret; + + if (bank != (bank & 1)) + return -EINVAL; + if (bank == ricoh619->bank_num) + return 0; + ret = __ricoh619_write(to_i2c_client(dev), RICOH619_REG_BANKSEL, bank); + if (!ret) + ricoh619->bank_num = bank; + + return ret; +} + +int ricoh619_write(struct device *dev, u8 reg, uint8_t val) +{ + struct ricoh619 *ricoh619 = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&ricoh619->io_lock); + ret = set_bank_ricoh619(dev, 0); + if( !ret ) + ret = __ricoh619_write(to_i2c_client(dev), reg, val); + mutex_unlock(&ricoh619->io_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(ricoh619_write); + +int ricoh619_write_bank1(struct device *dev, u8 reg, uint8_t val) +{ + struct ricoh619 *ricoh619 = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&ricoh619->io_lock); + ret = set_bank_ricoh619(dev, 1); + if( !ret ) + ret = __ricoh619_write(to_i2c_client(dev), reg, val); + mutex_unlock(&ricoh619->io_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(ricoh619_write_bank1); + +int ricoh619_bulk_writes(struct device *dev, u8 reg, u8 len, uint8_t *val) +{ + struct ricoh619 *ricoh619 = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&ricoh619->io_lock); + ret = set_bank_ricoh619(dev, 0); + if( !ret ) + ret = __ricoh619_bulk_writes(to_i2c_client(dev), reg, len, val); + mutex_unlock(&ricoh619->io_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(ricoh619_bulk_writes); + +int ricoh619_bulk_writes_bank1(struct device *dev, u8 reg, u8 len, uint8_t *val) +{ + struct ricoh619 *ricoh619 = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&ricoh619->io_lock); + ret = set_bank_ricoh619(dev, 1); + if( !ret ) + ret = __ricoh619_bulk_writes(to_i2c_client(dev), reg, len, val); + mutex_unlock(&ricoh619->io_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(ricoh619_bulk_writes_bank1); + +int ricoh619_read(struct device *dev, u8 reg, uint8_t *val) +{ + struct ricoh619 *ricoh619 = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&ricoh619->io_lock); + ret = set_bank_ricoh619(dev, 0); + if( !ret ) + ret = __ricoh619_read(to_i2c_client(dev), reg, val); + mutex_unlock(&ricoh619->io_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(ricoh619_read); + +int ricoh619_read_bank1(struct device *dev, u8 reg, uint8_t *val) +{ + struct ricoh619 *ricoh619 = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&ricoh619->io_lock); + ret = set_bank_ricoh619(dev, 1); + if( !ret ) + ret = __ricoh619_read(to_i2c_client(dev), reg, val); + mutex_unlock(&ricoh619->io_lock); + + return ret; +} + +EXPORT_SYMBOL_GPL(ricoh619_read_bank1); + +int ricoh619_bulk_reads(struct device *dev, u8 reg, u8 len, uint8_t *val) +{ + struct ricoh619 *ricoh619 = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&ricoh619->io_lock); + ret = set_bank_ricoh619(dev, 0); + if( !ret ) + ret = __ricoh619_bulk_reads(to_i2c_client(dev), reg, len, val); + mutex_unlock(&ricoh619->io_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(ricoh619_bulk_reads); + +int ricoh619_bulk_reads_bank1(struct device *dev, u8 reg, u8 len, uint8_t *val) +{ + struct ricoh619 *ricoh619 = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&ricoh619->io_lock); + ret = set_bank_ricoh619(dev, 1); + if( !ret ) + ret = __ricoh619_bulk_reads(to_i2c_client(dev), reg, len, val); + mutex_unlock(&ricoh619->io_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(ricoh619_bulk_reads_bank1); + +int ricoh619_set_bits(struct device *dev, u8 reg, uint8_t bit_mask) +{ + struct ricoh619 *ricoh619 = dev_get_drvdata(dev); + uint8_t reg_val; + int ret = 0; + + mutex_lock(&ricoh619->io_lock); + ret = set_bank_ricoh619(dev, 0); + if (!ret) { + ret = __ricoh619_read(to_i2c_client(dev), reg, ®_val); + if (ret) + goto out; + + if ((reg_val & bit_mask) != bit_mask) { + reg_val |= bit_mask; + ret = __ricoh619_write(to_i2c_client(dev), reg, + reg_val); + } + } +out: + mutex_unlock(&ricoh619->io_lock); + return ret; +} +EXPORT_SYMBOL_GPL(ricoh619_set_bits); + +int ricoh619_clr_bits(struct device *dev, u8 reg, uint8_t bit_mask) +{ + struct ricoh619 *ricoh619 = dev_get_drvdata(dev); + uint8_t reg_val; + int ret = 0; + + mutex_lock(&ricoh619->io_lock); + ret = set_bank_ricoh619(dev, 0); + if( !ret ){ + ret = __ricoh619_read(to_i2c_client(dev), reg, ®_val); + if (ret) + goto out; + + if (reg_val & bit_mask) { + reg_val &= ~bit_mask; + ret = __ricoh619_write(to_i2c_client(dev), reg, + reg_val); + } + } +out: + mutex_unlock(&ricoh619->io_lock); + return ret; +} +EXPORT_SYMBOL_GPL(ricoh619_clr_bits); + +int ricoh619_update(struct device *dev, u8 reg, uint8_t val, uint8_t mask) +{ + struct ricoh619 *ricoh619 = dev_get_drvdata(dev); + uint8_t reg_val; + int ret = 0; + + mutex_lock(&ricoh619->io_lock); + ret = set_bank_ricoh619(dev, 0); + if( !ret ){ + ret = __ricoh619_read(ricoh619->client, reg, ®_val); + if (ret) + goto out; + + if ((reg_val & mask) != val) { + reg_val = (reg_val & ~mask) | (val & mask); + ret = __ricoh619_write(ricoh619->client, reg, reg_val); + } + } +out: + mutex_unlock(&ricoh619->io_lock); + return ret; +} +EXPORT_SYMBOL_GPL(ricoh619_update); + +int ricoh619_update_bank1(struct device *dev, u8 reg, uint8_t val, uint8_t mask) +{ + struct ricoh619 *ricoh619 = dev_get_drvdata(dev); + uint8_t reg_val; + int ret = 0; + + mutex_lock(&ricoh619->io_lock); + ret = set_bank_ricoh619(dev, 1); + if( !ret ){ + ret = __ricoh619_read(ricoh619->client, reg, ®_val); + if (ret) + goto out; + + if ((reg_val & mask) != val) { + reg_val = (reg_val & ~mask) | (val & mask); + ret = __ricoh619_write(ricoh619->client, reg, reg_val); + } + } +out: + mutex_unlock(&ricoh619->io_lock); + return ret; +} + +static struct i2c_client *ricoh619_i2c_client; +int ricoh619_power_off(void) +{ + int ret; + uint8_t val; + int err = -1; + struct ricoh619 *ricoh619 = g_ricoh619; + + val = g_soc; + val &= 0x7f; + ret = ricoh619_write(ricoh619->dev, RICOH619_PSWR, val); + if (ret < 0) + dev_err(ricoh619->dev, "Error in writing PSWR_REG\n"); + + if (g_fg_on_mode == 0) { + ret = ricoh619_clr_bits(ricoh619->dev, + RICOH619_FG_CTRL, 0x01); + if (ret < 0) + dev_err(ricoh619->dev, "Error in writing FG_CTRL\n"); + } + + if (!ricoh619_i2c_client) + return -EINVAL; +//__ricoh618_write(ricoh618_i2c_client, RICOH618_PWR_REP_CNT, 0x0); //Not repeat power ON after power off(Power Off/N_OE) +// __ricoh618_write(ricoh618_i2c_client, RICOH618_PWR_SLP_CNT, 0x1); //Power OFF + ret = ricoh619_clr_bits(ricoh619->dev,RICOH619_PWR_REP_CNT,(0x1<<0));//Not repeat power ON after power off(Power Off/N_OE) + ret = ricoh619_set_bits(ricoh619->dev, RICOH619_PWR_SLP_CNT,(0x1<<0));//Power OFF + if (ret < 0) { + printk("ricoh619 power off error!\n"); + return err; + } + return 0; +} +EXPORT_SYMBOL_GPL(ricoh619_power_off); + +static int ricoh619_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + struct ricoh619 *ricoh619 = container_of(gc, struct ricoh619, gpio_chip); + uint8_t val; + int ret; + + ret = ricoh619_read(ricoh619->dev, RICOH619_GPIO_MON_IOIN, &val); + if (ret < 0) + return ret; + + return ((val & (0x1 << offset)) != 0); +} + +static void ricoh619_gpio_set(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct ricoh619 *ricoh619 = container_of(gc, struct ricoh619, gpio_chip); + if (value) + ricoh619_set_bits(ricoh619->dev, RICOH619_GPIO_IOOUT, + 1 << offset); + else + ricoh619_clr_bits(ricoh619->dev, RICOH619_GPIO_IOOUT, + 1 << offset); +} + +static int ricoh619_gpio_input(struct gpio_chip *gc, unsigned offset) +{ + struct ricoh619 *ricoh619 = container_of(gc, struct ricoh619, gpio_chip); + + return ricoh619_clr_bits(ricoh619->dev, RICOH619_GPIO_IOSEL, + 1 << offset); +} + +static int ricoh619_gpio_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct ricoh619 *ricoh619 = container_of(gc, struct ricoh619, gpio_chip); + + ricoh619_gpio_set(gc, offset, value); + return ricoh619_set_bits(ricoh619->dev, RICOH619_GPIO_IOSEL, + 1 << offset); +} + +static int ricoh619_gpio_to_irq(struct gpio_chip *gc, unsigned off) +{ + struct ricoh619 *ricoh619 = container_of(gc, struct ricoh619, gpio_chip); + + if ((off >= 0) && (off < 8)) + return ricoh619->irq_base + RICOH619_IRQ_GPIO0 + off; + + return -EIO; +} + + +static void __devinit ricoh619_gpio_init(struct ricoh619 *ricoh619, + struct ricoh619_platform_data *pdata) +{ + int ret; + int i; + struct ricoh619_gpio_init_data *ginit; + + if (pdata->gpio_base <= 0) + return; + + for (i = 0; i < pdata->num_gpioinit_data; ++i) { + ginit = &pdata->gpio_init_data[i]; + + if (!ginit->init_apply) + continue; + + if (ginit->output_mode_en) { + /* GPIO output mode */ + if (ginit->output_val) + /* output H */ + ret = ricoh619_set_bits(ricoh619->dev, + RICOH619_GPIO_IOOUT, 1 << i); + else + /* output L */ + ret = ricoh619_clr_bits(ricoh619->dev, + RICOH619_GPIO_IOOUT, 1 << i); + if (!ret) + ret = ricoh619_set_bits(ricoh619->dev, + RICOH619_GPIO_IOSEL, 1 << i); + } else + /* GPIO input mode */ + ret = ricoh619_clr_bits(ricoh619->dev, + RICOH619_GPIO_IOSEL, 1 << i); + + /* if LED function enabled in OTP */ + if (ginit->led_mode) { + /* LED Mode 1 */ + if (i == 0) /* GP0 */ + ret = ricoh619_set_bits(ricoh619->dev, + RICOH619_GPIO_LED_FUNC, + 0x04 | (ginit->led_func & 0x03)); + if (i == 1) /* GP1 */ + ret = ricoh619_set_bits(ricoh619->dev, + RICOH619_GPIO_LED_FUNC, + 0x40 | (ginit->led_func & 0x03) << 4); + + } + + + if (ret < 0) + dev_err(ricoh619->dev, "Gpio %d init " + "dir configuration failed: %d\n", i, ret); + + } + + ricoh619->gpio_chip.owner = THIS_MODULE; + ricoh619->gpio_chip.label = ricoh619->client->name; + ricoh619->gpio_chip.dev = ricoh619->dev; + ricoh619->gpio_chip.base = pdata->gpio_base; + ricoh619->gpio_chip.ngpio = RICOH619_NR_GPIO; + ricoh619->gpio_chip.can_sleep = 1; + + ricoh619->gpio_chip.direction_input = ricoh619_gpio_input; + ricoh619->gpio_chip.direction_output = ricoh619_gpio_output; + ricoh619->gpio_chip.set = ricoh619_gpio_set; + ricoh619->gpio_chip.get = ricoh619_gpio_get; + ricoh619->gpio_chip.to_irq = ricoh619_gpio_to_irq; + + ret = gpiochip_add(&ricoh619->gpio_chip); + if (ret) + dev_warn(ricoh619->dev, "GPIO registration failed: %d\n", ret); +} + +static int ricoh619_remove_subdev(struct device *dev, void *unused) +{ + platform_device_unregister(to_platform_device(dev)); + return 0; +} + +static int ricoh619_remove_subdevs(struct ricoh619 *ricoh619) +{ + return device_for_each_child(ricoh619->dev, NULL, + ricoh619_remove_subdev); +} + +static int __devinit ricoh619_add_subdevs(struct ricoh619 *ricoh619, + struct ricoh619_platform_data *pdata) +{ + struct ricoh619_subdev_info *subdev; + struct platform_device *pdev; + int i, ret = 0; + + for (i = 0; i < pdata->num_subdevs; i++) { + subdev = &pdata->subdevs[i]; + + pdev = platform_device_alloc(subdev->name, subdev->id); + + pdev->dev.parent = ricoh619->dev; + pdev->dev.platform_data = subdev->platform_data; + + ret = platform_device_add(pdev); + if (ret) + goto failed; + } + return 0; + +failed: + ricoh619_remove_subdevs(ricoh619); + return ret; +} + +#ifdef CONFIG_DEBUG_FS +#include +#include +static void print_regs(const char *header, struct seq_file *s, + struct i2c_client *client, int start_offset, + int end_offset) +{ + uint8_t reg_val; + int i; + int ret; + + seq_printf(s, "%s\n", header); + for (i = start_offset; i <= end_offset; ++i) { + ret = __ricoh619_read(client, i, ®_val); + if (ret >= 0) + seq_printf(s, "Reg 0x%02x Value 0x%02x\n", i, reg_val); + } + seq_printf(s, "------------------\n"); +} + +static int dbg_ricoh_show(struct seq_file *s, void *unused) +{ + struct ricoh619 *ricoh = s->private; + struct i2c_client *client = ricoh->client; + + seq_printf(s, "RICOH619 Registers\n"); + seq_printf(s, "------------------\n"); + + print_regs("System Regs", s, client, 0x0, 0x05); + print_regs("Power Control Regs", s, client, 0x07, 0x2B); + print_regs("DCDC Regs", s, client, 0x2C, 0x43); + print_regs("LDO Regs", s, client, 0x44, 0x61); + print_regs("ADC Regs", s, client, 0x64, 0x8F); + print_regs("GPIO Regs", s, client, 0x90, 0x98); + print_regs("INTC Regs", s, client, 0x9C, 0x9E); + print_regs("RTC Regs", s, client, 0xA0, 0xAF); + print_regs("OPT Regs", s, client, 0xB0, 0xB1); + print_regs("CHG Regs", s, client, 0xB2, 0xDF); + print_regs("FUEL Regs", s, client, 0xE0, 0xFC); + return 0; +} + +static int dbg_ricoh_open(struct inode *inode, struct file *file) +{ + return single_open(file, dbg_ricoh_show, inode->i_private); +} + +static const struct file_operations debug_fops = { + .open = dbg_ricoh_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +static void __init ricoh619_debuginit(struct ricoh619 *ricoh) +{ + (void)debugfs_create_file("ricoh619", S_IRUGO, NULL, + ricoh, &debug_fops); +} +#else +static void print_regs(const char *header, struct i2c_client *client, + int start_offset, int end_offset) +{ + uint8_t reg_val; + int i; + int ret; + + printk(KERN_INFO "%s\n", header); + for (i = start_offset; i <= end_offset; ++i) { + ret = __ricoh619_read(client, i, ®_val); + if (ret >= 0) + printk(KERN_INFO "Reg 0x%02x Value 0x%02x\n", + i, reg_val); + } + printk(KERN_INFO "------------------\n"); +} +static void __init ricoh619_debuginit(struct ricoh619 *ricoh) +{ + struct i2c_client *client = ricoh->client; + + printk(KERN_INFO "RICOH619 Registers\n"); + printk(KERN_INFO "------------------\n"); + + print_regs("System Regs", client, 0x0, 0x05); + print_regs("Power Control Regs", client, 0x07, 0x2B); + print_regs("DCDC Regs", client, 0x2C, 0x43); + print_regs("LDO Regs", client, 0x44, 0x5C); + print_regs("ADC Regs", client, 0x64, 0x8F); + print_regs("GPIO Regs", client, 0x90, 0x9B); + print_regs("INTC Regs", client, 0x9C, 0x9E); + print_regs("OPT Regs", client, 0xB0, 0xB1); + print_regs("CHG Regs", client, 0xB2, 0xDF); + print_regs("FUEL Regs", client, 0xE0, 0xFC); + + return 0; +} +#endif + +static void __devinit ricoh619_noe_init(struct ricoh619 *ricoh) +{ + struct i2c_client *client = ricoh->client; + + __ricoh619_write(client, RICOH619_PWR_NOE_TIMSET, 0x0); //N_OE timer setting to 128mS + __ricoh619_write(client, RICOH619_PWR_REP_CNT, 0x1); //Repeat power ON after reset (Power Off/N_OE) +} + +static int ricoh619_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ricoh619 *ricoh619; + struct ricoh619_platform_data *pdata = client->dev.platform_data; + int ret; + uint8_t control; + printk(KERN_INFO "PMU: %s:\n", __func__); + + ricoh619 = kzalloc(sizeof(struct ricoh619), GFP_KERNEL); + if (ricoh619 == NULL) + return -ENOMEM; + + ricoh619->client = client; + ricoh619->dev = &client->dev; + i2c_set_clientdata(client, ricoh619); + + mutex_init(&ricoh619->io_lock); + + ret = ricoh619_read(ricoh619->dev, 0x36, &control); + if ((control < 0) || (control == 0xff)) { + printk(KERN_INFO "The device is not ricoh619\n"); + return 0; + } + /***************set noe time 128ms**************/ + ret = ricoh619_set_bits(ricoh619->dev,0x11,(0x1 <<3)); + ret = ricoh619_clr_bits(ricoh619->dev,0x11,(0x7 <<0)); + ret = ricoh619_clr_bits(ricoh619->dev,0x11,(0x1 <<3)); + /**********************************************/ + + /***************set PKEY long press time 0sec*******/ + ret = ricoh619_set_bits(ricoh619->dev,0x10,(0x1 <<7)); + ret = ricoh619_clr_bits(ricoh619->dev,0x10,(0x1 <<3)); + ret = ricoh619_clr_bits(ricoh619->dev,0x10,(0x1 <<7)); + /**********************************************/ + + ricoh619->bank_num = 0; + +// ret = pdata->init_port(client->irq); // For init PMIC_IRQ port + if (client->irq) { + ret = ricoh619_irq_init(ricoh619, client->irq, pdata->irq_base); + if (ret) { + dev_err(&client->dev, "IRQ init failed: %d\n", ret); + goto err_irq_init; + } + } + + ret = ricoh619_add_subdevs(ricoh619, pdata); + if (ret) { + dev_err(&client->dev, "add devices failed: %d\n", ret); + goto err_add_devs; + } + ricoh619_noe_init(ricoh619); + + g_ricoh619 = ricoh619; + if (pdata && pdata->pre_init) { + ret = pdata->pre_init(ricoh619); + if (ret != 0) { + dev_err(ricoh619->dev, "pre_init() failed: %d\n", ret); + goto err; + } + } + + //ricoh619_gpio_init(ricoh619, pdata); + + ricoh619_debuginit(ricoh619); + + if (pdata && pdata->post_init) { + ret = pdata->post_init(ricoh619); + if (ret != 0) { + dev_err(ricoh619->dev, "post_init() failed: %d\n", ret); + goto err; + } + } + + ricoh619_i2c_client = client; + return 0; +err: + mfd_remove_devices(ricoh619->dev); + kfree(ricoh619); +err_add_devs: + if (client->irq) + ricoh619_irq_exit(ricoh619); +err_irq_init: + kfree(ricoh619); + return ret; +} + +static int __devexit ricoh619_i2c_remove(struct i2c_client *client) +{ + struct ricoh619 *ricoh619 = i2c_get_clientdata(client); + + if (client->irq) + ricoh619_irq_exit(ricoh619); + + ricoh619_remove_subdevs(ricoh619); + kfree(ricoh619); + return 0; +} + +#ifdef CONFIG_PM +static int ricoh619_i2c_suspend(struct i2c_client *client, pm_message_t state) +{ + if (client->irq) + disable_irq(client->irq); + return 0; +} + +int pwrkey_wakeup; +static int ricoh619_i2c_resume(struct i2c_client *client) +{ + uint8_t reg_val; + int ret; + + ret = __ricoh619_read(client, RICOH619_INT_IR_SYS, ®_val); + if(reg_val & 0x01) { //If PWR_KEY wakeup +// printk("PMU: %s: PWR_KEY Wakeup\n",__func__); + pwrkey_wakeup = 1; + __ricoh619_write(client, RICOH619_INT_IR_SYS, 0x0); //Clear PWR_KEY IRQ + } + + enable_irq(client->irq); + return 0; +} + +#endif + +static const struct i2c_device_id ricoh619_i2c_id[] = { + {"ricoh619", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, ricoh619_i2c_id); + +static struct i2c_driver ricoh619_i2c_driver = { + .driver = { + .name = "ricoh619", + .owner = THIS_MODULE, + }, + .probe = ricoh619_i2c_probe, + .remove = __devexit_p(ricoh619_i2c_remove), +#ifdef CONFIG_PM + .suspend = ricoh619_i2c_suspend, + .resume = ricoh619_i2c_resume, +#endif + .id_table = ricoh619_i2c_id, +}; + + +static int __init ricoh619_i2c_init(void) +{ + int ret = -ENODEV; + ret = i2c_add_driver(&ricoh619_i2c_driver); + if (ret != 0) + pr_err("Failed to register I2C driver: %d\n", ret); + + return ret; +} + +subsys_initcall_sync(ricoh619_i2c_init); + +static void __exit ricoh619_i2c_exit(void) +{ + i2c_del_driver(&ricoh619_i2c_driver); +} + +module_exit(ricoh619_i2c_exit); + +MODULE_DESCRIPTION("RICOH RC5T619 PMU multi-function core driver"); +MODULE_AUTHOR("zhangqing "); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig old mode 100755 new mode 100644 index 988b84868a0b..4935297f3bfc --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -71,6 +71,13 @@ config WM8350_POWER Say Y here to enable support for the power management unit provided by the Wolfson Microelectronics WM8350 PMIC. +config BATTERY_RICOH619 + tristate "Ricoh RC5T619 PMIC battery driver" + depends on MFD_RICOH619 && I2C && GENERIC_HARDIRQS + help + Say Y to enable support for the battery control of the Ricoh RC5T619 + Power Management device. + config TEST_POWER tristate "Test power driver" help diff --git a/drivers/power/Makefile b/drivers/power/Makefile index da73f4d6586e..fdc582a4f3c4 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -48,4 +48,4 @@ obj-$(CONFIG_BATTERY_RK30_ADC) += rk30_adc_battery.o obj-$(CONFIG_PLAT_RK) += rk29_charger_display.o obj-$(CONFIG_BATTERY_RK30_ADC_FAC) += rk30_factory_adc_battery.o obj-$(CONFIG_CW2015_BATTERY) += cw2015_battery.o - +obj-$(CONFIG_BATTERY_RICOH619) += ricoh619-battery.o diff --git a/drivers/power/ricoh619-battery.c b/drivers/power/ricoh619-battery.c new file mode 100644 index 000000000000..0124ca53c36a --- /dev/null +++ b/drivers/power/ricoh619-battery.c @@ -0,0 +1,2338 @@ +/* + * drivers/power/ricoh619-battery.c + * + * Charger driver for RICOH RC5T619 power management chip. + * + * Copyright (C) 2012-2013 RICOH COMPANY,LTD + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* define for function */ +#define ENABLE_FUEL_GAUGE_FUNCTION +#define ENABLE_LOW_BATTERY_DETECTION +#define ENABLE_FACTORY_MODE +#define ENABLE_FG_KEEP_ON_MODE + +/* define for current limit. unit is mA */ +/* if value is "0", these settings set by OTP */ +#define RICOH619_MAX_CHARGE_CURRENT 0 /* mA */ +#define RICOH619_MAX_ADP_CURRENT 0 /* mA */ +#define RICOH619_MAX_USB_CURRENT 0 /* mA */ +#define RICOH619_CHARGE_COMPLETION_CURRENT 200 /* mA Range 50~200 + * (Step 50) */ +#define RICOH619_FULL_CHARGING_VOLTAGE 0 /* mv can set 4050, + * 4100, 4150, 4200, + * 4350(default 4100) */ +#define RICOH619_RE_CHARGING_VOLTAGE 0 /* mv can set 3850, + * 3900, 3950, 4000, + * 4100(default 3900) */ + +/* FG setting */ +#define CUTOFF_VOL 0 /* mV "0" means cutoff + * voltage = original + * OCV table value */ +#define RICOH619_REL1_SEL_VALUE 64 /* mv Range 0~240 + * (Step 16) */ + +enum int_type { + SYS_INT = 0x01, + DCDC_INT = 0x02, + ADC_INT = 0x08, + GPIO_INT = 0x10, + CHG_INT = 0x40, +}; + +#ifdef ENABLE_FUEL_GAUGE_FUNCTION +/* define for FG parameter */ +#define RICOH619_MONITOR_START_TIME 15 +#define RICOH619_FG_RESET_TIME 6 +#define RICOH619_FG_STABLE_TIME 120 +#define RICOH619_DISPLAY_UPDATE_TIME 60 +#define RICOH619_SOCA_DISP_UPDATE_TIME 60 +#define RICOH619_MAX_RESET_SOC_DIFF 5 + +/* define for FG status */ +enum { + RICOH619_SOCA_START, + RICOH619_SOCA_UNSTABLE, + RICOH619_SOCA_FG_RESET, + RICOH619_SOCA_DISP, + RICOH619_SOCA_STABLE, + RICOH619_SOCA_ZERO, +}; +#endif + +#ifdef ENABLE_LOW_BATTERY_DETECTION +#define LOW_BATTERY_DETECTION_TIME 10 +#endif + +struct ricoh619_soca_info { + int Rbat; + int n_cap; + int ocv_table[11]; + int soc; /* Latest FG SOC value */ + int displayed_soc; + int status; /* SOCA status 0: Not initial; 5: Finished */ + int stable_count; + int chg_status; /* chg_status */ + int soc_delta; /* soc delta for status3(DISP) */ + int cc_delta; + int last_soc; + int ready_fg; + int reset_count; + int reset_soc[3]; +}; + +struct ricoh619_battery_info { + struct device *dev; + struct power_supply battery; + struct delayed_work monitor_work; + struct delayed_work usb_work; + struct delayed_work displayed_work; + struct delayed_work charge_stable_work; + struct delayed_work changed_work; +#ifdef ENABLE_LOW_BATTERY_DETECTION + struct delayed_work low_battery_work; +#endif + + struct work_struct irq_work; /* for Charging & VUSB/VADP */ + struct work_struct usb_irq_work; /* for ADC_VUSB */ + + struct workqueue_struct *monitor_wqueue; + struct workqueue_struct *workqueue; /* for Charging & VUSB/VADP */ + struct workqueue_struct *usb_workqueue; /* for ADC_VUSB */ + +#ifdef ENABLE_FACTORY_MODE + struct delayed_work factory_mode_work; + struct workqueue_struct *factory_mode_wqueue; +#endif + + struct mutex lock; + unsigned long monitor_time; + int adc_vdd_mv; + int multiple; + int alarm_vol_mv; + int status; + int min_voltage; + int max_voltage; + int cur_voltage; + int capacity; + int battery_temp; + int time_to_empty; + int time_to_full; + int chg_ctr; + int chg_stat1; + unsigned present:1; + u16 delay; + struct ricoh619_soca_info *soca; + int first_pwon; + bool entry_factory_mode; +}; + +int charger_irq; +/* this value is for mfd fucntion */ +int g_soc; +int g_fg_on_mode; + +static void ricoh619_battery_work(struct work_struct *work) +{ + struct ricoh619_battery_info *info = container_of(work, + struct ricoh619_battery_info, monitor_work.work); + + RICOH_FG_DBG("PMU: %s\n", __func__); + power_supply_changed(&info->battery); + queue_delayed_work(info->monitor_wqueue, &info->monitor_work, + info->monitor_time); +} + +#ifdef ENABLE_FUEL_GAUGE_FUNCTION +static int measure_vbatt_FG(struct ricoh619_battery_info *info, int *data); +static int measure_Ibatt_FG(struct ricoh619_battery_info *info, int *data); +static int calc_capacity(struct ricoh619_battery_info *info); +static int get_OCV_init_Data(struct ricoh619_battery_info *info, int index); +static int get_OCV_voltage(struct ricoh619_battery_info *info, int index); +static int get_check_fuel_gauge_reg(struct ricoh619_battery_info *info, + int Reg_h, int Reg_l, int enable_bit); +static int calc_capacity_in_period(struct ricoh619_battery_info *info, + int *cc_cap, bool *is_charging); + +/* check charge status. + * if CHG not Complete && SOC == 100 -> Stop charge + * if CHG Complete && SOC =! 100 -> SOC reset + */ +static int check_charge_status(struct ricoh619_battery_info *info) +{ + uint8_t status; + uint8_t supply_state; + uint8_t charge_state; + int ret = 0; + int current_SOC; + + /* get power supply status */ + ret = ricoh619_read(info->dev->parent, CHGSTATE_REG, &status); + if (ret < 0) { + dev_err(info->dev, + "Error in reading the control register\n"); + return ret; + } + + + charge_state = (status & 0x1F); + supply_state = ((status & 0xC0) >> 6); + + current_SOC = (info->soca->displayed_soc + 50)/100; + + if (charge_state == CHG_STATE_CHG_COMPLETE) { + /* check SOC */ + if (current_SOC != 100) { + ret = ricoh619_write(info->dev->parent, + FG_CTRL_REG, 0x51); + if (ret < 0) { + dev_err(info->dev, "Error in writing the control register\n"); + return ret; + } + info->soca->ready_fg = 0; + + info->soca->displayed_soc = 100 * 100; + + info->soca->status = RICOH619_SOCA_STABLE; + } + + } else { /* chg not complete */ + if (current_SOC == 100) { + ret = ricoh619_clr_bits(info->dev->parent, + CHGCTL1_REG, 0x03); + if (ret < 0) { + dev_err(info->dev, "Error in writing the control register\n"); + return ret; + } + info->soca->status = RICOH619_SOCA_STABLE; + } else { + ret = ricoh619_set_bits(info->dev->parent, + CHGCTL1_REG, 0x03); + if (ret < 0) { + dev_err(info->dev, "Error in writing the control register\n"); + return ret; + } + } + } + return ret; +} + +static int calc_ocv(struct ricoh619_battery_info *info) +{ + int Vbat = 0; + int Ibat = 0; + int ret; + int ocv; + + ret = measure_vbatt_FG(info, &Vbat); + ret = measure_Ibatt_FG(info, &Ibat); + + ocv = Vbat - Ibat * info->soca->Rbat; + + return ocv; +} + +/** +* Calculate Capacity in a period +* - read CC_SUM & FA_CAP from Coulom Counter +* - and calculate Capacity. +* @cc_cap: capacity in a period, unit 0.01% +* @is_charging: Flag of charging current direction +* TRUE : charging (plus) +* FALSE: discharging (minus) +**/ +static int calc_capacity_in_period(struct ricoh619_battery_info *info, + int *cc_cap, bool *is_charging) +{ + int err; + uint8_t cc_sum_reg[4]; + uint8_t cc_clr[4] = {0, 0, 0, 0}; + uint8_t fa_cap_reg[2]; + uint16_t fa_cap; + uint32_t cc_sum; + + *is_charging = true; /* currrent state initialize -> charging */ + + if (info->entry_factory_mode) + return 0; + + /* Disable Charging/Completion Interrupt */ + err = ricoh619_set_bits(info->dev->parent, + RICOH619_INT_MSK_CHGSTS1, 0x01); + if (err < 0) + goto out; + + /* In suspend - disable charging */ + err = ricoh619_set_bits(info->dev->parent, RICOH619_CHG_CTL1, 0x08); + if (err < 0) + goto out; + /* CC_pause enter */ + err = ricoh619_write(info->dev->parent, CC_CTRL_REG, 0x01); + if (err < 0) + goto out; + /* Read CC_SUM */ + err = ricoh619_bulk_reads(info->dev->parent, + CC_SUMREG3_REG, 4, cc_sum_reg); + if (err < 0) + goto out; + + /* CC_SUM <- 0 */ + err = ricoh619_bulk_writes(info->dev->parent, + CC_SUMREG3_REG, 4, cc_clr); + if (err < 0) + goto out; + + /* CC_pause exist */ + err = ricoh619_write(info->dev->parent, CC_CTRL_REG, 0); + if (err < 0) + goto out; + /* out suspend - enable charging */ + err = ricoh619_clr_bits(info->dev->parent, RICOH619_CHG_CTL1, 0x08); + if (err < 0) + goto out; + + udelay(1000); + + /* Clear Charging Interrupt status */ + err = ricoh619_clr_bits(info->dev->parent, + RICOH619_INT_IR_CHGSTS1, 0x01); + if (err < 0) + goto out; + + /* ricoh619_read(info->dev->parent, RICOH619_INT_IR_CHGSTS1, &val); + RICOH_FG_DBG("INT_IR_CHGSTS1 = 0x%x\n",val); */ + + /* Enable Charging Interrupt */ + err = ricoh619_clr_bits(info->dev->parent, + RICOH619_INT_MSK_CHGSTS1, 0x01); + if (err < 0) + goto out; + + /* Read FA_CAP */ + err = ricoh619_bulk_reads(info->dev->parent, + FA_CAP_H_REG, 2, fa_cap_reg); + if (err < 0) + goto out; + + /* fa_cap = *(uint16_t*)fa_cap_reg & 0x7fff; */ + fa_cap = (fa_cap_reg[0] << 8 | fa_cap_reg[1]) & 0x7fff; + + /* calculation two's complement of CC_SUM */ + cc_sum = cc_sum_reg[0] << 24 | cc_sum_reg[1] << 16 | + cc_sum_reg[2] << 8 | cc_sum_reg[3]; + + /* cc_sum = *(uint32_t*)cc_sum_reg; */ + if (cc_sum & 0x80000000) { + cc_sum = (cc_sum^0xffffffff)+0x01; + *is_charging = false; /* discharge */ + } + + *cc_cap = cc_sum*25/9/fa_cap; /* CC_SUM/3600/FA_CAP */ + + return 0; +out: + dev_err(info->dev, "Error !!-----\n"); + return err; +} + +static void ricoh619_displayed_work(struct work_struct *work) +{ + int err; + uint8_t val; + int soc_round; + int last_soc_round; + int last_disp_round; + int displayed_soc_temp; + int cc_cap = 0; + bool is_charging = true; + + struct ricoh619_battery_info *info = container_of(work, + struct ricoh619_battery_info, displayed_work.work); + + if (info->entry_factory_mode) { + info->soca->status = RICOH619_SOCA_STABLE; + info->soca->displayed_soc = -EINVAL; + info->soca->ready_fg = 0; + return; + } + + mutex_lock(&info->lock); + + if ((RICOH619_SOCA_START == info->soca->status) + || (RICOH619_SOCA_STABLE == info->soca->status)) + info->soca->ready_fg = 1; + + if (RICOH619_SOCA_STABLE == info->soca->status) { + info->soca->soc = calc_capacity(info) * 100; + info->soca->displayed_soc = info->soca->soc; + } else if (RICOH619_SOCA_DISP == info->soca->status) { + + info->soca->soc = calc_capacity(info) * 100; + + soc_round = info->soca->soc / 100; + last_soc_round = info->soca->last_soc / 100; + last_disp_round = (info->soca->displayed_soc + 50) / 100; + + info->soca->soc_delta = + info->soca->soc_delta + (soc_round - last_soc_round); + + info->soca->last_soc = info->soca->soc; + + /* six case */ + if (last_disp_round == soc_round) { + /* if SOC == DISPLAY move to stable */ + info->soca->displayed_soc = info->soca->soc ; + info->soca->status = RICOH619_SOCA_STABLE; + + } else if ((soc_round == 100) || (soc_round == 0)) { + /* if SOC is 0% or 100% , finish display state*/ + info->soca->displayed_soc = info->soca->soc ; + info->soca->status = RICOH619_SOCA_STABLE; + + } else if ((info->soca->chg_status) == + (POWER_SUPPLY_STATUS_CHARGING)) { + /* Charge */ + if (last_disp_round < soc_round) { + /* Case 1 : Charge, Display < SOC */ + if (info->soca->soc_delta >= 1) { + info->soca->displayed_soc + = (last_disp_round + + info->soca->soc_delta)*100; + info->soca->soc_delta = 0; + } else { + info->soca->displayed_soc + = (last_disp_round + 1)*100; + } + + if (last_disp_round >= soc_round) { + info->soca->displayed_soc + = info->soca->soc ; + info->soca->status + = RICOH619_SOCA_STABLE; + } + } else if (last_disp_round > soc_round) { + /* Case 2 : Charge, Display > SOC */ + if (info->soca->soc_delta >= 3) { + info->soca->displayed_soc = + (last_disp_round + 1)*100; + info->soca->soc_delta = 0; + } + if (last_disp_round <= soc_round) { + info->soca->displayed_soc + = info->soca->soc ; + info->soca->status + = RICOH619_SOCA_STABLE; + } + } + } else { + /* Dis-Charge */ + if (last_disp_round > soc_round) { + /* Case 3 : Dis-Charge, Display > SOC */ + if (info->soca->soc_delta <= -1) { + info->soca->displayed_soc + = (last_disp_round + + info->soca->soc_delta)*100; + info->soca->soc_delta = 0; + } else { + info->soca->displayed_soc + = (last_disp_round - 1)*100; + } + if (last_disp_round <= soc_round) { + info->soca->displayed_soc + = info->soca->soc ; + info->soca->status + = RICOH619_SOCA_STABLE; + } + } else if (last_disp_round < soc_round) { + /* dis Charge, Display < SOC */ + if (info->soca->soc_delta <= -3) { + info->soca->displayed_soc + = (last_disp_round - 1)*100; + info->soca->soc_delta = 0; + } + if (last_disp_round >= soc_round) { + info->soca->displayed_soc + = info->soca->soc ; + info->soca->status + = RICOH619_SOCA_STABLE; + } + } + } + } else if (RICOH619_SOCA_UNSTABLE == info->soca->status + || RICOH619_SOCA_FG_RESET == info->soca->status) { + /* No update */ + } else if (RICOH619_SOCA_START == info->soca->status) { + err = ricoh619_read(info->dev->parent, PSWR_REG, &val); + val &= 0x7f; + if (info->first_pwon) { + info->soca->soc = calc_capacity(info) * 100; + if ((info->soca->soc == 0) && (calc_ocv(info) + < get_OCV_voltage(info, 0))) { + info->soca->displayed_soc = 0; + info->soca->status = RICOH619_SOCA_ZERO; + } else { + info->soca->displayed_soc = info->soca->soc; + info->soca->status = RICOH619_SOCA_UNSTABLE; + } + } else if (g_fg_on_mode && (val == 0x7f)) { + info->soca->soc = calc_capacity(info) * 100; + if ((info->soca->soc == 0) && (calc_ocv(info) + < get_OCV_voltage(info, 0))) { + info->soca->displayed_soc = 0; + info->soca->status = RICOH619_SOCA_ZERO; + } else { + info->soca->displayed_soc = info->soca->soc; + info->soca->status = RICOH619_SOCA_STABLE; + } + } else { + info->soca->soc = val * 100; + if ((err < 0) || (val == 0)) { + dev_err(info->dev, + "Error in reading PSWR_REG %d\n", err); + info->soca->soc + = calc_capacity(info) * 100 + 50; + } + + err = calc_capacity_in_period(info, &cc_cap, + &is_charging); + if (err < 0) + dev_err(info->dev, "Read cc_sum Error !!-----\n"); + + info->soca->cc_delta + = (is_charging == true) ? cc_cap : -cc_cap; + if (calc_ocv(info) < get_OCV_voltage(info, 0)) { + info->soca->displayed_soc = 0; + info->soca->status = RICOH619_SOCA_ZERO; + } else { + displayed_soc_temp + = info->soca->soc + info->soca->cc_delta; + displayed_soc_temp + = min(10000, displayed_soc_temp); + displayed_soc_temp = max(0, displayed_soc_temp); + info->soca->displayed_soc = displayed_soc_temp; + info->soca->status = RICOH619_SOCA_UNSTABLE; + } + } + } else if (RICOH619_SOCA_ZERO == info->soca->status) { + if (calc_ocv(info) > get_OCV_voltage(info, 0)) { + err = ricoh619_write(info->dev->parent, + FG_CTRL_REG, 0x51); + if (err < 0) + dev_err(info->dev, "Error in writing the control register\n"); + info->soca->status = RICOH619_SOCA_STABLE; + info->soca->ready_fg = 0; + } + info->soca->displayed_soc = 0; + } + + /* Ceck charge status */ + err = check_charge_status(info); + if (err < 0) + dev_err(info->dev, "Error in writing the control register\n"); + + if (g_fg_on_mode + && (info->soca->status == RICOH619_SOCA_STABLE)) { + err = ricoh619_write(info->dev->parent, PSWR_REG, 0x7f); + if (err < 0) + dev_err(info->dev, "Error in writing PSWR_REG\n"); + g_soc = 0x7F; + } else { + val = (info->soca->displayed_soc + 50)/100; + val &= 0x7f; + err = ricoh619_write(info->dev->parent, PSWR_REG, val); + if (err < 0) + dev_err(info->dev, "Error in writing PSWR_REG\n"); + + g_soc = (info->soca->displayed_soc + 50)/100; + + err = calc_capacity_in_period(info, &cc_cap, &is_charging); + if (err < 0) + dev_err(info->dev, "Read cc_sum Error !!-----\n"); + } + + if (0 == info->soca->ready_fg) + queue_delayed_work(info->monitor_wqueue, &info->displayed_work, + RICOH619_FG_RESET_TIME * HZ); + else if (RICOH619_SOCA_DISP == info->soca->status) + queue_delayed_work(info->monitor_wqueue, &info->displayed_work, + RICOH619_SOCA_DISP_UPDATE_TIME * HZ); + else + queue_delayed_work(info->monitor_wqueue, &info->displayed_work, + RICOH619_DISPLAY_UPDATE_TIME * HZ); + + mutex_unlock(&info->lock); + + return; +} + +static void ricoh619_stable_charge_countdown_work(struct work_struct *work) +{ + int ret; + int max = 0; + int min = 100; + int i; + struct ricoh619_battery_info *info = container_of(work, + struct ricoh619_battery_info, charge_stable_work.work); + + if (info->entry_factory_mode) + return; + + mutex_lock(&info->lock); + if (RICOH619_SOCA_FG_RESET == info->soca->status) + info->soca->ready_fg = 1; + + if (2 <= info->soca->stable_count) { + if (3 == info->soca->stable_count + && RICOH619_SOCA_FG_RESET == info->soca->status) { + ret = ricoh619_write(info->dev->parent, + FG_CTRL_REG, 0x51); + if (ret < 0) + dev_err(info->dev, "Error in writing the control register\n"); + info->soca->ready_fg = 0; + } + info->soca->stable_count = info->soca->stable_count - 1; + queue_delayed_work(info->monitor_wqueue, + &info->charge_stable_work, + RICOH619_FG_STABLE_TIME * HZ / 10); + } else if (0 >= info->soca->stable_count) { + /* Finished queue, ignore */ + } else if (1 == info->soca->stable_count) { + if (RICOH619_SOCA_UNSTABLE == info->soca->status) { + /* Judge if FG need reset or Not */ + info->soca->soc = calc_capacity(info) * 100; + if (info->chg_ctr != 0) { + queue_delayed_work(info->monitor_wqueue, + &info->charge_stable_work, + RICOH619_FG_STABLE_TIME * HZ / 10); + mutex_unlock(&info->lock); + return; + } + /* Do reset setting */ + ret = ricoh619_write(info->dev->parent, + FG_CTRL_REG, 0x51); + if (ret < 0) + dev_err(info->dev, "Error in writing the control register\n"); + + info->soca->ready_fg = 0; + info->soca->status = RICOH619_SOCA_FG_RESET; + + /* Delay for addition Reset Time (6s) */ + queue_delayed_work(info->monitor_wqueue, + &info->charge_stable_work, + RICOH619_FG_RESET_TIME*HZ); + } else if (RICOH619_SOCA_FG_RESET == info->soca->status) { + info->soca->reset_soc[2] = info->soca->reset_soc[1]; + info->soca->reset_soc[1] = info->soca->reset_soc[0]; + info->soca->reset_soc[0] = calc_capacity(info) * 100; + info->soca->reset_count++; + + if (info->soca->reset_count > 10) { + /* Reset finished; */ + info->soca->soc = info->soca->reset_soc[0]; + info->soca->stable_count = 0; + goto adjust; + } + + for (i = 0; i < 3; i++) { + if (max < info->soca->reset_soc[i]/100) + max = info->soca->reset_soc[i]/100; + if (min > info->soca->reset_soc[i]/100) + min = info->soca->reset_soc[i]/100; + } + + if ((info->soca->reset_count > 3) && ((max - min) + < RICOH619_MAX_RESET_SOC_DIFF)) { + /* Reset finished; */ + info->soca->soc = info->soca->reset_soc[0]; + info->soca->stable_count = 0; + goto adjust; + } else { + /* Do reset setting */ + ret = ricoh619_write(info->dev->parent, + FG_CTRL_REG, 0x51); + if (ret < 0) + dev_err(info->dev, "Error in writing the control register\n"); + + info->soca->ready_fg = 0; + + /* Delay for addition Reset Time (6s) */ + queue_delayed_work(info->monitor_wqueue, + &info->charge_stable_work, + RICOH619_FG_RESET_TIME*HZ); + } + /* Finished queue From now, select FG as result; */ + } else if (RICOH619_SOCA_START == info->soca->status) { + /* Normal condition */ + } else { /* other state ZERO/DISP/STABLE */ + info->soca->stable_count = 0; + } + + mutex_unlock(&info->lock); + return; + +adjust: + info->soca->last_soc = info->soca->soc; + info->soca->status = RICOH619_SOCA_DISP; + + } + mutex_unlock(&info->lock); + return; +} + +/* Initial setting of FuelGauge SOCA function */ +static int ricoh619_init_fgsoca(struct ricoh619_battery_info *info) +{ + int i; + int err; + uint8_t val; + uint8_t val2; + + for (i = 0; i <= 10; i = i+1) { + info->soca->ocv_table[i] = get_OCV_voltage(info, i); + RICOH_FG_DBG("PMU: %s : * %d0%% voltage = %d uV\n", + __func__, i, info->soca->ocv_table[i]); + } + + for (i = 0; i < 3; i = i+1) + info->soca->reset_soc[i] = 0; + info->soca->reset_count = 0; + + if (info->first_pwon) { + + err = ricoh619_read(info->dev->parent, CHGISET_REG, &val); + if (err < 0) + dev_err(info->dev, + "Error in read CHGISET_REG%d\n", err); + + err = ricoh619_write(info->dev->parent, CHGISET_REG, 0); + if (err < 0) + dev_err(info->dev, + "Error in writing CHGISET_REG%d\n", err); + msleep(1000); + + + err = ricoh619_write(info->dev->parent, + FG_CTRL_REG, 0x51); + if (err < 0) + dev_err(info->dev, "Error in writing the control register\n"); + + msleep(6000); + + err = ricoh619_write(info->dev->parent, CHGISET_REG, val); + if (err < 0) + dev_err(info->dev, + "Error in writing CHGISET_REG%d\n", err); + } + + /* Rbat : Transfer */ + info->soca->Rbat = get_OCV_init_Data(info, 12) * 1000 / 512 + * 5000 / 4095; + info->soca->n_cap = get_OCV_init_Data(info, 11); + + info->soca->displayed_soc = 0; + info->soca->ready_fg = 0; + info->soca->soc_delta = 0; + info->soca->status = RICOH619_SOCA_START; + /* stable count down 11->2, 1: reset; 0: Finished; */ + info->soca->stable_count = 11; + +#ifdef ENABLE_FG_KEEP_ON_MODE + g_fg_on_mode = 1; +#else + g_fg_on_mode = 0; +#endif + + /* Start first Display job */ + queue_delayed_work(info->monitor_wqueue, &info->displayed_work, + RICOH619_FG_RESET_TIME*HZ); + + /* Start first Waiting stable job */ + queue_delayed_work(info->monitor_wqueue, &info->charge_stable_work, + RICOH619_FG_STABLE_TIME*HZ/10); + + RICOH_FG_DBG("PMU: %s : * Rbat = %d mOhm n_cap = %d mAH\n", + __func__, info->soca->Rbat, info->soca->n_cap); + return 1; +} +#endif + +static void ricoh619_changed_work(struct work_struct *work) +{ + struct ricoh619_battery_info *info = container_of(work, + struct ricoh619_battery_info, changed_work.work); + + RICOH_FG_DBG("PMU: %s\n", __func__); + power_supply_changed(&info->battery); + + return; +} + +#ifdef ENABLE_FACTORY_MODE +/*------------------------------------------------------*/ +/* Factory Mode */ +/* Check Battery exist or not */ +/* If not, disabled Rapid to Complete State change */ +/*------------------------------------------------------*/ +static int ricoh619_factory_mode(struct ricoh619_battery_info *info) +{ + int ret = 0; + uint8_t val = 0; + + ret = ricoh619_read(info->dev->parent, RICOH619_INT_MON_CHGCTR, &val); + if (ret < 0) { + dev_err(info->dev, "Error in reading the control register\n"); + return ret; + } + if (!(val & 0x01)) /* No Adapter connected */ + return ret; + + /* Rapid to Complete State change disable */ + ret = ricoh619_write(info->dev->parent, RICOH619_CHG_CTL1, 0xe3); + if (ret < 0) { + dev_err(info->dev, "Error in writing the control register\n"); + return ret; + } + + /* Wait 1s for checking Charging State */ + queue_delayed_work(info->factory_mode_wqueue, &info->factory_mode_work, + 1*HZ); + + return ret; +} + +static void check_charging_state_work(struct work_struct *work) +{ + struct ricoh619_battery_info *info = container_of(work, + struct ricoh619_battery_info, factory_mode_work.work); + + int ret = 0; + uint8_t val = 0; + int chargeCurrent = 0; + + ret = ricoh619_read(info->dev->parent, CHGSTATE_REG, &val); + if (ret < 0) { + dev_err(info->dev, "Error in reading the control register\n"); + return; + } + + + chargeCurrent = get_check_fuel_gauge_reg(info, CC_AVERAGE1_REG, + CC_AVERAGE0_REG, 0x3fff); + if (chargeCurrent < 0) { + dev_err(info->dev, "Error in reading the FG register\n"); + return; + } + + /* Repid State && Charge Current about 0mA */ + if (((chargeCurrent > 0x3ffc && chargeCurrent < 0x3fff) + || chargeCurrent < 0x05) && val == 0x43) { + RICOH_FG_DBG("PMU:%s --- No battery !! Enter Factory mode ---\n" + , __func__); + info->entry_factory_mode = true; + return; /* Factory Mode */ + } + + /* Return Normal Mode --> Rapid to Complete State change enable */ + ret = ricoh619_write(info->dev->parent, RICOH619_CHG_CTL1, 0xa3); + if (ret < 0) { + dev_err(info->dev, "Error in writing the control register\n"); + return; + } + RICOH_FG_DBG("PMU:%s --- Battery exist !! Return Normal mode ---0x%2x\n" + , __func__, val); + + return; +} +#endif /* ENABLE_FACTORY_MODE */ + +static int Calc_Linear_Interpolation(int x0, int y0, int x1, int y1, int y) +{ + int alpha; + int x; + + alpha = (y - y0)*100 / (y1 - y0); + + x = ((100 - alpha) * x0 + alpha * x1) / 100; + + return x; +} + +static int ricoh619_set_OCV_table(struct ricoh619_battery_info *info) +{ + int ret = 0; + int ocv_table[11]; + int i, j; + int available_cap; + int temp; + int start_par; + int percent_step; + int OCV_percent_new[11]; + + if (CUTOFF_VOL == 0) { /* normal version */ + } else { /*Slice cutoff voltage version. */ + + /* get ocv table. this table is calculated by Apprication */ + for (i = 0; i <= 10; i = i+1) { + temp = (battery_init_para[i*2]<<8) + | (battery_init_para[i*2+1]); + /* conversion unit 1 Unit is 1.22mv (5000/4095 mv) */ + temp = ((temp * 50000 * 10 / 4095) + 5) / 10; + ocv_table[i] = temp; + } + + + /* Check Start % */ + for (i = 1; i < 11; i++) { + if (ocv_table[i] >= CUTOFF_VOL * 10) { + /* unit is 0.001% */ + start_par = Calc_Linear_Interpolation( + (i-1)*1000, ocv_table[i-1], i*1000, + ocv_table[i], (CUTOFF_VOL * 10)); + i = 11; + } + } + /* calc new ocv percent */ + percent_step = (10000 - start_par) / 10; + + for (i = 0; i < 11; i++) { + OCV_percent_new[i] + = start_par + percent_step*(i - 0); + } + + /* calc new ocv voltage */ + for (i = 0; i < 11; i++) { + for (j = 1; j < 11; j++) { + if (1000*j >= OCV_percent_new[i]) { + temp = Calc_Linear_Interpolation( + ocv_table[j-1], (j-1)*1000, + ocv_table[j] , j*1000, + OCV_percent_new[i]); + + temp = temp * 4095 / 50000; + + battery_init_para[i*2 + 1] = temp; + battery_init_para[i*2] = temp >> 8; + + j = 11; + } + } + } + + /* calc available capacity */ + /* get avilable capacity */ + /* battery_init_para23-24 is designe capacity */ + available_cap = (battery_init_para[22]<<8) + | (battery_init_para[23]); + + available_cap = available_cap + * ((10000 - start_par) / 100) / 100 ; + + + battery_init_para[23] = available_cap; + battery_init_para[22] = available_cap >> 8; + + } + ret = ricoh619_bulk_writes_bank1(info->dev->parent, + BAT_INIT_TOP_REG, 32, battery_init_para); + if (ret < 0) { + dev_err(info->dev, "batterry initialize error\n"); + return ret; + } + + return 1; +} + +/* Initial setting of battery */ +static int ricoh619_init_battery(struct ricoh619_battery_info *info) +{ + int ret = 0; + uint8_t val; + /* Need to implement initial setting of batery and error */ + /* -------------------------- */ +#ifdef ENABLE_FUEL_GAUGE_FUNCTION + + /* set kanwa state */ + if (RICOH619_REL1_SEL_VALUE > 240) + val = 0x0F; + else + val = RICOH619_REL1_SEL_VALUE / 16 ; + + val = 0x20 + val; + + ret = ricoh619_write_bank1(info->dev->parent, BAT_REL_SEL_REG, val); + if (ret < 0) { + dev_err(info->dev, "Error in writing the OCV Tabler\n"); + return ret; + } + + ret = ricoh619_read(info->dev->parent, FG_CTRL_REG, &val); + if (ret < 0) { + dev_err(info->dev, "Error in reading the control register\n"); + return ret; + } + + val = (val & 0x10) >> 4; + info->first_pwon = (val == 0) ? 1 : 0; + + ret = ricoh619_set_OCV_table(info); + if (ret < 0) { + dev_err(info->dev, "Error in writing the OCV Tabler\n"); + return ret; + } + + ret = ricoh619_write(info->dev->parent, FG_CTRL_REG, 0x11); + if (ret < 0) { + dev_err(info->dev, "Error in writing the control register\n"); + return ret; + } + +#endif + + if (info->alarm_vol_mv < 2700 || info->alarm_vol_mv > 3400) { + dev_err(info->dev, "alarm_vol_mv is out of range!\n"); + return -1; + } + + return ret; +} + +/* Initial setting of charger */ +static int ricoh619_init_charger(struct ricoh619_battery_info *info) +{ + int err; + uint8_t val; + uint8_t val2; + + info->chg_ctr = 0; + info->chg_stat1 = 0; + + /* In suspend - disable charging */ + err = ricoh619_clr_bits(info->dev->parent,CHGCTL1_REG, 0x03); + if (err < 0) { + dev_err(info->dev, "Error in writing the control register\n"); + goto free_device; + } + + if (RICOH619_MAX_ADP_CURRENT != 0) { + /* Change ADP Current to 2.5A. */ + err = ricoh619_write(info->dev->parent, 0xb6, + (RICOH619_MAX_ADP_CURRENT-1)/100); + if (err < 0) { + dev_err(info->dev, "Error in writing INT_MSK_CHGSTS1 %d\n", + err); + goto free_device; + } + } + + if (RICOH619_MAX_USB_CURRENT != 0) { + /* Set Max Change USB Current (0xB7) */ + err = ricoh619_write(info->dev->parent, REGISET2_REG, + (RICOH619_MAX_USB_CURRENT-1)/100); + if (err < 0) { + dev_err(info->dev, + "Error in writing RICOH619_MAX_USB_CURRENT %d\n", err); + goto free_device; + } + } + + /* Set Charge competion current (0xB8) */ + /* this value for bit 4-0 */ + if (RICOH619_MAX_CHARGE_CURRENT != 0) { + val = (RICOH619_MAX_CHARGE_CURRENT-1)/100; + } else { + err = ricoh619_read(info->dev->parent, CHGISET_REG, &val); + if (err < 0) { + dev_err(info->dev, + "Error in read RICOH619_MAX_CHARGE_CURRENT %d\n", err); + goto free_device; + } + val &= 0x3F; + } + + /* Set Charge competion current (0xB8) */ + /* this value for bit 7-6 */ + if(RICOH619_CHARGE_COMPLETION_CURRENT != 0) { + val2 = (RICOH619_CHARGE_COMPLETION_CURRENT - 50) / 50; + } else { + err = ricoh619_read(info->dev->parent, CHGISET_REG, &val2); + if (err < 0) { + dev_err(info->dev, + "Error in read RICOH619_MAX_CHARGE_CURRENT %d\n", err); + goto free_device; + } + val2 &= 0xC0; + } + val = val + (val2 << 6); + err = ricoh619_write(info->dev->parent, CHGISET_REG, val); + if (err < 0) { + dev_err(info->dev, + "Error in writing RICOH619_MAX_CHARGE_CURRENT %d\n", err); + goto free_device; + } + + /* Change Charger Voltege to 4.2V. Recharge Point to 4.1V */ + /* for define FULL charging voltage (bit 6~4)*/ + if (RICOH619_FULL_CHARGING_VOLTAGE != 0) { + if (RICOH619_FULL_CHARGING_VOLTAGE < 4050) + val2 = 0x00; + else if (RICOH619_FULL_CHARGING_VOLTAGE > 4200) + val2 = 0x04; + else + val2 = (RICOH619_FULL_CHARGING_VOLTAGE - 4050) / 50; + } else { + err = ricoh619_read(info->dev->parent, BATSET2_REG, &val2); + if (err < 0) { + dev_err(info->dev, + "Error in read RICOH619_FULL_CHARGE_VOLTAGE %d\n", err); + goto free_device; + } + val2 &= 0x70; + } + + /* for define re-charging voltage (bit 2~0)*/ + if (RICOH619_RE_CHARGING_VOLTAGE != 0) { + if (RICOH619_RE_CHARGING_VOLTAGE < 3850) + val = 0x00; + else if (RICOH619_RE_CHARGING_VOLTAGE > 4000) + val = 0x04; + else + val = (RICOH619_RE_CHARGING_VOLTAGE - 3850) / 50; + } else { + err = ricoh619_read(info->dev->parent, BATSET2_REG, &val); + if (err < 0) { + dev_err(info->dev, + "Error in read RICOH619_RE_CHARGE_VOLTAGE %d\n", err); + goto free_device; + } + val &= 0x07; + } + + val = val + (val2 << 4); + + err = ricoh619_write(info->dev->parent, BATSET2_REG, val); + if (err < 0) { + dev_err(info->dev, "Error in writing RICOH619_RE_CHARGE_VOLTAGE %d\n", + err); + goto free_device; + } + + /* out suspend - enable charging */ + err = ricoh619_set_bits(info->dev->parent,CHGCTL1_REG, 0x03); + if (err < 0) { + dev_err(info->dev, "Error in writing the control register\n"); + goto free_device; + } + + /* Set rising edge setting ([1:0]=01b)for INT in charging */ + /* and rising edge setting ([3:2]=01b)for charge completion */ + err = ricoh619_read(info->dev->parent, RICOH619_CHG_STAT_DETMOD1, &val); + if (err < 0) { + dev_err(info->dev, "Error in reading CHG_STAT_DETMOD1 %d\n", + err); + goto free_device; + } + val &= 0xf0; + val |= 0x05; + err = ricoh619_write(info->dev->parent, RICOH619_CHG_STAT_DETMOD1, val); + if (err < 0) { + dev_err(info->dev, "Error in writing CHG_STAT_DETMOD1 %d\n", + err); + goto free_device; + } + + /* Unmask In charging/charge completion */ + err = ricoh619_write(info->dev->parent, RICOH619_INT_MSK_CHGSTS1, 0xfc); + if (err < 0) { + dev_err(info->dev, "Error in writing INT_MSK_CHGSTS1 %d\n", + err); + goto free_device; + } + + /* Set both edge for VUSB([3:2]=11b)/VADP([1:0]=11b) detect */ + err = ricoh619_read(info->dev->parent, RICOH619_CHG_CTRL_DETMOD1, &val); + if (err < 0) { + dev_err(info->dev, "Error in reading CHG_CTRL_DETMOD1 %d\n", + err); + goto free_device; + } + val &= 0xf0; + val |= 0x0f; + err = ricoh619_write(info->dev->parent, RICOH619_CHG_CTRL_DETMOD1, val); + if (err < 0) { + dev_err(info->dev, "Error in writing CHG_CTRL_DETMOD1 %d\n", + err); + goto free_device; + } + + /* Unmask In VUSB/VADP completion */ + err = ricoh619_write(info->dev->parent, RICOH619_INT_MSK_CHGCTR, 0xfc); + if (err < 0) { + dev_err(info->dev, "Error in writing INT_MSK_CHGSTS1 %d\n", + err); + goto free_device; + } + +#ifdef ENABLE_LOW_BATTERY_DETECTION + /* Set ADRQ=00 to stop ADC */ + ricoh619_write(info->dev->parent, RICOH619_ADC_CNT3, 0x0); + /* Enable VSYS threshold Low interrupt */ + ricoh619_write(info->dev->parent, RICOH619_INT_EN_ADC1, 0x10); + /* Set ADC auto conversion interval 250ms */ + ricoh619_write(info->dev->parent, RICOH619_ADC_CNT2, 0x0); + /* Enable VSYS pin conversion in auto-ADC */ + ricoh619_write(info->dev->parent, RICOH619_ADC_CNT1, 0x10); + /* Set VSYS threshold low voltage = 3.50v */ + ricoh619_write(info->dev->parent, RICOH619_ADC_VSYS_THL, 0x77); + /* Start auto-mode & average 4-time conversion mode for ADC */ + ricoh619_write(info->dev->parent, RICOH619_ADC_CNT3, 0x28); + /* Enable master ADC INT */ + ricoh619_set_bits(info->dev->parent, RICOH619_INTC_INTEN, ADC_INT); +#endif + +free_device: + return err; +} + + +static int get_power_supply_status(struct ricoh619_battery_info *info) +{ + uint8_t status; + uint8_t supply_state; + uint8_t charge_state; + int ret = 0; + + /* get power supply status */ + ret = ricoh619_read(info->dev->parent, CHGSTATE_REG, &status); + if (ret < 0) { + dev_err(info->dev, "Error in reading the control register\n"); + return ret; + } + + charge_state = (status & 0x1F); + supply_state = ((status & 0xC0) >> 6); + + if (supply_state == SUPPLY_STATE_BAT) { + info->soca->chg_status = POWER_SUPPLY_STATUS_DISCHARGING; + } else { + switch (charge_state) { + case CHG_STATE_CHG_OFF: + info->soca->chg_status + = POWER_SUPPLY_STATUS_DISCHARGING; + break; + case CHG_STATE_CHG_READY_VADP: + info->soca->chg_status + = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case CHG_STATE_CHG_TRICKLE: + info->soca->chg_status + = POWER_SUPPLY_STATUS_CHARGING; + break; + case CHG_STATE_CHG_RAPID: + info->soca->chg_status + = POWER_SUPPLY_STATUS_CHARGING; + break; + case CHG_STATE_CHG_COMPLETE: + info->soca->chg_status + = POWER_SUPPLY_STATUS_FULL; + break; + case CHG_STATE_SUSPEND: + info->soca->chg_status + = POWER_SUPPLY_STATUS_DISCHARGING; + break; + case CHG_STATE_VCHG_OVER_VOL: + info->soca->chg_status + = POWER_SUPPLY_STATUS_DISCHARGING; + break; + case CHG_STATE_BAT_ERROR: + info->soca->chg_status + = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case CHG_STATE_NO_BAT: + info->soca->chg_status + = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case CHG_STATE_BAT_OVER_VOL: + info->soca->chg_status + = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case CHG_STATE_BAT_TEMP_ERR: + info->soca->chg_status + = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case CHG_STATE_DIE_ERR: + info->soca->chg_status + = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case CHG_STATE_DIE_SHUTDOWN: + info->soca->chg_status + = POWER_SUPPLY_STATUS_DISCHARGING; + break; + case CHG_STATE_NO_BAT2: + info->soca->chg_status + = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case CHG_STATE_CHG_READY_VUSB: + info->soca->chg_status + = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + default: + info->soca->chg_status + = POWER_SUPPLY_STATUS_UNKNOWN; + break; + } + } + + return info->soca->chg_status; +} + +static void charger_irq_work(struct work_struct *work) +{ + struct ricoh619_battery_info *info + = container_of(work, struct ricoh619_battery_info, irq_work); + int ret = 0; + RICOH_FG_DBG("PMU:%s In\n", __func__); + + power_supply_changed(&info->battery); + + mutex_lock(&info->lock); + info->chg_ctr = 0; + info->chg_stat1 = 0; + + /* Enable Interrupt for VADP */ + ret = ricoh619_clr_bits(info->dev->parent, + RICOH619_INT_MSK_CHGCTR, 0x01); + if (ret < 0) + dev_err(info->dev, + "%s(): Error in enable charger mask INT %d\n", + __func__, ret); + + /* Enable Interrupt for Charging & complete */ + ret = ricoh619_write(info->dev->parent, RICOH619_INT_MSK_CHGSTS1, 0xfc); + if (ret < 0) + dev_err(info->dev, + "%s(): Error in enable charger mask INT %d\n", + __func__, ret); + + mutex_unlock(&info->lock); + RICOH_FG_DBG("PMU:%s Out\n", __func__); +} + +#ifdef ENABLE_LOW_BATTERY_DETECTION +static void low_battery_irq_work(struct work_struct *work) +{ + struct ricoh619_battery_info *info = container_of(work, + struct ricoh619_battery_info, low_battery_work.work); + + int ret = 0; + + RICOH_FG_DBG("PMU:%s In\n", __func__); + + power_supply_changed(&info->battery); + + /* Enable VADP threshold Low interrupt */ + ricoh619_write(info->dev->parent, RICOH619_INT_EN_ADC1, 0x10); + if (ret < 0) + dev_err(info->dev, + "%s(): Error in enable adc mask INT %d\n", + __func__, ret); +} +#endif + +static void usb_det_irq_work(struct work_struct *work) +{ + struct ricoh619_battery_info *info = container_of(work, + struct ricoh619_battery_info, usb_irq_work); + int ret = 0; + uint8_t sts; + int time =0; + + RICOH_FG_DBG("PMU:%s In\n", __func__); + + power_supply_changed(&info->battery); + + mutex_lock(&info->lock); + + /* Enable Interrupt for VUSB */ + ret = ricoh619_clr_bits(info->dev->parent, + RICOH619_INT_MSK_CHGCTR, 0x02); + if (ret < 0) + dev_err(info->dev, + "%s(): Error in enable charger mask INT %d\n", + __func__, ret); + + mutex_unlock(&info->lock); + + ret = ricoh619_read(info->dev->parent, RICOH619_INT_MON_CHGCTR, &sts); + if (ret < 0) + dev_err(info->dev, "Error in reading the control register\n"); + + sts &= 0x02; + if (sts) { + + } else { + /*********************/ + /* No process ?? */ + /*********************/ + } + + RICOH_FG_DBG("PMU:%s Out\n", __func__); +} + +extern void rk28_send_wakeup_key(void); +static irqreturn_t charger_in_isr(int irq, void *battery_info) +{ + struct ricoh619_battery_info *info = battery_info; + printk("PMU:%s\n", __func__); + + info->chg_stat1 |= 0x01; + queue_work(info->workqueue, &info->irq_work); + rk28_send_wakeup_key(); + return IRQ_HANDLED; +} + +static irqreturn_t charger_complete_isr(int irq, void *battery_info) +{ + struct ricoh619_battery_info *info = battery_info; + printk("PMU:%s\n", __func__); + + info->chg_stat1 |= 0x02; + queue_work(info->workqueue, &info->irq_work); + + return IRQ_HANDLED; +} + +static irqreturn_t charger_usb_isr(int irq, void *battery_info) +{ + struct ricoh619_battery_info *info = battery_info; + printk("PMU:%s\n", __func__); + + info->chg_ctr |= 0x02; + queue_work(info->usb_workqueue, &info->usb_irq_work); + rk28_send_wakeup_key(); + + if (RICOH619_SOCA_UNSTABLE == info->soca->status + || RICOH619_SOCA_FG_RESET == info->soca->status) + info->soca->stable_count = 11; + + return IRQ_HANDLED; +} + +static irqreturn_t charger_adp_isr(int irq, void *battery_info) +{ + struct ricoh619_battery_info *info = battery_info; + struct ricoh619 *ricoh619 = g_ricoh619; + printk("PMU:%s\n", __func__); + + info->chg_ctr |= 0x01; + queue_work(info->workqueue, &info->irq_work); + rk28_send_wakeup_key(); + /* clr usb det irq */ + ricoh619_clr_bits(ricoh619->dev, RICOH619_INT_IR_CHGCTR, + info->chg_ctr); + + /* set adp limit current 2A */ + ricoh619_write(ricoh619->dev, 0xb6, 0x13); + /* set charge current 2A */ + ricoh619_write(ricoh619->dev, 0xb8, 0x13); + + if (RICOH619_SOCA_UNSTABLE == info->soca->status + || RICOH619_SOCA_FG_RESET == info->soca->status) + info->soca->stable_count = 11; + + return IRQ_HANDLED; +} + + +#ifdef ENABLE_LOW_BATTERY_DETECTION +/*************************************************************/ +/* for Detecting Low Battery */ +/*************************************************************/ + +static irqreturn_t adc_vsysl_isr(int irq, void *battery_info) +{ + + struct ricoh619_battery_info *info = battery_info; + +#if 0 + RICOH_FG_DBG("PMU:%s\n", __func__); + + queue_delayed_work(info->monitor_wqueue, &info->low_battery_work, + LOW_BATTERY_DETECTION_TIME*HZ); + +#endif + + RICOH_FG_DBG("PMU:%s\n", __func__); + ricoh619_write(info->dev->parent, RICOH619_INT_EN_ADC1, 0x10); + rk28_send_wakeup_key(); + + return IRQ_HANDLED; +} +#endif + + +#ifdef ENABLE_FUEL_GAUGE_FUNCTION +static int get_check_fuel_gauge_reg(struct ricoh619_battery_info *info, + int Reg_h, int Reg_l, int enable_bit) +{ + uint8_t get_data_h, get_data_l; + int old_data, current_data; + int i; + int ret = 0; + + old_data = 0; + + for (i = 0; i < 5 ; i++) { + ret = ricoh619_read(info->dev->parent, Reg_h, &get_data_h); + if (ret < 0) { + dev_err(info->dev, "Error in reading the control register\n"); + return ret; + } + + ret = ricoh619_read(info->dev->parent, Reg_l, &get_data_l); + if (ret < 0) { + dev_err(info->dev, "Error in reading the control register\n"); + return ret; + } + + current_data = ((get_data_h & 0xff) << 8) | (get_data_l & 0xff); + current_data = (current_data & enable_bit); + + if (current_data == old_data) + return current_data; + else + old_data = current_data; + } + + return current_data; +} + +static int calc_capacity(struct ricoh619_battery_info *info) +{ + uint8_t capacity; + int temp; + int ret = 0; + + /* get remaining battery capacity from fuel gauge */ + ret = ricoh619_read(info->dev->parent, SOC_REG, &capacity); + if (ret < 0) { + dev_err(info->dev, "Error in reading the control register\n"); + return ret; + } + + temp = capacity; + + return temp; /* Unit is 1% */ +} + +static int get_battery_temp(struct ricoh619_battery_info *info) +{ + int ret = 0; + int sign_bit; + + ret = get_check_fuel_gauge_reg(info, TEMP_1_REG, TEMP_2_REG, 0x0fff); + if (ret < 0) { + dev_err(info->dev, "Error in reading the fuel gauge control register\n"); + return ret; + } + + /* bit3 of 0xED(TEMP_1) is sign_bit */ + sign_bit = ((ret & 0x0800) >> 11); + + ret = (ret & 0x07ff); + + if (sign_bit == 0) /* positive value part */ + /* conversion unit */ + /* 1 unit is 0.0625 degree and retun unit + * should be 0.1 degree, + */ + ret = ret * 625 / 1000; + else /*negative value part */ + ret = -1 * ret * 625 / 1000; + + return ret; +} + +static int get_time_to_empty(struct ricoh619_battery_info *info) +{ + int ret = 0; + + ret = get_check_fuel_gauge_reg(info, TT_EMPTY_H_REG, TT_EMPTY_L_REG, + 0xffff); + if (ret < 0) { + dev_err(info->dev, "Error in reading the fuel gauge control register\n"); + return ret; + } + + /* conversion unit */ + /* 1unit is 1miniute and return nnit should be 1 second */ + ret = ret * 60; + + return ret; +} + +static int get_time_to_full(struct ricoh619_battery_info *info) +{ + int ret = 0; + + ret = get_check_fuel_gauge_reg(info, TT_FULL_H_REG, TT_FULL_L_REG, + 0xffff); + if (ret < 0) { + dev_err(info->dev, "Error in reading the fuel gauge control register\n"); + return ret; + } + + ret = ret * 60; + + return ret; +} + +/* battery voltage is get from Fuel gauge */ +static int measure_vbatt_FG(struct ricoh619_battery_info *info, int *data) +{ + int ret = 0; + + ret = get_check_fuel_gauge_reg(info, VOLTAGE_1_REG, VOLTAGE_2_REG, + 0x0fff); + if (ret < 0) { + dev_err(info->dev, "Error in reading the fuel gauge control register\n"); + return ret; + } + + *data = ret; + /* conversion unit 1 Unit is 1.22mv (5000/4095 mv) */ + *data = *data * 50000 / 4095; + /* return unit should be 1uV */ + *data = *data * 100; + + return ret; +} + +static int measure_Ibatt_FG(struct ricoh619_battery_info *info, int *data) +{ + int ret = 0; + + ret = get_check_fuel_gauge_reg(info, CC_AVERAGE1_REG, + CC_AVERAGE0_REG, 0x3fff); + if (ret < 0) { + dev_err(info->dev, "Error in reading the fuel gauge control register\n"); + return ret; + } + + *data = (ret > 0x1fff) ? (ret - 0x4000) : ret; + return ret; +} + +static int get_OCV_init_Data(struct ricoh619_battery_info *info, int index) +{ + int ret = 0; + ret = (battery_init_para[index*2]<<8) | (battery_init_para[index*2+1]); + return ret; +} + +static int get_OCV_voltage(struct ricoh619_battery_info *info, int index) +{ + int ret = 0; + ret = get_OCV_init_Data(info, index); + /* conversion unit 1 Unit is 1.22mv (5000/4095 mv) */ + ret = ret * 50000 / 4095; + /* return unit should be 1uV */ + ret = ret * 100; + return ret; +} + +#else +/* battery voltage is get from ADC */ +static int measure_vbatt_ADC(struct ricoh619_battery_info *info, int *data) +{ + int i; + uint8_t data_l = 0, data_h = 0; + int ret; + + /* ADC interrupt enable */ + ret = ricoh619_set_bits(info->dev->parent, INTEN_REG, 0x08); + if (ret < 0) { + dev_err(info->dev, "Error in setting the control register bit\n"); + goto err; + } + + /* enable interrupt request of single mode */ + ret = ricoh619_set_bits(info->dev->parent, EN_ADCIR3_REG, 0x01); + if (ret < 0) { + dev_err(info->dev, "Error in setting the control register bit\n"); + goto err; + } + + /* single request */ + ret = ricoh619_write(info->dev->parent, ADCCNT3_REG, 0x10); + if (ret < 0) { + dev_err(info->dev, "Error in writing the control register\n"); + goto err; + } + + for (i = 0; i < 5; i++) { + usleep(1000); + RICOH_FG_DBG("ADC conversion times: %d\n", i); + /* read completed flag of ADC */ + ret = ricoh619_read(info->dev->parent, EN_ADCIR3_REG, &data_h); + if (ret < 0) { + dev_err(info->dev, "Error in reading the control register\n"); + goto err; + } + + if (data_h & 0x01) + goto done; + } + + dev_err(info->dev, "ADC conversion too long!\n"); + goto err; + +done: + ret = ricoh619_read(info->dev->parent, VBATDATAH_REG, &data_h); + if (ret < 0) { + dev_err(info->dev, "Error in reading the control register\n"); + goto err; + } + + ret = ricoh619_read(info->dev->parent, VBATDATAL_REG, &data_l); + if (ret < 0) { + dev_err(info->dev, "Error in reading the control register\n"); + goto err; + } + + *data = ((data_h & 0xff) << 4) | (data_l & 0x0f); + /* conversion unit 1 Unit is 1.22mv (5000/4095 mv) */ + *data = *data * 5000 / 4095; + /* return unit should be 1uV */ + *data = *data * 1000; + + return 0; + +err: + return -1; +} +#endif + +/* +static void ricoh619_external_power_changed(struct power_supply *psy) +{ + struct ricoh619_battery_info *info; + + info = container_of(psy, struct ricoh619_battery_info, battery); + queue_delayed_work(info->monitor_wqueue, + &info->changed_work, HZ / 2); + return; +} +*/ + +static int ricoh619_batt_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct ricoh619_battery_info *info = dev_get_drvdata(psy->dev->parent); + int data = 0; + int ret = 0; + uint8_t status; + + mutex_lock(&info->lock); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + ret = ricoh619_read(info->dev->parent, CHGSTATE_REG, &status); + if (ret < 0) { + dev_err(info->dev, "Error in reading the control register\n"); + mutex_unlock(&info->lock); + return ret; + } + if (psy->type == POWER_SUPPLY_TYPE_MAINS) + val->intval = (status & 0x40 ? 1 : 0); + else if (psy->type == POWER_SUPPLY_TYPE_USB) + val->intval = (status & 0x80 ? 1 : 0); + break; + /* this setting is same as battery driver of 584 */ + case POWER_SUPPLY_PROP_STATUS: + ret = get_power_supply_status(info); + val->intval = ret; + info->status = ret; + /* RICOH_FG_DBG("Power Supply Status is %d\n", + info->status); */ + break; + + /* this setting is same as battery driver of 584 */ + case POWER_SUPPLY_PROP_PRESENT: + val->intval = info->present; + break; + + /* current voltage is get from fuel gauge */ + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + /* return real vbatt Voltage */ +#ifdef ENABLE_FUEL_GAUGE_FUNCTION + if (info->soca->ready_fg) + ret = measure_vbatt_FG(info, &data); + else { + val->intval = -EINVAL; + RICOH_FG_DBG( "battery voltage is not ready\n"); + break; + } +#else + ret = measure_vbatt_ADC(info, &data); +#endif + val->intval = data; + /* convert unit uV -> mV */ + info->cur_voltage = data / 1000; + RICOH_FG_DBG( "battery voltage is %d mV\n", + info->cur_voltage); + break; + +#ifdef ENABLE_FUEL_GAUGE_FUNCTION + /* current battery capacity is get from fuel gauge */ + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = (info->soca->displayed_soc + 50)/100; + info->capacity = (info->soca->displayed_soc + 50)/100; + /* RICOH_FG_DBG("battery capacity is %d%%\n", + info->capacity); */ + break; + + /* current temperature of battery */ + case POWER_SUPPLY_PROP_TEMP: + if (info->soca->ready_fg) { + ret = get_battery_temp(info); + val->intval = ret; + info->battery_temp = ret/10; + RICOH_FG_DBG( "battery temperature is %d degree\n", info->battery_temp); + } else { + val->intval = -EINVAL; + RICOH_FG_DBG("battery temperature is not ready\n"); + } + break; + + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: + if (info->soca->ready_fg) { + ret = get_time_to_empty(info); + val->intval = ret; + info->time_to_empty = ret/60; + RICOH_FG_DBG("time of empty battery is %d minutes\n", info->time_to_empty); + } else { + val->intval = -EINVAL; + RICOH_FG_DBG( "time of empty battery is not ready\n"); + } + break; + + case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: + if (info->soca->ready_fg) { + ret = get_time_to_full(info); + val->intval = ret; + info->time_to_full = ret/60; + RICOH_FG_DBG( "time of full battery is %d minutes\n", info->time_to_full); + } else { + val->intval = -EINVAL; + RICOH_FG_DBG("time of full battery is not ready\n"); + } + break; +#endif + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + ret = 0; + break; + + case POWER_SUPPLY_PROP_HEALTH: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + ret = 0; + break; + + default: + mutex_unlock(&info->lock); + return -ENODEV; + } + + mutex_unlock(&info->lock); + + return ret; +} + +static enum power_supply_property ricoh619_batt_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + +#ifdef ENABLE_FUEL_GAUGE_FUNCTION + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, + POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, +#endif + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_HEALTH, +}; + +static enum power_supply_property ricoh619_power_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +struct power_supply powerac = { + .name = "acpwr", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = ricoh619_power_props, + .num_properties = ARRAY_SIZE(ricoh619_power_props), + .get_property = ricoh619_batt_get_prop, +}; + +struct power_supply powerusb = { + .name = "usbpwr", + .type = POWER_SUPPLY_TYPE_USB, + .properties = ricoh619_power_props, + .num_properties = ARRAY_SIZE(ricoh619_power_props), + .get_property = ricoh619_batt_get_prop, +}; + +static __devinit int ricoh619_battery_probe(struct platform_device *pdev) +{ + struct ricoh619_battery_info *info; + struct ricoh619_battery_platform_data *pdata; + int ret; + + RICOH_FG_DBG("PMU: %s\n", __func__); + + info = kzalloc(sizeof(struct ricoh619_battery_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + info->soca = kzalloc(sizeof(struct ricoh619_soca_info), GFP_KERNEL); + if (!info->soca) + return -ENOMEM; + + info->dev = &pdev->dev; + info->status = POWER_SUPPLY_STATUS_CHARGING; + pdata = pdev->dev.platform_data; + info->monitor_time = pdata->monitor_time * HZ; + info->alarm_vol_mv = pdata->alarm_vol_mv; + info->adc_vdd_mv = ADC_VDD_MV; /* 2800; */ + info->min_voltage = MIN_VOLTAGE; /* 3100; */ + info->max_voltage = MAX_VOLTAGE; /* 4200; */ + info->delay = 500; + info->entry_factory_mode = false; + + mutex_init(&info->lock); + platform_set_drvdata(pdev, info); + + info->battery.name = "battery"; + info->battery.type = POWER_SUPPLY_TYPE_BATTERY; + info->battery.properties = ricoh619_batt_props; + info->battery.num_properties = ARRAY_SIZE(ricoh619_batt_props); + info->battery.get_property = ricoh619_batt_get_prop; + info->battery.set_property = NULL; +/* info->battery.external_power_changed + = ricoh619_external_power_changed; */ + + /* Disable Charger/ADC interrupt */ + ret = ricoh619_clr_bits(info->dev->parent, RICOH619_INTC_INTEN, + CHG_INT | ADC_INT); + if (ret) + goto out; + + ret = ricoh619_init_battery(info); + if (ret) + goto out; + +#ifdef ENABLE_FACTORY_MODE + info->factory_mode_wqueue + = create_singlethread_workqueue("ricoh619_factory_mode"); + INIT_DELAYED_WORK_DEFERRABLE(&info->factory_mode_work, + check_charging_state_work); + + ret = ricoh619_factory_mode(info); + if (ret) + goto out; + +#endif + + ret = power_supply_register(&pdev->dev, &info->battery); + + if (ret) + info->battery.dev->parent = &pdev->dev; + + ret = power_supply_register(&pdev->dev, &powerac); + ret = power_supply_register(&pdev->dev, &powerusb); + + info->monitor_wqueue + = create_singlethread_workqueue("ricoh619_battery_monitor"); + INIT_DELAYED_WORK_DEFERRABLE(&info->monitor_work, + ricoh619_battery_work); + INIT_DELAYED_WORK_DEFERRABLE(&info->displayed_work, + ricoh619_displayed_work); + INIT_DELAYED_WORK_DEFERRABLE(&info->charge_stable_work, + ricoh619_stable_charge_countdown_work); + INIT_DELAYED_WORK(&info->changed_work, ricoh619_changed_work); + queue_delayed_work(info->monitor_wqueue, &info->monitor_work, + RICOH619_MONITOR_START_TIME*HZ); + + + /* Charger IRQ workqueue settings */ + charger_irq = pdata->irq; + + info->workqueue = create_singlethread_workqueue("rc5t619_charger_in"); + INIT_WORK(&info->irq_work, charger_irq_work); + + ret = request_threaded_irq(charger_irq + RICOH619_IRQ_FONCHGINT, + NULL, charger_in_isr, IRQF_ONESHOT, + "rc5t619_charger_in", info); + if (ret < 0) { + dev_err(&pdev->dev, "Can't get CHG_INT IRQ for chrager: %d\n", + ret); + goto out; + } + info->workqueue = create_singlethread_workqueue("rc5t619_charger_in"); + INIT_WORK(&info->irq_work, charger_irq_work); + + ret = request_threaded_irq(charger_irq + RICOH619_IRQ_FCHGCMPINT, + NULL, charger_complete_isr, + IRQF_ONESHOT, "rc5t619_charger_comp", + info); + if (ret < 0) { + dev_err(&pdev->dev, "Can't get CHG_COMP IRQ for chrager: %d\n", + ret); + goto out; + } + + ret = request_threaded_irq(charger_irq + RICOH619_IRQ_FVUSBDETSINT, + NULL, charger_usb_isr, IRQF_ONESHOT, + "rc5t619_usb_det", info); + if (ret < 0) { + dev_err(&pdev->dev, "Can't get USB_DET IRQ for chrager: %d\n", + ret); + goto out; + } + + ret = request_threaded_irq(charger_irq + RICOH619_IRQ_FVADPDETSINT, + NULL, charger_adp_isr, IRQF_ONESHOT, + "rc5t619_adp_det", info); + if (ret < 0) { + dev_err(&pdev->dev, + "Can't get ADP_DET IRQ for chrager: %d\n", ret); + goto out; + } + + info->usb_workqueue + = create_singlethread_workqueue("rc5t619_usb_det"); + INIT_WORK(&info->usb_irq_work, usb_det_irq_work); + + +#ifdef ENABLE_LOW_BATTERY_DETECTION + ret = request_threaded_irq(charger_irq + RICOH619_IRQ_VSYSLIR, + NULL, adc_vsysl_isr, IRQF_ONESHOT, + "rc5t619_adc_vsysl", info); + if (ret < 0) { + dev_err(&pdev->dev, + "Can't get ADC_VSYSL IRQ for chrager: %d\n", ret); + goto out; + } + INIT_DELAYED_WORK_DEFERRABLE(&info->low_battery_work, + low_battery_irq_work); +#endif + + /* Charger init and IRQ setting */ + ret = ricoh619_init_charger(info); + if (ret) + goto out; + +#ifdef ENABLE_FUEL_GAUGE_FUNCTION + ret = ricoh619_init_fgsoca(info); +#endif + + /* Enable Charger interrupt */ + ricoh619_set_bits(info->dev->parent, RICOH619_INTC_INTEN, CHG_INT); + + return 0; + +out: + kfree(info); + return ret; +} + +static int __devexit ricoh619_battery_remove(struct platform_device *pdev) +{ + struct ricoh619_battery_info *info = platform_get_drvdata(pdev); + uint8_t val; + int ret; + int err; + int cc_cap = 0; + bool is_charging = true; + + if (g_fg_on_mode + && (info->soca->status == RICOH619_SOCA_STABLE)) { + err = ricoh619_write(info->dev->parent, PSWR_REG, 0x7f); + if (err < 0) + dev_err(info->dev, "Error in writing PSWR_REG\n"); + g_soc = 0x7f; + } else { + val = (info->soca->displayed_soc + 50)/100; + val &= 0x7f; + ret = ricoh619_write(info->dev->parent, PSWR_REG, val); + if (ret < 0) + dev_err(info->dev, "Error in writing PSWR_REG\n"); + + g_soc = (info->soca->displayed_soc + 50)/100; + + ret = calc_capacity_in_period(info, &cc_cap, &is_charging); + if (ret < 0) + dev_err(info->dev, "Read cc_sum Error !!-----\n"); + } + + if (g_fg_on_mode == 0) { + ret = ricoh619_clr_bits(info->dev->parent, + FG_CTRL_REG, 0x01); + if (ret < 0) + dev_err(info->dev, "Error in clr FG EN\n"); + } + + cancel_delayed_work(&info->monitor_work); + cancel_delayed_work(&info->charge_stable_work); + cancel_delayed_work(&info->changed_work); +#ifdef ENABLE_LOW_BATTERY_DETECTION + cancel_delayed_work(&info->low_battery_work); +#endif + + flush_work(&info->irq_work); + flush_work(&info->usb_irq_work); + + flush_workqueue(info->monitor_wqueue); + flush_workqueue(info->workqueue); + flush_workqueue(info->usb_workqueue); + + destroy_workqueue(info->monitor_wqueue); + destroy_workqueue(info->workqueue); + destroy_workqueue(info->usb_workqueue); + + power_supply_unregister(&info->battery); + kfree(info); + platform_set_drvdata(pdev, NULL); + return 0; +} + +#ifdef CONFIG_PM +static int ricoh619_battery_suspend(struct device *dev) +{ + struct ricoh619_battery_info *info = dev_get_drvdata(dev); + uint8_t val; + int ret; + int err; + int cc_cap = 0; + bool is_charging = true; + + if (g_fg_on_mode + && (info->soca->status == RICOH619_SOCA_STABLE)) { + err = ricoh619_write(info->dev->parent, PSWR_REG, 0x7f); + if (err < 0) + dev_err(info->dev, "Error in writing PSWR_REG\n"); + g_soc = 0x7F; + } else { + val = (info->soca->displayed_soc + 50)/100; + val &= 0x7f; + ret = ricoh619_write(info->dev->parent, PSWR_REG, val); + if (ret < 0) + dev_err(info->dev, "Error in writing PSWR_REG\n"); + + g_soc = (info->soca->displayed_soc + 50)/100; + + ret = calc_capacity_in_period(info, &cc_cap, &is_charging); + if (ret < 0) + dev_err(info->dev, "Read cc_sum Error !!-----\n"); + } + + disable_irq(charger_irq + RICOH619_IRQ_FONCHGINT); + disable_irq(charger_irq + RICOH619_IRQ_FCHGCMPINT); + disable_irq(charger_irq + RICOH619_IRQ_FVUSBDETSINT); + disable_irq(charger_irq + RICOH619_IRQ_FVADPDETSINT); +#ifdef ENABLE_LOW_BATTERY_DETECTION + disable_irq(charger_irq + RICOH619_IRQ_VSYSLIR); +#endif + + cancel_delayed_work_sync(&info->monitor_work); + cancel_delayed_work_sync(&info->displayed_work); + cancel_delayed_work_sync(&info->charge_stable_work); + cancel_delayed_work_sync(&info->changed_work); +#ifdef ENABLE_LOW_BATTERY_DETECTION + cancel_delayed_work_sync(&info->low_battery_work); +#endif + cancel_work_sync(&info->irq_work); + cancel_work_sync(&info->usb_irq_work); + + flush_workqueue(info->monitor_wqueue); + flush_workqueue(info->workqueue); + flush_workqueue(info->usb_workqueue); + + return 0; +} + +static int ricoh619_battery_resume(struct device *dev) +{ + struct ricoh619_battery_info *info = dev_get_drvdata(dev); + uint8_t val; + int ret; + int displayed_soc_temp; + int cc_cap = 0; + bool is_charging = true; + int i; + + if (info->entry_factory_mode) { + info->soca->displayed_soc = -EINVAL; + } else if (RICOH619_SOCA_STABLE == info->soca->status) { + info->soca->soc = calc_capacity(info) * 100; + info->soca->displayed_soc = info->soca->soc; + } else if (RICOH619_SOCA_ZERO == info->soca->status) { + if (calc_ocv(info) > get_OCV_voltage(info, 0)) { + ret = ricoh619_read(info->dev->parent, PSWR_REG, &val); + val &= 0x7f; + info->soca->soc = val * 100; + if ((ret < 0) || (val == 0)) { + dev_err(info->dev, + "Error in reading PSWR_REG %d\n", ret); + info->soca->soc + = calc_capacity(info) * 100 + 50; + } + + ret = calc_capacity_in_period(info, &cc_cap, + &is_charging); + if (ret < 0) + dev_err(info->dev, "Read cc_sum Error !!-----\n"); + + info->soca->cc_delta + = (is_charging == true) ? cc_cap : -cc_cap; + + displayed_soc_temp + = info->soca->soc + info->soca->cc_delta; + displayed_soc_temp = min(10000, displayed_soc_temp); + displayed_soc_temp = max(0, displayed_soc_temp); + info->soca->displayed_soc = displayed_soc_temp; + + ret = ricoh619_write(info->dev->parent, + FG_CTRL_REG, 0x51); + if (ret < 0) + dev_err(info->dev, "Error in writing the control register\n"); + info->soca->ready_fg = 0; + info->soca->status = RICOH619_SOCA_FG_RESET; + + } else + info->soca->displayed_soc = 0; + } else { + ret = ricoh619_read(info->dev->parent, PSWR_REG, &val); + val &= 0x7f; + info->soca->soc = val * 100; + if ((ret < 0) || (val == 0)) { + dev_err(info->dev, + "Error in reading PSWR_REG %d\n", ret); + info->soca->soc + = calc_capacity(info) * 100 + 50; + } + + ret = calc_capacity_in_period(info, &cc_cap, &is_charging); + if (ret < 0) + dev_err(info->dev, "Read cc_sum Error !!-----\n"); + + info->soca->cc_delta = (is_charging == true) ? cc_cap : -cc_cap; + + displayed_soc_temp = info->soca->soc + info->soca->cc_delta; + displayed_soc_temp = min(10000, displayed_soc_temp); + displayed_soc_temp = max(0, displayed_soc_temp); + info->soca->displayed_soc = displayed_soc_temp; + if (RICOH619_SOCA_DISP == info->soca->status) + info->soca->last_soc = calc_capacity(info) * 100; + } + + power_supply_changed(&info->battery); + queue_delayed_work(info->monitor_wqueue, &info->displayed_work, HZ); + + if (RICOH619_SOCA_UNSTABLE == info->soca->status) { + info->soca->stable_count = 10; + queue_delayed_work(info->monitor_wqueue, + &info->charge_stable_work, + RICOH619_FG_STABLE_TIME*HZ/10); + } else if (RICOH619_SOCA_FG_RESET == info->soca->status) { + info->soca->stable_count = 1; + + for (i = 0; i < 3; i = i+1) + info->soca->reset_soc[i] = 0; + info->soca->reset_count = 0; + + queue_delayed_work(info->monitor_wqueue, + &info->charge_stable_work, + RICOH619_FG_RESET_TIME*HZ); + } + + queue_delayed_work(info->monitor_wqueue, &info->monitor_work, + info->monitor_time); + + enable_irq(charger_irq + RICOH619_IRQ_FONCHGINT); + enable_irq(charger_irq + RICOH619_IRQ_FCHGCMPINT); + enable_irq(charger_irq + RICOH619_IRQ_FVUSBDETSINT); + enable_irq(charger_irq + RICOH619_IRQ_FVADPDETSINT); +#ifdef ENABLE_LOW_BATTERY_DETECTION + enable_irq(charger_irq + RICOH619_IRQ_VSYSLIR); +#endif + return 0; +} + +static const struct dev_pm_ops ricoh619_battery_pm_ops = { + .suspend = ricoh619_battery_suspend, + .resume = ricoh619_battery_resume, +}; +#endif + +static struct platform_driver ricoh619_battery_driver = { + .driver = { + .name = "ricoh619-battery", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &ricoh619_battery_pm_ops, +#endif + }, + .probe = ricoh619_battery_probe, + .remove = __devexit_p(ricoh619_battery_remove), +}; + +static int __init ricoh619_battery_init(void) +{ + RICOH_FG_DBG("PMU: %s\n", __func__); + return platform_driver_register(&ricoh619_battery_driver); +} +subsys_initcall_sync(ricoh619_battery_init); + +static void __exit ricoh619_battery_exit(void) +{ + platform_driver_unregister(&ricoh619_battery_driver); +} +module_exit(ricoh619_battery_exit); + +MODULE_DESCRIPTION("RICOH619 Battery driver"); +MODULE_ALIAS("platform:ricoh619-battery"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig old mode 100755 new mode 100644 index e01f5afcb377..b8903982e33a --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -171,6 +171,13 @@ config REGULATOR_WM8994 This driver provides support for the voltage regulators on the WM8994 CODEC. +config REGULATOR_RICOH619 + tristate "RICOH 619 Power regulators" + depends on MFD_RICOH619 + default n + help + This driver supports regulator driver for RICOH619 PMIC. + config REGULATOR_DA903X tristate "Support regulators on Dialog Semiconductor DA9030/DA9034 PMIC" depends on PMIC_DA903X diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile old mode 100755 new mode 100644 index 21b93d3d51f1..13f27a9f1a71 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -53,5 +53,6 @@ obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o obj-$(CONFIG_REGULATOR_ACT8891) += act8891.o obj-$(CONFIG_REGULATOR_ACT8931) += act8931.o obj-$(CONFIG_REGULATOR_ACT8846) += act8846.o +obj-$(CONFIG_REGULATOR_RICOH619) += ricoh619-regulator.o ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG diff --git a/drivers/regulator/ricoh619-regulator.c b/drivers/regulator/ricoh619-regulator.c new file mode 100644 index 000000000000..3c5d433127b1 --- /dev/null +++ b/drivers/regulator/ricoh619-regulator.c @@ -0,0 +1,578 @@ +/* + * drivers/regulator/ricoh619-regulator.c + * + * Regulator driver for RICOH619 power management chip. + * + * Copyright (C) 2012-2013 RICOH COMPANY,LTD + * + * Based on code + * Copyright (C) 2011 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/*#define DEBUG 1*/ +/*#define VERBOSE_DEBUG 1*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ricoh619_regulator { + int id; + int sleep_id; + /* Regulator register address.*/ + u8 reg_en_reg; + u8 en_bit; + u8 reg_disc_reg; + u8 disc_bit; + u8 vout_reg; + u8 vout_mask; + u8 vout_reg_cache; + u8 sleep_reg; + u8 eco_reg; + u8 eco_bit; + u8 eco_slp_reg; + u8 eco_slp_bit; + + /* chip constraints on regulator behavior */ + int min_uV; + int max_uV; + int step_uV; + int nsteps; + + /* regulator specific turn-on delay */ + u16 delay; + + /* used by regulator core */ + struct regulator_desc desc; + + /* Device */ + struct device *dev; +}; + +//static unsigned int ricoh619_suspend_status = 0; + +static inline struct device *to_ricoh619_dev(struct regulator_dev *rdev) +{ + return rdev_get_dev(rdev)->parent->parent; +} + +static int ricoh619_regulator_enable_time(struct regulator_dev *rdev) +{ + struct ricoh619_regulator *ri = rdev_get_drvdata(rdev); + + return ri->delay; +} + +static int ricoh619_reg_is_enabled(struct regulator_dev *rdev) +{ + struct ricoh619_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_ricoh619_dev(rdev); + uint8_t control; + int ret; + + ret = ricoh619_read(parent, ri->reg_en_reg, &control); + if (ret < 0) { + dev_err(&rdev->dev, "Error in reading the control register\n"); + return ret; + } + return (((control >> ri->en_bit) & 1) == 1); +} + +static int ricoh619_reg_enable(struct regulator_dev *rdev) +{ + struct ricoh619_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_ricoh619_dev(rdev); + int ret; + ret = ricoh619_set_bits(parent, ri->reg_en_reg, (1 << ri->en_bit)); + if (ret < 0) { + dev_err(&rdev->dev, "Error in updating the STATE register\n"); + return ret; + } + udelay(ri->delay); + return ret; +} + +static int ricoh619_reg_disable(struct regulator_dev *rdev) +{ + struct ricoh619_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_ricoh619_dev(rdev); + int ret; + ret = ricoh619_clr_bits(parent, ri->reg_en_reg, (1 << ri->en_bit)); + if (ret < 0) + dev_err(&rdev->dev, "Error in updating the STATE register\n"); + + return ret; +} + +static int ricoh619_list_voltage(struct regulator_dev *rdev, unsigned index) +{ + struct ricoh619_regulator *ri = rdev_get_drvdata(rdev); + + return ri->min_uV + (ri->step_uV * index); +} + +static int __ricoh619_set_s_voltage(struct device *parent, + struct ricoh619_regulator *ri, int min_uV, int max_uV) +{ + int vsel; + int ret; + + if ((min_uV < ri->min_uV) || (max_uV > ri->max_uV)) + return -EDOM; + + vsel = (min_uV - ri->min_uV + ri->step_uV - 1)/ri->step_uV; + if (vsel > ri->nsteps) + return -EDOM; + + ret = ricoh619_update(parent, ri->sleep_reg, vsel, ri->vout_mask); + if (ret < 0) + dev_err(ri->dev, "Error in writing the sleep register\n"); + return ret; +} + +static int __ricoh619_set_voltage(struct device *parent, + struct ricoh619_regulator *ri, int min_uV, int max_uV, + unsigned *selector) +{ + int vsel; + int ret; + uint8_t vout_val; + + if ((min_uV < ri->min_uV) || (max_uV > ri->max_uV)) + return -EDOM; + + vsel = (min_uV - ri->min_uV + ri->step_uV - 1)/ri->step_uV; + if (vsel > ri->nsteps) + return -EDOM; + + if (selector) + *selector = vsel; + + vout_val = (ri->vout_reg_cache & ~ri->vout_mask) | + (vsel & ri->vout_mask); + ret = ricoh619_write(parent, ri->vout_reg, vout_val); + if (ret < 0) + dev_err(ri->dev, "Error in writing the Voltage register\n"); + else + ri->vout_reg_cache = vout_val; + + return ret; +} + +static int ricoh619_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned *selector) +{ + struct ricoh619_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_ricoh619_dev(rdev); + +// if(ricoh619_suspend_status) +// return -EBUSY; + + return __ricoh619_set_voltage(parent, ri, min_uV, max_uV, selector); +} + +static int ricoh619_set_suspend_voltage(struct regulator_dev *rdev, + int uV) +{ + struct ricoh619_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_ricoh619_dev(rdev); + + return __ricoh619_set_s_voltage(parent, ri, uV, uV); +} + +static int ricoh619_get_voltage(struct regulator_dev *rdev) +{ + struct ricoh619_regulator *ri = rdev_get_drvdata(rdev); + uint8_t vsel; + + vsel = ri->vout_reg_cache & ri->vout_mask; + return ri->min_uV + vsel * ri->step_uV; +} + + int ricoh619_regulator_enable_eco_mode(struct regulator_dev *rdev) +{ + struct ricoh619_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_ricoh619_dev(rdev); + int ret; + + ret = ricoh619_set_bits(parent, ri->eco_reg, (1 << ri->eco_bit)); + if (ret < 0) + dev_err(&rdev->dev, "Error Enable LDO eco mode\n"); + + return ret; +} +EXPORT_SYMBOL_GPL(ricoh619_regulator_enable_eco_mode); + +int ricoh619_regulator_disable_eco_mode(struct regulator_dev *rdev) +{ + struct ricoh619_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_ricoh619_dev(rdev); + int ret; + + ret = ricoh619_clr_bits(parent, ri->eco_reg, (1 << ri->eco_bit)); + if (ret < 0) + dev_err(&rdev->dev, "Error Disable LDO eco mode\n"); + + return ret; +} +EXPORT_SYMBOL_GPL(ricoh619_regulator_disable_eco_mode); + +int ricoh619_regulator_enable_eco_slp_mode(struct regulator_dev *rdev) +{ + struct ricoh619_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_ricoh619_dev(rdev); + int ret; + + ret = ricoh619_set_bits(parent, ri->eco_slp_reg, (1 << ri->eco_slp_bit)); + if (ret < 0) + dev_err(&rdev->dev, "Error Enable LDO eco mode in d during sleep\n"); + + return ret; +} +EXPORT_SYMBOL_GPL(ricoh619_regulator_enable_eco_slp_mode); + +int ricoh619_regulator_disable_eco_slp_mode(struct regulator_dev *rdev) +{ + struct ricoh619_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_ricoh619_dev(rdev); + int ret; + + ret = ricoh619_clr_bits(parent, ri->eco_slp_reg, (1 << ri->eco_slp_bit)); + if (ret < 0) + dev_err(&rdev->dev, "Error Enable LDO eco mode in d during sleep\n"); + + return ret; +} +EXPORT_SYMBOL_GPL(ricoh619_regulator_disable_eco_slp_mode); + +static unsigned int ricoh619_dcdc_get_mode(struct regulator_dev *rdev) +{ + struct ricoh619_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_ricoh619_dev(rdev); + int ret; + uint8_t control; + u8 mask = 0x30; + + ret = ricoh619_read(parent, ri->reg_en_reg,&control); + if (ret < 0) { + return ret; + } + control=(control & mask) >> 4; + switch (control) { + case 1: + return REGULATOR_MODE_FAST; + case 0: + return REGULATOR_MODE_NORMAL; + case 2: + return REGULATOR_MODE_STANDBY; + case 4: + return REGULATOR_MODE_NORMAL; + default: + return -1; + } + +} +static int ricoh619_dcdc_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct ricoh619_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_ricoh619_dev(rdev); + int ret; + uint8_t control; + + ret = ricoh619_read(parent, ri->reg_en_reg,&control); + switch(mode) + { + case REGULATOR_MODE_FAST: + return ricoh619_write(parent, ri->reg_en_reg, ((control & 0xef) | 0x10)); + case REGULATOR_MODE_NORMAL: + return ricoh619_write(parent, ri->reg_en_reg, (control & 0xcf)); + case REGULATOR_MODE_STANDBY: + return ricoh619_write(parent, ri->reg_en_reg, ((control & 0xdf) | 0x20)); + default: + printk("error:pmu_619 only powersave pwm psm mode\n"); + return -EINVAL; + } + + +} + +static int ricoh619_dcdc_set_voltage_time_sel(struct regulator_dev *rdev, unsigned int old_selector, + unsigned int new_selector) +{ + int old_volt, new_volt; + + old_volt = ricoh619_list_voltage(rdev, old_selector); + if (old_volt < 0) + return old_volt; + + new_volt = ricoh619_list_voltage(rdev, new_selector); + if (new_volt < 0) + return new_volt; + + return DIV_ROUND_UP(abs(old_volt - new_volt)*2, 14000); +} + + +static struct regulator_ops ricoh619_ops = { + .list_voltage = ricoh619_list_voltage, + .set_voltage = ricoh619_set_voltage, + .get_voltage = ricoh619_get_voltage, + .set_suspend_voltage = ricoh619_set_suspend_voltage, + .set_voltage_time_sel = ricoh619_dcdc_set_voltage_time_sel, + .get_mode = ricoh619_dcdc_get_mode, + .set_mode = ricoh619_dcdc_set_mode, + .enable = ricoh619_reg_enable, + .disable = ricoh619_reg_disable, + .is_enabled = ricoh619_reg_is_enabled, +}; + +#define RICOH619_REG(_id, _en_reg, _en_bit, _disc_reg, _disc_bit, _vout_reg, \ + _vout_mask, _ds_reg, _min_uv, _max_uv, _step_uV, _nsteps, \ + _ops, _delay, _eco_reg, _eco_bit, _eco_slp_reg, _eco_slp_bit) \ +{ \ + .reg_en_reg = _en_reg, \ + .en_bit = _en_bit, \ + .reg_disc_reg = _disc_reg, \ + .disc_bit = _disc_bit, \ + .vout_reg = _vout_reg, \ + .vout_mask = _vout_mask, \ + .sleep_reg = _ds_reg, \ + .min_uV = _min_uv, \ + .max_uV = _max_uv , \ + .step_uV = _step_uV, \ + .nsteps = _nsteps, \ + .delay = _delay, \ + .id = RICOH619_ID_##_id, \ + .sleep_id = RICOH619_DS_##_id, \ + .eco_reg = _eco_reg, \ + .eco_bit = _eco_bit, \ + .eco_slp_reg = _eco_slp_reg, \ + .eco_slp_bit = _eco_slp_bit, \ + .desc = { \ + .name = ricoh619_rails(_id), \ + .id = RICOH619_ID_##_id, \ + .n_voltages = _nsteps, \ + .ops = &_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ +} + +static struct ricoh619_regulator ricoh619_regulator[] = { + RICOH619_REG(DC1, 0x2C, 0, 0x2C, 1, 0x36, 0xFF, 0x3B, + 600000, 3500000, 12500, 0xE8, ricoh619_ops, 500, + 0x00, 0, 0x00, 0), + + RICOH619_REG(DC2, 0x2E, 0, 0x2E, 1, 0x37, 0xFF, 0x3C, + 600000, 3500000, 12500, 0xE8, ricoh619_ops, 500, + 0x00, 0, 0x00, 0), + + RICOH619_REG(DC3, 0x30, 0, 0x30, 1, 0x38, 0xFF, 0x3D, + 600000, 3500000, 12500, 0xE8, ricoh619_ops, 500, + 0x00, 0, 0x00, 0), + + RICOH619_REG(DC4, 0x32, 0, 0x32, 1, 0x39, 0xFF, 0x3E, + 600000, 3500000, 12500, 0xE8, ricoh619_ops, 500, + 0x00, 0, 0x00, 0), + + RICOH619_REG(DC5, 0x34, 0, 0x34, 1, 0x3A, 0xFF, 0x3F, + 600000, 3500000, 12500, 0xE8, ricoh619_ops, 500, + 0x00, 0, 0x00, 0), + + RICOH619_REG(LDO1, 0x44, 0, 0x46, 0, 0x4C, 0x7F, 0x58, + 900000, 3500000, 25000, 0x68, ricoh619_ops, 500, + 0x48, 0, 0x4A, 0), + + RICOH619_REG(LDO2, 0x44, 1, 0x46, 1, 0x4D, 0x7F, 0x59, + 900000, 3500000, 25000, 0x68, ricoh619_ops, 500, + 0x48, 1, 0x4A, 1), + + RICOH619_REG(LDO3, 0x44, 2, 0x46, 2, 0x4E, 0x7F, 0x5A, + 900000, 3500000, 25000, 0x68, ricoh619_ops, 500, + 0x48, 2, 0x4A, 2), + + RICOH619_REG(LDO4, 0x44, 3, 0x46, 3, 0x4F, 0x7F, 0x5B, + 900000, 3500000, 25000, 0x68, ricoh619_ops, 500, + 0x48, 3, 0x4A, 3), + + RICOH619_REG(LDO5, 0x44, 4, 0x46, 4, 0x50, 0x7F, 0x5C, + 600000, 3500000, 25000, 0x74, ricoh619_ops, 500, + 0x48, 4, 0x4A, 4), + + RICOH619_REG(LDO6, 0x44, 5, 0x46, 5, 0x51, 0x7F, 0x5D, + 600000, 3500000, 25000, 0x74, ricoh619_ops, 500, + 0x48, 5, 0x4A, 5), + + RICOH619_REG(LDO7, 0x44, 6, 0x46, 6, 0x52, 0x7F, 0x5E, + 900000, 3500000, 25000, 0x68, ricoh619_ops, 500, + 0x00, 0, 0x00, 0), + + RICOH619_REG(LDO8, 0x44, 7, 0x46, 7, 0x53, 0x7F, 0x5F, + 900000, 3500000, 25000, 0x68, ricoh619_ops, 500, + 0x00, 0, 0x00, 0), + + RICOH619_REG(LDO9, 0x45, 0, 0x47, 0, 0x54, 0x7F, 0x60, + 900000, 3500000, 25000, 0x68, ricoh619_ops, 500, + 0x00, 0, 0x00, 0), + + RICOH619_REG(LDO10, 0x45, 1, 0x47, 1, 0x55, 0x7F, 0x61, + 900000, 3500000, 25000, 0x68, ricoh619_ops, 500, + 0x00, 0, 0x00, 0), + + RICOH619_REG(LDORTC1, 0x45, 4, 0x00, 0, 0x56, 0x7F, 0x00, + 1700000, 3500000, 25000, 0x48, ricoh619_ops, 500, + 0x00, 0, 0x00, 0), + + RICOH619_REG(LDORTC2, 0x45, 5, 0x00, 0, 0x57, 0x7F, 0x00, + 900000, 3500000, 25000, 0x68, ricoh619_ops, 500, + 0x00, 0, 0x00, 0), +}; +static inline struct ricoh619_regulator *find_regulator_info(int id) +{ + struct ricoh619_regulator *ri; + int i; + + for (i = 0; i < ARRAY_SIZE(ricoh619_regulator); i++) { + ri = &ricoh619_regulator[i]; + if (ri->desc.id == id) + return ri; + } + return NULL; +} + +static int ricoh619_regulator_preinit(struct device *parent, + struct ricoh619_regulator *ri, + struct ricoh619_regulator_platform_data *ricoh619_pdata) +{ + int ret = 0; + + if (!ricoh619_pdata->init_apply) + return 0; +/* + if (ricoh619_pdata->init_uV >= 0) { + ret = __ricoh619_set_voltage(parent, ri, + ricoh619_pdata->init_uV, + ricoh619_pdata->init_uV, 0); + if (ret < 0) { + dev_err(ri->dev, "Not able to initialize voltage %d " + "for rail %d err %d\n", ricoh619_pdata->init_uV, + ri->desc.id, ret); + return ret; + } + } +*/ + if (ricoh619_pdata->init_enable) + ret = ricoh619_set_bits(parent, ri->reg_en_reg, + (1 << ri->en_bit)); + else + ret = ricoh619_clr_bits(parent, ri->reg_en_reg, + (1 << ri->en_bit)); + if (ret < 0) + dev_err(ri->dev, "Not able to %s rail %d err %d\n", + (ricoh619_pdata->init_enable) ? "enable" : "disable", + ri->desc.id, ret); + + return ret; +} + +static inline int ricoh619_cache_regulator_register(struct device *parent, + struct ricoh619_regulator *ri) +{ + ri->vout_reg_cache = 0; + return ricoh619_read(parent, ri->vout_reg, &ri->vout_reg_cache); +} + +static int __devinit ricoh619_regulator_probe(struct platform_device *pdev) +{ + struct ricoh619_regulator *ri = NULL; + struct regulator_dev *rdev; + struct ricoh619_regulator_platform_data *tps_pdata; + int id = pdev->id; + int err; + + ri = find_regulator_info(id); + if (ri == NULL) { + dev_err(&pdev->dev, "invalid regulator ID specified\n"); + return -EINVAL; + } + tps_pdata = pdev->dev.platform_data; + ri->dev = &pdev->dev; +/* + err = ricoh619_cache_regulator_register(pdev->dev.parent, ri); + if (err) { + dev_err(&pdev->dev, "Fail in caching register\n"); + return err; + } + + err = ricoh619_regulator_preinit(pdev->dev.parent, ri, tps_pdata); + if (err) { + dev_err(&pdev->dev, "Fail in pre-initialisation\n"); + return err; + } + */ + rdev = regulator_register(&ri->desc, &pdev->dev, + &tps_pdata->regulator, ri); + if (IS_ERR_OR_NULL(rdev)) { + dev_err(&pdev->dev, "failed to register regulator %s\n", + ri->desc.name); + return PTR_ERR(rdev); + } + + platform_set_drvdata(pdev, rdev); + return 0; +} + +static int __devexit ricoh619_regulator_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + + regulator_unregister(rdev); + return 0; +} + +static struct platform_driver ricoh619_regulator_driver = { + .driver = { + .name = "ricoh619-regulator", + .owner = THIS_MODULE, + }, + .probe = ricoh619_regulator_probe, + .remove = __devexit_p(ricoh619_regulator_remove), +}; + +static int __init ricoh619_regulator_init(void) +{ + + return platform_driver_register(&ricoh619_regulator_driver); +} +subsys_initcall_sync(ricoh619_regulator_init); + +static void __exit ricoh619_regulator_exit(void) +{ + platform_driver_unregister(&ricoh619_regulator_driver); +} +module_exit(ricoh619_regulator_exit); + +MODULE_DESCRIPTION("RICOH619 regulator driver"); +MODULE_ALIAS("platform:ricoh619-regulator"); +MODULE_AUTHOR("zhangqing "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 62424f04a6c2..bf69ac4bdb81 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1121,5 +1121,15 @@ config RK808_RTC depends on MFD_RK808 help enable rk808 rtc for system + +config RTC_DRV_RC5T619 + tristate "RICOH RC5T619 PMU RTC driver" + depends on MFD_RICOH619 + default n + help + If you say yes here you get support for the RICOH RC5T619 RTC module. + + This driver can also be built as a module. If so, the module + will be called rtc-rc5t619. endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index e429fff538ec..936824da423b 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -117,3 +117,4 @@ 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 +obj-$(CONFIG_RTC_DRV_RC5T619) += rtc-ricoh619.o diff --git a/drivers/rtc/rtc-ricoh619.c b/drivers/rtc/rtc-ricoh619.c new file mode 100644 index 000000000000..cae88e1305ef --- /dev/null +++ b/drivers/rtc/rtc-ricoh619.c @@ -0,0 +1,413 @@ +/* + * drivers/rtc/rtc-ricoh619.c + * + * Real time clock driver for RICOH RC5T619 power management chip. + * + * Copyright (C) 2012-2013 RICOH COMPANY,LTD + * + * Based on code + * Copyright (C) 2011 NVIDIA Corporation + * + * this program is free software; you can redistribute it and/or modify + * it under the terms of the gnu general public license as published by + * the free software foundation; either version 2 of the license, or + * (at your option) any later version. + * + * this program is distributed in the hope that it will be useful, but without + * any warranty; without even the implied warranty of merchantability or + * fitness for a particular purpose. see the gnu general public license for + * more details. + * + * you should have received a copy of the gnu general public license + * along with this program. If not, see . + * + */ + +/* #define debug 1 */ +/* #define verbose_debug 1 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ricoh619_rtc { + unsigned long epoch_start; + int irq; + struct rtc_device *rtc; + bool irq_en; +}; + +static int ricoh619_read_regs(struct device *dev, int reg, int len, + uint8_t *val) +{ + int ret; + + ret = ricoh619_bulk_reads(dev->parent, reg, len, val); + if (ret < 0) { + dev_err(dev->parent, "\n %s failed reading from 0x%02x\n", + __func__, reg); + WARN_ON(1); + } + return ret; +} + +static int ricoh619_write_regs(struct device *dev, int reg, int len, + uint8_t *val) +{ + int ret; + ret = ricoh619_bulk_writes(dev->parent, reg, len, val); + if (ret < 0) { + dev_err(dev->parent, "\n %s failed writing\n", __func__); + WARN_ON(1); + } + + return ret; +} + +static int ricoh619_rtc_valid_tm(struct device *dev, struct rtc_time *tm) +{ + if (tm->tm_year >= (rtc_year_offset + 99) + || tm->tm_mon > 12 + || tm->tm_mday < 1 + || tm->tm_mday > rtc_month_days(tm->tm_mon, + tm->tm_year + os_ref_year) + || tm->tm_hour >= 24 + || tm->tm_min >= 60 + || tm->tm_sec >= 60) { + dev_err(dev->parent, "\n returning error due to time" + "%d/%d/%d %d:%d:%d", tm->tm_mon, tm->tm_mday, + tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec); + return -EINVAL; + } + return 0; +} + +static u8 dec2bcd(u8 dec) +{ + return ((dec/10)<<4)+(dec%10); +} + +static u8 bcd2dec(u8 bcd) +{ + return (bcd >> 4)*10+(bcd & 0xf); +} + +static void convert_bcd_to_decimal(u8 *buf, u8 len) +{ + int i = 0; + for (i = 0; i < len; i++) + buf[i] = bcd2dec(buf[i]); +} + +static void convert_decimal_to_bcd(u8 *buf, u8 len) +{ + int i = 0; + for (i = 0; i < len; i++) + buf[i] = dec2bcd(buf[i]); +} + +static void print_time(struct device *dev, struct rtc_time *tm) +{ + dev_info(dev, "rtc-time : %d/%d/%d %d:%d\n", + (tm->tm_mon + 1), tm->tm_mday, (tm->tm_year + os_ref_year), + tm->tm_hour, tm->tm_min); +} + +static int ricoh619_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + u8 buff[7]; + int err; + err = ricoh619_read_regs(dev, rtc_seconds_reg, sizeof(buff), buff); + if (err < 0) { + dev_err(dev, "\n %s :: failed to read time\n", __FILE__); + return err; + } + convert_bcd_to_decimal(buff, sizeof(buff)); + tm->tm_sec = buff[0]; + tm->tm_min = buff[1]; + tm->tm_hour = buff[2]; + tm->tm_wday = buff[3]; + tm->tm_mday = buff[4]; + tm->tm_mon = buff[5] - 1; + tm->tm_year = buff[6] + rtc_year_offset; +// print_time(dev, tm); + return ricoh619_rtc_valid_tm(dev, tm); +} + +static int ricoh619_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + u8 buff[7]; + int err; + +// print_time(dev, tm); + buff[0] = tm->tm_sec; + buff[1] = tm->tm_min; + buff[2] = tm->tm_hour; + buff[3] = tm->tm_wday; + buff[4] = tm->tm_mday; + buff[5] = tm->tm_mon + 1; + buff[6] = tm->tm_year - rtc_year_offset; + + convert_decimal_to_bcd(buff, sizeof(buff)); + err = ricoh619_write_regs(dev, rtc_seconds_reg, sizeof(buff), buff); + if (err < 0) { + dev_err(dev->parent, "\n failed to program new time\n"); + return err; + } + + return 0; +} +static int ricoh619_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm); + +static int ricoh619_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct ricoh619_rtc *rtc = dev_get_drvdata(dev); + unsigned long seconds; + u8 buff[6]; + int err; + struct rtc_time tm; + + if (rtc->irq == -1) + return -EIO; + + rtc_tm_to_time(&alrm->time, &seconds); + err = ricoh619_rtc_read_time(dev, &tm); + if (err) { + dev_err(dev, "\n failed to read time\n"); + return err; + } + rtc_tm_to_time(&tm, &rtc->epoch_start); + + dev_info(dev->parent, "\n setting alarm to requested time::\n"); +// print_time(dev->parent, &alrm->time); + + if (WARN_ON(alrm->enabled && (seconds < rtc->epoch_start))) { + dev_err(dev->parent, "\n can't set alarm to requested time\n"); + return -EINVAL; + } + + if (alrm->enabled && !rtc->irq_en) + rtc->irq_en = true; + else if (!alrm->enabled && rtc->irq_en) + rtc->irq_en = false; + + buff[0] = alrm->time.tm_sec; + buff[1] = alrm->time.tm_min; + buff[2] = alrm->time.tm_hour; + buff[3] = alrm->time.tm_mday; + buff[4] = alrm->time.tm_mon + 1; + buff[5] = alrm->time.tm_year - rtc_year_offset; + convert_decimal_to_bcd(buff, sizeof(buff)); + buff[3] |= 0x80; /* set DAL_EXT */ + err = ricoh619_write_regs(dev, rtc_alarm_y_sec, sizeof(buff), buff); + if (err) { + dev_err(dev->parent, "\n unable to set alarm\n"); + return -EBUSY; + } + + err = ricoh619_read_regs(dev, rtc_ctrl2, 1, buff); + if (err) { + dev_err(dev->parent, "unable to read rtc_ctrl2 reg\n"); + return -EBUSY; + } + + buff[1] = buff[0] & ~0x81; /* to clear alarm-D flag, and set adjustment parameter */ + buff[0] = 0x60; /* to enable alarm_d and 24-hour format */ + err = ricoh619_write_regs(dev, rtc_ctrl1, 2, buff); + if (err) { + dev_err(dev, "failed programming rtc ctrl regs\n"); + return -EBUSY; + } +return err; +} + +static int ricoh619_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + u8 buff[6]; + int err; + + err = ricoh619_read_regs(dev, rtc_alarm_y_sec, sizeof(buff), buff); + if (err) + return err; + buff[3] &= ~0x80; /* clear DAL_EXT */ + convert_bcd_to_decimal(buff, sizeof(buff)); + + alrm->time.tm_sec = buff[0]; + alrm->time.tm_min = buff[1]; + alrm->time.tm_hour = buff[2]; + alrm->time.tm_mday = buff[3]; + alrm->time.tm_mon = buff[4] - 1; + alrm->time.tm_year = buff[5] + rtc_year_offset; + +// dev_info(dev->parent, "\n getting alarm time::\n"); +// print_time(dev, &alrm->time); + + return 0; +} + +static const struct rtc_class_ops ricoh619_rtc_ops = { + .read_time = ricoh619_rtc_read_time, + .set_time = ricoh619_rtc_set_time, + .set_alarm = ricoh619_rtc_set_alarm, + .read_alarm = ricoh619_rtc_read_alarm, +}; + +static irqreturn_t ricoh619_rtc_irq(int irq, void *data) +{ + struct device *dev = data; + struct ricoh619_rtc *rtc = dev_get_drvdata(dev); + u8 reg; + int err; + + /* clear alarm-D status bits.*/ + err = ricoh619_read_regs(dev, rtc_ctrl2, 1, ®); + if (err) { + dev_err(dev->parent, "unable to read rtc_ctrl2 reg\n"); + } + reg &= ~0x81; /* to clear alarm-D flag, and set adjustment parameter */ + err = ricoh619_write_regs(dev, rtc_ctrl2, 1, ®); + if (err) { + dev_err(dev->parent, "unable to program rtc_status reg\n"); + } + + rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF); + return IRQ_HANDLED; +} + +static int __devinit ricoh619_rtc_probe(struct platform_device *pdev) +{ + struct ricoh619_rtc_platform_data *pdata = pdev->dev.platform_data; + struct ricoh619_rtc *rtc; + struct rtc_time tm; + int err; + u8 reg; + rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); +// printk("%s,line=%d\n", __func__,__LINE__); + + if (!rtc) + return -ENOMEM; + + rtc->irq = -1; + + if (!pdata) { + dev_err(&pdev->dev, "no platform_data specified\n"); + return -EINVAL; + } + + if (pdata->irq < 0) + dev_err(&pdev->dev, "\n no irq specified, wakeup is disabled\n"); + + dev_set_drvdata(&pdev->dev, rtc); + device_init_wakeup(&pdev->dev, 1); + rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, + &ricoh619_rtc_ops, THIS_MODULE); + + if (IS_ERR(rtc->rtc)) { + err = PTR_ERR(rtc->rtc); + goto fail; + } + + reg = 0x20; /* to clear power-on-reset flag */ + err = ricoh619_write_regs(&pdev->dev, rtc_ctrl2, 1, ®); + if (err) { + dev_err(&pdev->dev, "failed rtc setup\n"); + return -EBUSY; + } + + reg = 0x60; /* to enable alarm_d and 24-hour format */ + err = ricoh619_write_regs(&pdev->dev, rtc_ctrl1, 1, ®); + if (err) { + dev_err(&pdev->dev, "failed rtc setup\n"); + return -EBUSY; + } + + reg = 0; /* clearing RTC Adjust register */ + err = ricoh619_write_regs(&pdev->dev, rtc_adjust, 1, ®); + if (err) { + dev_err(&pdev->dev, "unable to program rtc_adjust reg\n"); + return -EBUSY; + } + + err = ricoh619_rtc_read_time(&pdev->dev, &tm); + if (err) { + dev_err(&pdev->dev, "\n failed to read time\n"); + return err; + } + if (ricoh619_rtc_valid_tm(&pdev->dev, &tm)) { + if (pdata->time.tm_year < 2000 || pdata->time.tm_year > 2100) { + memset(&pdata->time, 0, sizeof(pdata->time)); + pdata->time.tm_year = rtc_year_offset; + pdata->time.tm_mday = 1; + } else + pdata->time.tm_year -= os_ref_year; + err = ricoh619_rtc_set_time(&pdev->dev, &pdata->time); + if (err) { + dev_err(&pdev->dev, "\n failed to set time\n"); + return err; + } + } + if (pdata && (pdata->irq >= 0)) { + rtc->irq = pdata->irq + RICOH619_IRQ_DALE; + err = request_threaded_irq(rtc->irq, NULL, ricoh619_rtc_irq, + IRQF_ONESHOT, "rtc_ricoh619", + &pdev->dev); + if (err) { + dev_err(&pdev->dev, "request IRQ:%d fail\n", rtc->irq); + rtc->irq = -1; + } else { + device_init_wakeup(&pdev->dev, 1); + enable_irq_wake(rtc->irq); + } + } + return 0; + +fail: + if (!IS_ERR_OR_NULL(rtc->rtc)) + rtc_device_unregister(rtc->rtc); + kfree(rtc); + return err; +} + +static int __devexit ricoh619_rtc_remove(struct platform_device *pdev) +{ + struct ricoh619_rtc *rtc = dev_get_drvdata(&pdev->dev); + + if (rtc->irq != -1) + free_irq(rtc->irq, rtc); + rtc_device_unregister(rtc->rtc); + kfree(rtc); + return 0; +} + +static struct platform_driver ricoh619_rtc_driver = { + .driver = { + .name = "rtc_ricoh619", + .owner = THIS_MODULE, + }, + .probe = ricoh619_rtc_probe, + .remove = __devexit_p(ricoh619_rtc_remove), +}; + +static int __init ricoh619_rtc_init(void) +{ + return platform_driver_register(&ricoh619_rtc_driver); +} +subsys_initcall_sync(ricoh619_rtc_init); + +static void __exit ricoh619_rtc_exit(void) +{ + platform_driver_unregister(&ricoh619_rtc_driver); +} +module_exit(ricoh619_rtc_exit); + +MODULE_DESCRIPTION("RICOH RICOH619 RTC driver"); +MODULE_ALIAS("platform:rtc_ricoh619"); +MODULE_AUTHOR("zhangqing "); +MODULE_LICENSE("GPL"); + diff --git a/include/linux/mfd/ricoh619.h b/include/linux/mfd/ricoh619.h new file mode 100644 index 000000000000..627a607c45a7 --- /dev/null +++ b/include/linux/mfd/ricoh619.h @@ -0,0 +1,354 @@ +/* + * include/linux/mfd/ricoh619.h + * + * Core driver interface to access RICOH RC5T619 power management chip. + * + * Copyright (C) 2012-2013 RICOH COMPANY,LTD + * + * Based on code + * Copyright (C) 2011 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef __LINUX_MFD_RICOH619_H +#define __LINUX_MFD_RICOH619_H + +#include +#include +#include +#include + +/* Maximum number of main interrupts */ +#define MAX_INTERRUPT_MASKS 13 +#define MAX_MAIN_INTERRUPT 7 +#define MAX_GPEDGE_REG 2 + +/* Power control register */ +#define RICOH619_PWR_WD 0x0B +#define RICOH619_PWR_WD_COUNT 0x0C +#define RICOH619_PWR_FUNC 0x0D +#define RICOH619_PWR_SLP_CNT 0x0E +#define RICOH619_PWR_REP_CNT 0x0F +#define RICOH619_PWR_ON_TIMSET 0x10 +#define RICOH619_PWR_NOE_TIMSET 0x11 +#define RICOH619_PWR_IRSEL 0x15 + +/* Interrupt enable register */ +#define RICOH619_INT_EN_SYS 0x12 +#define RICOH619_INT_EN_DCDC 0x40 +#define RICOH619_INT_EN_RTC 0xAE +#define RICOH619_INT_EN_ADC1 0x88 +#define RICOH619_INT_EN_ADC2 0x89 +#define RICOH619_INT_EN_ADC3 0x8A +#define RICOH619_INT_EN_GPIO 0x94 +#define RICOH619_INT_EN_GPIO2 0x94 // dummy +#define RICOH619_INT_MSK_CHGCTR 0xBE +#define RICOH619_INT_MSK_CHGSTS1 0xBF +#define RICOH619_INT_MSK_CHGSTS2 0xC0 +#define RICOH619_INT_MSK_CHGERR 0xC1 +#define RICOH619_INT_MSK_CHGEXTIF 0xD1 + +/* Interrupt select register */ +#define RICOH619_PWR_IRSEL 0x15 +#define RICOH619_CHG_CTRL_DETMOD1 0xCA +#define RICOH619_CHG_CTRL_DETMOD2 0xCB +#define RICOH619_CHG_STAT_DETMOD1 0xCC +#define RICOH619_CHG_STAT_DETMOD2 0xCD +#define RICOH619_CHG_STAT_DETMOD3 0xCE + + +/* interrupt status registers (monitor regs)*/ +#define RICOH619_INTC_INTPOL 0x9C +#define RICOH619_INTC_INTEN 0x9D +#define RICOH619_INTC_INTMON 0x9E + +#define RICOH619_INT_MON_SYS 0x14 +#define RICOH619_INT_MON_DCDC 0x42 +#define RICOH619_INT_MON_RTC 0xAF + +#define RICOH619_INT_MON_CHGCTR 0xC6 +#define RICOH619_INT_MON_CHGSTS1 0xC7 +#define RICOH619_INT_MON_CHGSTS2 0xC8 +#define RICOH619_INT_MON_CHGERR 0xC9 +#define RICOH619_INT_MON_CHGEXTIF 0xD3 + +/* interrupt clearing registers */ +#define RICOH619_INT_IR_SYS 0x13 +#define RICOH619_INT_IR_DCDC 0x41 +#define RICOH619_INT_IR_RTC 0xAF +#define RICOH619_INT_IR_ADCL 0x8C +#define RICOH619_INT_IR_ADCH 0x8D +#define RICOH619_INT_IR_ADCEND 0x8E +#define RICOH619_INT_IR_GPIOR 0x95 +#define RICOH619_INT_IR_GPIOF 0x96 +#define RICOH619_INT_IR_CHGCTR 0xC2 +#define RICOH619_INT_IR_CHGSTS1 0xC3 +#define RICOH619_INT_IR_CHGSTS2 0xC4 +#define RICOH619_INT_IR_CHGERR 0xC5 +#define RICOH619_INT_IR_CHGEXTIF 0xD2 + +/* GPIO register base address */ +#define RICOH619_GPIO_IOSEL 0x90 +#define RICOH619_GPIO_IOOUT 0x91 +#define RICOH619_GPIO_GPEDGE1 0x92 +#define RICOH619_GPIO_GPEDGE2 0x93 +//#define RICOH619_GPIO_EN_GPIR 0x94 +//#define RICOH619_GPIO_IR_GPR 0x95 +//#define RICOH619_GPIO_IR_GPF 0x96 +#define RICOH619_GPIO_MON_IOIN 0x97 +#define RICOH619_GPIO_LED_FUNC 0x98 + +#define RICOH619_REG_BANKSEL 0xFF + +/* Charger Control register */ +#define RICOH619_CHG_CTL1 0xB3 + +/* ADC Control register */ +#define RICOH619_ADC_CNT1 0x64 +#define RICOH619_ADC_CNT2 0x65 +#define RICOH619_ADC_CNT3 0x66 +#define RICOH619_ADC_VADP_THL 0x7C +#define RICOH619_ADC_VSYS_THL 0x80 + +#define RICOH619_FG_CTRL 0xE0 +#define RICOH619_PSWR 0x07 + +/* RICOH619 IRQ definitions */ +enum { + RICOH619_IRQ_POWER_ON, + RICOH619_IRQ_EXTIN, + RICOH619_IRQ_PRE_VINDT, + RICOH619_IRQ_PREOT, + RICOH619_IRQ_POWER_OFF, + RICOH619_IRQ_NOE_OFF, + RICOH619_IRQ_WD, + RICOH619_IRQ_CLK_STP, + + RICOH619_IRQ_DC1LIM, + RICOH619_IRQ_DC2LIM, + RICOH619_IRQ_DC3LIM, + RICOH619_IRQ_DC4LIM, + RICOH619_IRQ_DC5LIM, + + RICOH619_IRQ_ILIMLIR, + RICOH619_IRQ_VBATLIR, + RICOH619_IRQ_VADPLIR, + RICOH619_IRQ_VUSBLIR, + RICOH619_IRQ_VSYSLIR, + RICOH619_IRQ_VTHMLIR, + RICOH619_IRQ_AIN1LIR, + RICOH619_IRQ_AIN0LIR, + + RICOH619_IRQ_ILIMHIR, + RICOH619_IRQ_VBATHIR, + RICOH619_IRQ_VADPHIR, + RICOH619_IRQ_VUSBHIR, + RICOH619_IRQ_VSYSHIR, + RICOH619_IRQ_VTHMHIR, + RICOH619_IRQ_AIN1HIR, + RICOH619_IRQ_AIN0HIR, + + RICOH619_IRQ_ADC_ENDIR, + + RICOH619_IRQ_GPIO0, + RICOH619_IRQ_GPIO1, + RICOH619_IRQ_GPIO2, + RICOH619_IRQ_GPIO3, + RICOH619_IRQ_GPIO4, + + RICOH619_IRQ_CTC, + RICOH619_IRQ_DALE, + + RICOH619_IRQ_FVADPDETSINT, + RICOH619_IRQ_FVUSBDETSINT, + RICOH619_IRQ_FVADPLVSINT, + RICOH619_IRQ_FVUSBLVSINT, + RICOH619_IRQ_FWVADPSINT, + RICOH619_IRQ_FWVUSBSINT, + + RICOH619_IRQ_FONCHGINT, + RICOH619_IRQ_FCHGCMPINT, + RICOH619_IRQ_FBATOPENINT, + RICOH619_IRQ_FSLPMODEINT, + RICOH619_IRQ_FBTEMPJTA1INT, + RICOH619_IRQ_FBTEMPJTA2INT, + RICOH619_IRQ_FBTEMPJTA3INT, + RICOH619_IRQ_FBTEMPJTA4INT, + + RICOH619_IRQ_FCURTERMINT, + RICOH619_IRQ_FVOLTERMINT, + RICOH619_IRQ_FICRVSINT, + RICOH619_IRQ_FPOOR_CHGCURINT, + RICOH619_IRQ_FOSCFDETINT1, + RICOH619_IRQ_FOSCFDETINT2, + RICOH619_IRQ_FOSCFDETINT3, + RICOH619_IRQ_FOSCMDETINT, + + RICOH619_IRQ_FDIEOFFINT, + RICOH619_IRQ_FDIEERRINT, + RICOH619_IRQ_FBTEMPERRINT, + RICOH619_IRQ_FVBATOVINT, + RICOH619_IRQ_FTTIMOVINT, + RICOH619_IRQ_FRTIMOVINT, + RICOH619_IRQ_FVADPOVSINT, + RICOH619_IRQ_FVUSBOVSINT, + + RICOH619_IRQ_FGCDET, + RICOH619_IRQ_FPCDET, + RICOH619_IRQ_FWARN_ADP, + + /* Should be last entry */ + RICOH619_NR_IRQS, +}; + +/* Ricoh619 gpio definitions */ +enum { + RICOH619_GPIO0, + RICOH619_GPIO1, + RICOH619_GPIO2, + RICOH619_GPIO3, + RICOH619_GPIO4, + + RICOH619_NR_GPIO, +}; + +enum ricoh619_sleep_control_id { + RICOH619_DS_DC1, + RICOH619_DS_DC2, + RICOH619_DS_DC3, + RICOH619_DS_DC4, + RICOH619_DS_DC5, + RICOH619_DS_LDO1, + RICOH619_DS_LDO2, + RICOH619_DS_LDO3, + RICOH619_DS_LDO4, + RICOH619_DS_LDO5, + RICOH619_DS_LDO6, + RICOH619_DS_LDO7, + RICOH619_DS_LDO8, + RICOH619_DS_LDO9, + RICOH619_DS_LDO10, + RICOH619_DS_LDORTC1, + RICOH619_DS_LDORTC2, + RICOH619_DS_PSO0, + RICOH619_DS_PSO1, + RICOH619_DS_PSO2, + RICOH619_DS_PSO3, + RICOH619_DS_PSO4, +}; + + +struct ricoh619_subdev_info { + int id; + const char *name; + void *platform_data; +}; + +/* +struct ricoh619_rtc_platform_data { + int irq; + struct rtc_time time; +}; +*/ + +struct ricoh619_gpio_init_data { + unsigned output_mode_en:1; /* Enable output mode during init */ + unsigned output_val:1; /* Output value if it is in output mode */ + unsigned init_apply:1; /* Apply init data on configuring gpios*/ + unsigned led_mode:1; /* Select LED mode during init */ + unsigned led_func:1; /* Set LED function if LED mode is 1 */ +}; + +struct ricoh619 { + struct device *dev; + struct i2c_client *client; + struct mutex io_lock; + int gpio_base; + struct gpio_chip gpio_chip; + int irq_base; +// struct irq_chip irq_chip; + int chip_irq; + struct mutex irq_lock; + unsigned long group_irq_en[MAX_MAIN_INTERRUPT]; + + /* For main interrupt bits in INTC */ + u8 intc_inten_cache; + u8 intc_inten_reg; + + /* For group interrupt bits and address */ + u8 irq_en_cache[MAX_INTERRUPT_MASKS]; + u8 irq_en_reg[MAX_INTERRUPT_MASKS]; + + /* For gpio edge */ + u8 gpedge_cache[MAX_GPEDGE_REG]; + u8 gpedge_reg[MAX_GPEDGE_REG]; + + int bank_num; +}; + +struct ricoh619_platform_data { + int num_subdevs; + struct ricoh619_subdev_info *subdevs; + int (*init_port)(int irq_num); // Init GPIO for IRQ pin + int gpio_base; + int irq_base; + struct ricoh619_gpio_init_data *gpio_init_data; + int num_gpioinit_data; + bool enable_shutdown_pin; + int (*pre_init)(struct ricoh619 *ricoh619); + int (*post_init)(struct ricoh619 *ricoh619); +}; + +/* ==================================== */ +/* RICOH619 Power_Key device data */ +/* ==================================== */ +struct ricoh619_pwrkey_platform_data { + int irq; + unsigned long delay_ms; +}; +extern int pwrkey_wakeup; +extern struct ricoh619 *g_ricoh619; +/* ==================================== */ +/* RICOH619 battery device data */ +/* ==================================== */ +extern int g_soc; +extern int g_fg_on_mode; + +extern int ricoh619_read(struct device *dev, uint8_t reg, uint8_t *val); +extern int ricoh619_read_bank1(struct device *dev, uint8_t reg, uint8_t *val); +extern int ricoh619_bulk_reads(struct device *dev, u8 reg, u8 count, + uint8_t *val); +extern int ricoh619_bulk_reads_bank1(struct device *dev, u8 reg, u8 count, + uint8_t *val); +extern int ricoh619_write(struct device *dev, u8 reg, uint8_t val); +extern int ricoh619_write_bank1(struct device *dev, u8 reg, uint8_t val); +extern int ricoh619_bulk_writes(struct device *dev, u8 reg, u8 count, + uint8_t *val); +extern int ricoh619_bulk_writes_bank1(struct device *dev, u8 reg, u8 count, + uint8_t *val); +extern int ricoh619_set_bits(struct device *dev, u8 reg, uint8_t bit_mask); +extern int ricoh619_clr_bits(struct device *dev, u8 reg, uint8_t bit_mask); +extern int ricoh619_update(struct device *dev, u8 reg, uint8_t val, + uint8_t mask); +extern int ricoh619_update_bank1(struct device *dev, u8 reg, uint8_t val, + uint8_t mask); +extern int ricoh619_power_off(void); +extern int ricoh619_irq_init(struct ricoh619 *ricoh619, int irq, int irq_base); +extern int ricoh619_irq_exit(struct ricoh619 *ricoh619); +extern int ricoh619_power_off(void); + +#endif diff --git a/include/linux/power/ricoh619_battery.h b/include/linux/power/ricoh619_battery.h new file mode 100644 index 000000000000..d930ff6c57df --- /dev/null +++ b/include/linux/power/ricoh619_battery.h @@ -0,0 +1,121 @@ +/* + * include/linux/power/ricoh619-battery.c + * + * RICOH RC5T619 Charger Driver + * + * Copyright (C) 2012-2013 RICOH COMPANY,LTD + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#ifndef __LINUX_POWER_RICOH619_H_ +#define __LINUX_POWER_RICOH619_H_ + +/* #include */ +/* #include */ + +#if 0 +#define RICOH_FG_DBG(fmt, args...) printk(KERN_DEBUG "RICOH_FG_DBG:\t"fmt, ##args) +#else +#define RICOH_FG_DBG(fmt, args...) {while(0);} +#endif + +/* Defined buttery information */ +#define ADC_VDD_MV 2800 +#define MIN_VOLTAGE 3100 +#define MAX_VOLTAGE 4200 + +/* 619 Register information */ +/* bank 0 */ +#define PSWR_REG 0x07 +/* for ADC */ +#define INTEN_REG 0x9D +#define EN_ADCIR3_REG 0x8A +#define ADCCNT3_REG 0x66 +#define VBATDATAH_REG 0x6A +#define VBATDATAL_REG 0x6B + +#define CHGCTL1_REG 0xB3 +#define REGISET1_REG 0xB6 +#define REGISET2_REG 0xB7 +#define CHGISET_REG 0xB8 +#define BATSET2_REG 0xBB + +#define CHGSTATE_REG 0xBD + +#define FG_CTRL_REG 0xE0 +#define SOC_REG 0xE1 +#define RE_CAP_H_REG 0xE2 +#define RE_CAP_L_REG 0xE3 +#define FA_CAP_H_REG 0xE4 +#define FA_CAP_L_REG 0xE5 +#define TT_EMPTY_H_REG 0xE7 +#define TT_EMPTY_L_REG 0xE8 +#define TT_FULL_H_REG 0xE9 +#define TT_FULL_L_REG 0xEA +#define VOLTAGE_1_REG 0xEB +#define VOLTAGE_2_REG 0xEC +#define TEMP_1_REG 0xED +#define TEMP_2_REG 0xEE + +#define CC_CTRL_REG 0xEF +#define CC_SUMREG3_REG 0xF3 +#define CC_SUMREG2_REG 0xF4 +#define CC_SUMREG1_REG 0xF5 +#define CC_SUMREG0_REG 0xF6 +#define CC_AVERAGE1_REG 0xFB +#define CC_AVERAGE0_REG 0xFC + +/* bank 1 */ +/* Top address for battery initial setting */ +#define BAT_INIT_TOP_REG 0xBC +#define BAT_REL_SEL_REG 0xDA +/**************************/ + +/* detailed status in CHGSTATE (0xBD) */ +enum ChargeState { + CHG_STATE_CHG_OFF = 0, + CHG_STATE_CHG_READY_VADP, + CHG_STATE_CHG_TRICKLE, + CHG_STATE_CHG_RAPID, + CHG_STATE_CHG_COMPLETE, + CHG_STATE_SUSPEND, + CHG_STATE_VCHG_OVER_VOL, + CHG_STATE_BAT_ERROR, + CHG_STATE_NO_BAT, + CHG_STATE_BAT_OVER_VOL, + CHG_STATE_BAT_TEMP_ERR, + CHG_STATE_DIE_ERR, + CHG_STATE_DIE_SHUTDOWN, + CHG_STATE_NO_BAT2, + CHG_STATE_CHG_READY_VUSB, +}; + +enum SupplyState { + SUPPLY_STATE_BAT = 0, + SUPPLY_STATE_ADP, + SUPPLY_STATE_USB, +} ; + +struct ricoh619_battery_platform_data { + int irq; + int alarm_vol_mv; + int multiple; + unsigned long monitor_time; +}; + +extern struct ricoh619 *g_ricoh619; + + +#endif diff --git a/include/linux/power/ricoh61x_battery_init.h b/include/linux/power/ricoh61x_battery_init.h new file mode 100644 index 000000000000..d772d6f113d7 --- /dev/null +++ b/include/linux/power/ricoh61x_battery_init.h @@ -0,0 +1,54 @@ +/* + * include/linux/power/ricoh61x_battery_init.h + * + * Battery initial parameter for RICOH RN5T618/619 power management chip. + * + * Copyright (C) 2012-2013 RICOH COMPANY,LTD + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef __LINUX_POWER_RICOH61X_BATTERY_INIT_H +#define __LINUX_POWER_RICOH61X_BATTERY_INIT_H + + +uint8_t battery_init_para[32] = { + 0x0B, 0x3F, 0x0B, 0xCB, 0x0B, 0xEE, 0x0C, 0x08, 0x0C, 0x1E, 0x0C, 0x38, 0x0C, 0x5B, 0x0C, 0x94, + 0x0C, 0xC8, 0x0D, 0x08, 0x0D, 0x55, 0x0E, 0x14, 0x00, 0x39, 0x0F, 0xC8, 0x05, 0x2C, 0x22, 0x56 +// 0x0C, 0xC8, 0x0D, 0x08, 0x0D, 0x55, 0x0E, 0x14, 0x00, 0x3E, 0x0F, 0xC8, 0x05, 0x2C, 0x22, 0x56 //150ohme +// 0x0C, 0xC8, 0x0D, 0x08, 0x0D, 0x55, 0x0E, 0x14, 0x00, 0x32, 0x0F, 0xC8, 0x05, 0x2C, 0x22, 0x56 //120ohme + //0x08, 0xa3, 0x0a, 0xf9, 0x0b, 0x4b, 0x0b, 0x74, + //0x0b, 0x94, 0x0b, 0xb5, 0x0b, 0xe6, 0x0c, 0x30, + //0x0c, 0x92, 0x0c, 0xf4, 0x0d, 0x6f, 0x08, 0xca, + //0x00, 0x36, 0x0f, 0xc8, 0x05, 0x2c, 0x22, 0x56 +}; + +#endif + +/* + +nominal_capacity=3800 +cut-off_v=3400 +thermistor_b=3435 +board_impe=0 +bat_impe=0.1363 +load_c=768 +available_c=3604 +battery_v=3515 +MakingMode=Normal +ChargeV=4.20V +LoadMode=Resistor + */ + diff --git a/include/linux/regulator/ricoh619-regulator.h b/include/linux/regulator/ricoh619-regulator.h new file mode 100755 index 000000000000..96b4cee71a8c --- /dev/null +++ b/include/linux/regulator/ricoh619-regulator.h @@ -0,0 +1,72 @@ +/* + * linux/regulator/ricoh619-regulator.h + * + * Regulator driver for RICOH619 power management chip. + * + * Copyright (C) 2012-2013 RICOH COMPANY,LTD + * + * Based on code + * Copyright (C) 2011 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef __LINUX_REGULATOR_RICOH619_H +#define __LINUX_REGULATOR_RICOH619_H + +#include +#include + +#define ricoh619_rails(_name) "RICOH619_"#_name + +/* RICHOH Regulator IDs */ +enum regulator_id { + RICOH619_ID_DC1, + RICOH619_ID_DC2, + RICOH619_ID_DC3, + RICOH619_ID_DC4, + RICOH619_ID_DC5, + RICOH619_ID_LDO1, + RICOH619_ID_LDO2, + RICOH619_ID_LDO3, + RICOH619_ID_LDO4, + RICOH619_ID_LDO5, + RICOH619_ID_LDO6, + RICOH619_ID_LDO7, + RICOH619_ID_LDO8, + RICOH619_ID_LDO9, + RICOH619_ID_LDO10, + RICOH619_ID_LDORTC1, + RICOH619_ID_LDORTC2, +}; + +struct ricoh619_regulator_platform_data { + struct regulator_init_data regulator; + int init_uV; + unsigned init_enable:1; + unsigned init_apply:1; + int sleep_uV; + int sleep_slots; + unsigned long ext_pwr_req; + unsigned long flags; +}; + +extern int ricoh619_regulator_enable_eco_mode(struct regulator_dev *rdev); +extern int ricoh619_regulator_disable_eco_mode(struct regulator_dev *rdev); +extern int ricoh619_regulator_enable_eco_slp_mode(struct regulator_dev *rdev); +extern int ricoh619_regulator_disable_eco_slp_mode(struct regulator_dev *rdev); + + +#endif diff --git a/include/linux/rtc/rtc-ricoh619.h b/include/linux/rtc/rtc-ricoh619.h new file mode 100755 index 000000000000..317e7ef9364b --- /dev/null +++ b/include/linux/rtc/rtc-ricoh619.h @@ -0,0 +1,57 @@ +/* + * include/linux/rtc/rtc-ricoh619.h + * + * Real time clock driver for RICOH RC5T619 power management chip. + * + * Copyright (C) 2012-2013 RICOH COMPANY,LTD + * + * Based on code + * Copyright (C) 2011 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * you should have received a copy of the gnu general public license + * along with this program. If not, see . + * + */ +#ifndef __LINUX_RTC_RICOH619_H_ +#define __LINUX_RTC_RICOH619_H_ + +#include + +#define rtc_ctrl1 0xAE +#define rtc_ctrl2 0xAF +#define rtc_seconds_reg 0xA0 +#define rtc_alarm_y_sec 0xA8 +#define rtc_adjust 0xA7 + + +/* +linux rtc driver refers 1900 as base year in many calculations. +(e.g. refer drivers/rtc/rtc-lib.c) +*/ +#define os_ref_year 1900 + +/* + pmu rtc have only 2 nibbles to store year information, so using an + offset of 100 to set the base year as 2000 for our driver. +*/ +#define rtc_year_offset 100 + + + +struct ricoh619_rtc_platform_data { + int irq; + struct rtc_time time; +}; + + +#endif -- 2.34.1