From: 张晴 Date: Thu, 4 Jul 2013 07:09:13 +0000 (+0800) Subject: rk31xx:RT5025:support pmic rt5025 X-Git-Tag: firefly_0821_release~6949 X-Git-Url: http://plrg.eecs.uci.edu/git/?a=commitdiff_plain;h=236516672fa900a185a8e9325111571a04180454;p=firefly-linux-kernel-4.4.55.git rk31xx:RT5025:support pmic rt5025 --- diff --git a/arch/arm/mach-rk30/board-pmu-rt5025.c b/arch/arm/mach-rk30/board-pmu-rt5025.c new file mode 100755 index 000000000000..7b48c7b0b808 --- /dev/null +++ b/arch/arm/mach-rk30/board-pmu-rt5025.c @@ -0,0 +1,458 @@ +#include +#include +#include + +#include +#include +#include + +#include + +#ifdef CONFIG_MFD_RT5025 + +static int rt5025_pre_init(struct rt5025_chip *rt5025_chip){ + + + printk("%s,line=%d\n", __func__,__LINE__); + int ret; + /**********set voltage speed***********************/ + ret = rt5025_reg_read(rt5025_chip->i2c, 0x08); + ret &= (~(3<<0)); //dcdc1 25mv/10us + rt5025_reg_write(rt5025_chip->i2c, 0x08,ret); + + ret = rt5025_reg_read(rt5025_chip->i2c, 0x09); + ret &= (~(3<<0));//dcdc2 100mv/10us + rt5025_reg_write(rt5025_chip->i2c, 0x09,ret); + + ret = rt5025_reg_read(rt5025_chip->i2c, 0x0a); + ret &= (~(3<<0));//dcdc3 50mv/12us + rt5025_reg_write(rt5025_chip->i2c, 0x0a,ret); + /************************************************/ + /***************set power off voltage***************/ + ret = rt5025_reg_read(rt5025_chip->i2c, 0x17); + ret &= (~(7<<5)); //power off 2.8v + rt5025_reg_write(rt5025_chip->i2c, 0x17,ret); + + ret = rt5025_reg_read(rt5025_chip->i2c, 0x17); + ret |= (1<<3); //enable DC4 boost + rt5025_reg_write(rt5025_chip->i2c, 0x17,ret); + /***********************************************/ + /************************************************/ + return 0; + } +static int rt5025_post_init(void) +{ + struct regulator *dcdc; + struct regulator *ldo; + int i = 0; + printk("%s,line=%d\n", __func__,__LINE__); + + #ifndef CONFIG_RK_CONFIG + g_pmic_type = PMIC_TYPE_RT5025; + #endif + printk("%s:g_pmic_type=%d\n",__func__,g_pmic_type); + + for(i = 0; i < ARRAY_SIZE(rt5025_dcdc_info); i++) + { + if(rt5025_dcdc_info[i].min_uv == 0 && rt5025_dcdc_info[i].max_uv == 0) + continue; + dcdc =regulator_get(NULL, rt5025_dcdc_info[i].name); + + regulator_set_voltage(dcdc, rt5025_dcdc_info[i].min_uv, rt5025_dcdc_info[i].max_uv); + + regulator_set_mode(dcdc, REGULATOR_MODE_NORMAL); + regulator_enable(dcdc); + printk("%s %s =%duV end\n", __func__,rt5025_dcdc_info[i].name, regulator_get_voltage(dcdc)); + regulator_put(dcdc); + udelay(100); + } + + for(i = 0; i < ARRAY_SIZE(rt5025_ldo_info); i++) + { + if(rt5025_ldo_info[i].min_uv == 0 && rt5025_ldo_info[i].max_uv == 0) + continue; + ldo =regulator_get(NULL, rt5025_ldo_info[i].name); + regulator_set_voltage(ldo, rt5025_ldo_info[i].min_uv, rt5025_ldo_info[i].max_uv); + regulator_enable(ldo); + printk("%s %s =%duV end\n", __func__,rt5025_ldo_info[i].name, regulator_get_voltage(ldo)); + regulator_put(ldo); + } + + printk("%s,line=%d END\n", __func__,__LINE__); + + return 0; +} +static struct regulator_consumer_supply rt5025_dcdc1_supply[] = { + { + .supply = "rt5025-dcdc1", + }, +// { +// .supply = "vdd_cpu", +// }, + +}; +static struct regulator_consumer_supply rt5025_dcdc2_supply[] = { + { + .supply = "rt5025-dcdc2", + }, +// { +// .supply = "vdd_core", +// }, +}; +static struct regulator_consumer_supply rt5025_dcdc3_supply[] = { + { + .supply = "rt5025-dcdc3", + }, +}; + +static struct regulator_consumer_supply rt5025_dcdc4_supply[] = { + { + .supply = "rt5025-dcdc4", + }, +}; + +static struct regulator_consumer_supply rt5025_ldo1_supply[] = { + { + .supply = "rt5025-ldo1", + }, +}; +static struct regulator_consumer_supply rt5025_ldo2_supply[] = { + { + .supply = "rt5025-ldo2", + }, +}; + +static struct regulator_consumer_supply rt5025_ldo3_supply[] = { + { + .supply = "rt5025-ldo3", + }, +}; +static struct regulator_consumer_supply rt5025_ldo4_supply[] = { + { + .supply = "rt5025-ldo4", + }, +}; +static struct regulator_consumer_supply rt5025_ldo5_supply[] = { + { + .supply = "rt5025-ldo5", + }, +}; +static struct regulator_consumer_supply rt5025_ldo6_supply[] = { + { + .supply = "rt5025-ldo6", + }, +}; + +static struct regulator_init_data rt5025_dcdc1_info = { + .constraints = { + .name = "RT5025-DCDC1", + .min_uV = 700000, + .max_uV = 2275000, + .valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_FAST, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS| REGULATOR_CHANGE_MODE, + }, + .num_consumer_supplies = ARRAY_SIZE(rt5025_dcdc1_supply), + .consumer_supplies = rt5025_dcdc1_supply, +}; + +static struct regulator_init_data rt5025_dcdc2_info = { + .constraints = { + .name = "RT5025-DCDC2", + .min_uV = 700000, + .max_uV = 3500000, + .valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_FAST, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE, + }, + .num_consumer_supplies = ARRAY_SIZE(rt5025_dcdc2_supply), + .consumer_supplies = rt5025_dcdc2_supply, +}; + +static struct regulator_init_data rt5025_dcdc3_info = { + .constraints = { + .name = "RT5025-DCDC3", + .min_uV = 700000, + .max_uV = 3500000, + .valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_FAST, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE, + }, + .num_consumer_supplies = ARRAY_SIZE(rt5025_dcdc3_supply), + .consumer_supplies = rt5025_dcdc3_supply, +}; + +static struct regulator_init_data rt5025_dcdc4_info = { + .constraints = { + .name = "RT5025-DCDC4", + .min_uV = 4500000, + .max_uV = 5500000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(rt5025_dcdc4_supply), + .consumer_supplies = rt5025_dcdc4_supply, +}; + +static struct regulator_init_data rt5025_ldo1_info = { + .constraints = { + .name = "RT5025-LDO1", + .min_uV = 700000, + .max_uV = 3500000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(rt5025_ldo1_supply), + .consumer_supplies = rt5025_ldo1_supply, +}; + +static struct regulator_init_data rt5025_ldo2_info = { + .constraints = { + .name = "RT5025-LDO2", + .min_uV = 700000, + .max_uV = 3500000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(rt5025_ldo2_supply), + .consumer_supplies = rt5025_ldo2_supply, +}; + +static struct regulator_init_data rt5025_ldo3_info = { + .constraints = { + .name = "RT5025-LDO3", + .min_uV = 1000000, + .max_uV = 3300000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(rt5025_ldo3_supply), + .consumer_supplies = rt5025_ldo3_supply, +}; + +static struct regulator_init_data rt5025_ldo4_info = { + .constraints = { + .name = "RT5025-LDO4", + .min_uV = 1000000, + .max_uV = 3300000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(rt5025_ldo4_supply), + .consumer_supplies = rt5025_ldo4_supply, +}; + +static struct regulator_init_data rt5025_ldo5_info = { + .constraints = { + .name = "RT5025-LDO5", + .min_uV = 1000000, + .max_uV = 3300000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(rt5025_ldo5_supply), + .consumer_supplies = rt5025_ldo5_supply, +}; + +static struct regulator_init_data rt5025_ldo6_info = { + .constraints = { + .name = "RT5025-LDO6", + .min_uV = 1000000, + .max_uV = 3300000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(rt5025_ldo6_supply), + .consumer_supplies = rt5025_ldo6_supply, +}; + +static struct rt5025_power_data rt5025_power_data = { + .CHGControl2 = { + .bitfield = { + .CHGBC_EN = 1, + .TE = 1, + .CCCHG_TIMEOUT = RT5025_CCCHG_TO_6H, + .PRECHG_TIMEOUT = RT5025_PRECHG_TO_30M, + }, + }, + .CHGControl3 = { + .bitfield = { + .VOREG = 0x23, + }, + }, + .CHGControl4 = { + .bitfield = { + .AICR = RT5025_AICR_500MA, + .ICC = RT5025_ICC_1P8A, + }, + }, + .CHGControl5 = { + .bitfield = { + .DPM = RT5025_DPM_DIS, + }, + }, + .CHGControl6 = { + .bitfield = { + .IPREC = RT5025_IPREC_10P, + .IEOC = RT5025_IEOC_10P, + .VPREC = RT5025_VPREC_3V, + }, + }, + .CHGControl7 = { + .bitfield = { + .CHGC_EN = 1, + .CHG_DCDC_MODE = 0, + .BATD_EN = 0, + }, + }, +}; + +static struct rt5025_gpio_data rt5025_gpio_data = { +// .gpio_base = RT5025_GPIO_BASE, + .irq_base = IRQ_BOARD_BASE, +}; + +static struct rt5025_misc_data rt5025_misc_data = { + .RSTCtrl = { + .bitfield = { + .Action = 2, + .Delayed1 = RT5025_RSTDELAY1_1S, + .Delayed2 = RT5025_RSTDELAY2_1S, + }, + }, + .VSYSCtrl = { + .bitfield = { + .VOFF = RT5025_VOFF_3P1V, + }, + }, + .PwrOnCfg = { + .bitfield = { + .PG_DLY = RT5025_PGDLY_100MS, + .SHDN_PRESS = RT5025_SHDNPRESS_6S, + .LPRESS_TIME = RT5025_LPRESS_1P5S, + .START_TIME = RT5025_STARTIME_100MS, + }, + }, + .SHDNCtrl = { + .bitfield = { + .SHDN_DLYTIME = RT5025_SHDNDLY_1S, + .SHDN_TIMING = 1, + .SHDN_CTRL = 0, + }, + }, + .PwrOffCond = { + .bitfield = { + .OT_ENSHDN = 1, + .PWRON_ENSHDN = 1, + .DCDC3LV_ENSHDN = 0, + .DCDC2LV_ENSHDN = 0, + .DCDC1LV_ENSHDN = 0, + .SYSLV_ENSHDN = 0, + }, + }, +}; + +static struct rt5025_irq_data rt5025_irq_data = { + .irq_enable1 = { + .bitfield = { + .BATABS = 1, + .INUSB_PLUGIN = 1, + .INUSBOVP = 1, + .INAC_PLUGIN = 1, + .INACOVP = 1, + }, + }, + .irq_enable2 = { + .bitfield = { + .CHTERMI = 1, + .CHBATOVI = 1, + .CHGOODI_INUSB = 0, + .CHBADI_INUSB = 1, + .CHSLPI_INUSB = 1, + .CHGOODI_INAC = 0, + .CHBADI_INAC = 1, + .CHSLPI_INAC = 1, + }, + }, + .irq_enable3 = { + .bitfield = { + .TIMEOUT_CC = 1, + .TIMEOUT_PC = 1, + .CHVSREGI = 0, + .CHTREGI = 0, + .CHRCHGI = 0, + }, + }, + .irq_enable4 = { + .bitfield = { + .SYSLV = 0, + .DCDC4LVHV = 1, + .PWRONLP = 0, + .PWRONSP = 0, + .DCDC3LV = 1, + .DCDC2LV = 1, + .DCDC1LV = 1, + .OT = 1, + }, + }, + .irq_enable5 = { + .bitfield = { + .GPIO0_IE = 0, + .GPIO1_IE = 0, + .GPIO2_IE = 0, + .RESETB = 1, + .PWRONF = 0, + .PWRONR = 0, + .KPSHDN = 1, + }, + }, +}; + +static void rt5025_charger_event_callback(uint32_t detected) +{ + RTINFO("event detected = 0x%08x\n", detected); +} + +static void rt5025_power_event_callback(uint32_t detected) +{ + RTINFO("event detected = 0x%08x\n", detected); +} + +static struct rt5025_event_callback rt5025_event_callback = { + .charger_event_callback = rt5025_charger_event_callback, + .power_event_callkback = rt5025_power_event_callback, +}; + +static struct rt5025_platform_data rt5025_data = { + .pre_init=rt5025_pre_init, + .post_init=rt5025_post_init, + .regulator = { + &rt5025_dcdc1_info, + &rt5025_dcdc2_info, + &rt5025_dcdc3_info, + &rt5025_dcdc4_info, + &rt5025_ldo1_info, + &rt5025_ldo2_info, + &rt5025_ldo3_info, + &rt5025_ldo4_info, + &rt5025_ldo5_info, + &rt5025_ldo6_info, + }, + .power_data = &rt5025_power_data, + .gpio_data = &rt5025_gpio_data, + .misc_data = &rt5025_misc_data, + .irq_data = &rt5025_irq_data, + .cb = &rt5025_event_callback, + .intr_pin = 81, //GPIO81 +}; + +void __sramfunc board_pmu_rt5025_suspend(void) +{ +} +void __sramfunc board_pmu_rt5025_resume(void) +{ +} + + +#endif + + + + diff --git a/arch/arm/mach-rk30/board-rk3168-tb.c b/arch/arm/mach-rk30/board-rk3168-tb.c index 592af755211e..677aefa727f8 100755 --- a/arch/arm/mach-rk30/board-rk3168-tb.c +++ b/arch/arm/mach-rk30/board-rk3168-tb.c @@ -49,6 +49,9 @@ #include #include #include +#ifdef CONFIG_MFD_RT5025 +#include +#endif #ifdef CONFIG_CW2015_BATTERY #include @@ -2079,6 +2082,65 @@ static struct pmu_info ricoh619_ldo_info[] = { #include "board-pmu-ricoh619.c" #endif +#ifdef CONFIG_MFD_RT5025 +#define RT5025_HOST_IRQ RK30_PIN0_PB3 + +static struct pmu_info rt5025_dcdc_info[] = { + { + .name = "rt5025-dcdc1", //arm + .min_uv = 1000000, + .max_uv = 1000000, + }, + { + .name = "rt5025-dcdc2", //logic + .min_uv = 1000000, + .max_uv = 1000000, + }, + + { + .name = "rt5025-dcdc3", //vccio + .min_uv = 3300000, + .max_uv = 3300000, + }, + +}; +static struct pmu_info rt5025_ldo_info[] = { + { + .name = "rt5025-ldo1", //vdd10 + .min_uv = 1000000, + .max_uv = 1000000, + }, + { + .name = "rt5025-ldo2", //vddjetta + .min_uv = 1200000, + .max_uv = 1200000, + }, + { + .name = "rt5025-ldo3", //vcc18 + .min_uv = 1800000, + .max_uv = 1800000, + }, + { + .name = "rt5025-ldo4", //vccjetta + .min_uv = 3300000, + .max_uv = 3300000, + }, + { + .name = "rt5025-ldo5", //vcctp + .min_uv = 3300000, + .max_uv = 3300000, + }, + { + .name = "rt5025-ldo6", //vccio_wl + .min_uv = 1800000, + .max_uv = 1800000, + }, + + }; + +#include "board-pmu-rt5025.c" +#endif + static struct i2c_board_info __initdata i2c1_info[] = { @@ -2130,6 +2192,16 @@ static struct i2c_board_info __initdata i2c1_info[] = { }, #endif +#if defined (CONFIG_MFD_RT5025) + { + .type = "RT5025", + .addr = 0x35, + .flags = 0, + .irq = RT5025_HOST_IRQ, + .platform_data=&rt5025_data, + }, +#endif + #if defined (CONFIG_RTC_HYM8563) { .type = "rtc_hym8563", @@ -2438,6 +2510,12 @@ static void rk30_pm_power_off(void) } #endif + #if defined(CONFIG_MFD_RT5025) + if(pmic_is_rt5025()){ + rt5025_power_off(); //rt5025 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 9d14b1e56a57..45bd521e5de9 100755 --- a/arch/arm/plat-rk/include/plat/board.h +++ b/arch/arm/plat-rk/include/plat/board.h @@ -91,6 +91,7 @@ enum { PMIC_TYPE_ACT8846 =3, PMIC_TYPE_RK808 =4, PMIC_TYPE_RICOH619 =5, + PMIC_TYPE_RT5025 =6, PMIC_TYPE_MAX, }; extern __sramdata int g_pmic_type; @@ -100,6 +101,7 @@ extern __sramdata int g_pmic_type; #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) +#define pmic_is_rt5025() (g_pmic_type == PMIC_TYPE_RT5025) struct pmu_info { char *name; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index fbdbde2b3efa..dadd65efd04e 100755 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -499,4 +499,10 @@ config GPIO_TPS65910 help Select this option to enable GPIO driver for the TPS65910 chip family. +config GPIO_RT5025 + bool "Richtek RT5025 GPIO support" + depends on MFD_RT5025 + default n + help + This is the gpio driver for RT5025 PMIC. endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 54e59b9e0939..55451aa37a54 100755 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -56,3 +56,4 @@ obj-$(CONFIG_GPIO_ML_IOH) += ml_ioh_gpio.o obj-$(CONFIG_AB8500_GPIO) += ab8500-gpio.o obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o +obj-$(CONFIG_GPIO_RT5025) += rt5025-gpio.o diff --git a/drivers/gpio/rt5025-gpio.c b/drivers/gpio/rt5025-gpio.c new file mode 100755 index 000000000000..e4636f959757 --- /dev/null +++ b/drivers/gpio/rt5025-gpio.c @@ -0,0 +1,234 @@ +/* + * drivers/gpio/rt5025-gpio.c + * Driver foo Richtek RT5025 PMIC GPIO + * + * Copyright (C) 2013 Richtek Electronics + * cy_huang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct rt5025_gpio_info { + struct i2c_client *i2c; + unsigned gpio_base; + unsigned irq_base; + struct gpio_chip gpio_chip; +}; + +static inline int find_rt5025_gpioreg(unsigned off, int *gpio_reg) +{ + int ret = 0; + switch (off) + { + case 0: + case 1: + case 2: + *gpio_reg = RT5025_REG_GPIO0 + off; + break; + default: + ret = -EINVAL; + } + return ret; +} + +static int rt5025_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct rt5025_gpio_info *gi = dev_get_drvdata(chip->dev); + int gpio_reg = 0; + int ret = 0; + + ret = find_rt5025_gpioreg(offset , &gpio_reg); + if (ret < 0) + { + dev_err(chip->dev, "not a valid gpio index\n"); + return ret; + } + + ret = rt5025_clr_bits(gi->i2c, gpio_reg, RT5025_GPIO_DIRMASK); + if (ret<0) + { + dev_err(chip->dev, "set gpio input fail\n"); + return ret; + } + + return 0; +} + +static int rt5025_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) +{ + struct rt5025_gpio_info *gi = dev_get_drvdata(chip->dev); + int gpio_reg = 0; + int ret = 0; + + ret = find_rt5025_gpioreg(offset, &gpio_reg); + if (ret < 0) + { + dev_err(chip->dev, "not a valid gpio index\n"); + return ret; + } + + ret = rt5025_clr_bits(gi->i2c, gpio_reg, RT5025_GPIO_DIRSHIFT); + if (ret<0) + { + dev_err(chip->dev, "clr gpio direction fail\n"); + return ret; + } + + ret = rt5025_set_bits(gi->i2c, gpio_reg, RT5025_GPIO_OUTPUT<dev, "set gpio output dir fail\n"); + return ret; + } + + if (value) + ret = rt5025_set_bits(gi->i2c, gpio_reg, RT5025_GPIO_OVALUEMASK); + else + ret = rt5025_clr_bits(gi->i2c, gpio_reg, RT5025_GPIO_OVALUEMASK); + + if (ret<0) + { + dev_err(chip->dev, "set gpio output value fail\n"); + return ret; + } + + return 0; +} + +static int rt5025_gpio_get_value(struct gpio_chip *chip, unsigned offset) +{ + struct rt5025_gpio_info *gi = dev_get_drvdata(chip->dev); + int gpio_reg = 0; + int ret = 0; + + ret = find_rt5025_gpioreg(offset, &gpio_reg); + if (ret < 0) + { + dev_err(chip->dev, "not a valid gpio index\n"); + return ret; + } + + ret = rt5025_reg_read(gi->i2c, gpio_reg); + if (ret<0) + { + dev_err(chip->dev, "read gpio register fail\n"); + return ret; + } + + return (ret&RT5025_GPIO_IVALUEMASK)?1:0; +} + +static void rt5025_gpio_set_value(struct gpio_chip *chip, unsigned offset, int value) +{ + struct rt5025_gpio_info *gi = dev_get_drvdata(chip->dev); + int gpio_reg = 0; + int ret = 0; + + ret = find_rt5025_gpioreg(offset, &gpio_reg); + if (ret < 0) + { + dev_err(chip->dev, "not a valid gpio index\n"); + return; + } + + if (value) + ret = rt5025_set_bits(gi->i2c, gpio_reg, RT5025_GPIO_OVALUEMASK); + else + ret = rt5025_clr_bits(gi->i2c, gpio_reg, RT5025_GPIO_OVALUEMASK); + + if (ret<0) + { + dev_err(chip->dev, "read gpio register fail\n"); + } +} + +static int __devinit rt5025_gpio_probe(struct platform_device *pdev) +{ + struct rt5025_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct rt5025_platform_data *pdata = chip->dev->platform_data; + struct rt5025_gpio_info *gi; + int ret = 0; + + gi = kzalloc(sizeof(*gi), GFP_KERNEL); + if (!gi) + return -ENOMEM; + + gi->i2c = chip->i2c; + gi->gpio_base = pdata->gpio_data->gpio_base; + gi->irq_base = pdata->gpio_data->irq_base; + + gi->gpio_chip.direction_input = rt5025_gpio_direction_input; + gi->gpio_chip.direction_output = rt5025_gpio_direction_output; + gi->gpio_chip.get = rt5025_gpio_get_value; + gi->gpio_chip.set = rt5025_gpio_set_value; + gi->gpio_chip.can_sleep = 0; + + gi->gpio_chip.base = gi->gpio_base; + gi->gpio_chip.ngpio = RT5025_GPIO_NR; + gi->gpio_chip.label = pdev->name; + gi->gpio_chip.dev = &pdev->dev; + gi->gpio_chip.owner = THIS_MODULE; + + ret = gpiochip_add(&gi->gpio_chip); + if (ret) + goto out_dev; + + platform_set_drvdata(pdev, gi); + return ret; +out_dev: + kfree(gi); + return ret; +} + +static int __devexit rt5025_gpio_remove(struct platform_device *pdev) +{ + int ret; + struct rt5025_gpio_info *gi = platform_get_drvdata(pdev); + + ret = gpiochip_remove(&gi->gpio_chip); + kfree(gi); + + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver rt5025_gpio_driver = +{ + .driver = { + .name = RT5025_DEVICE_NAME "-gpio", + .owner = THIS_MODULE, + }, + .probe = rt5025_gpio_probe, + .remove = __devexit_p(rt5025_gpio_remove), +}; + +static int __init rt5025_gpio_init(void) +{ + return platform_driver_register(&rt5025_gpio_driver); +} +subsys_initcall_sync(rt5025_gpio_init); + +static void __exit rt5025_gpio_exit(void) +{ + platform_driver_unregister(&rt5025_gpio_driver); +} +module_exit(rt5025_gpio_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("CY Huang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include + +#ifdef CONFIG_REGULATOR_RT5025 +#define RT5025_VR_DEVS(_id) \ +{ \ + .name = RT5025_DEVICE_NAME "-regulator", \ + .num_resources = 0, \ + .id = RT5025_ID_##_id, \ +} + +static struct mfd_cell regulator_devs[] = { + RT5025_VR_DEVS(DCDC1), + RT5025_VR_DEVS(DCDC2), + RT5025_VR_DEVS(DCDC3), + RT5025_VR_DEVS(DCDC4), + RT5025_VR_DEVS(LDO1), + RT5025_VR_DEVS(LDO2), + RT5025_VR_DEVS(LDO3), + RT5025_VR_DEVS(LDO4), + RT5025_VR_DEVS(LDO5), + RT5025_VR_DEVS(LDO6), +}; +#endif /* CONFIG_REGULATOR_RT5025 */ + +#ifdef CONFIG_POWER_RT5025 +static struct mfd_cell power_devs[] = { +{ + .name = RT5025_DEVICE_NAME "-power", + .id = -1, + .num_resources = 0, +}, +}; +#endif /* CONFIG_POWER_RT5025 */ + +#ifdef CONFIG_GPIO_RT5025 +static struct mfd_cell gpio_devs[] = { +{ + .name = RT5025_DEVICE_NAME "-gpio", + .id = -1, + .num_resources = 0, +}, +}; +#endif /* CONFIG_GPIO_RT5025 */ + +#ifdef CONFIG_MFD_RT5025_MISC +static struct mfd_cell misc_devs[] = { +{ + .name = RT5025_DEVICE_NAME "-misc", + .id = -1, + .num_resources = 0, +}, +}; +#endif /* CONFIG_MFD_RT5025_MISC */ + +#ifdef CONFIG_MFD_RT5025_IRQ +static struct mfd_cell irq_devs[] = { +{ + .name = RT5025_DEVICE_NAME "-irq", + .id = -1, + .num_resources = 0, +}, +}; +#endif /* CONFIG_MFD_RT5025_IRQ */ + +#ifdef CONFIG_MFD_RT5025_DEBUG +static struct mfd_cell debug_devs[] = { +{ + .name = RT5025_DEVICE_NAME "-debug", + .id = -1, + .num_resources = 0, +}, +}; +#endif /* CONFIG_MFD_RT5025_DEBUG */ + +int __devinit rt5025_core_init(struct rt5025_chip *chip, struct rt5025_platform_data *pdata) +{ + int ret = 0; + + RTINFO("Start to initialize all device\n"); + printk("%s,line=%d\n", __func__,__LINE__); + + #ifdef CONFIG_REGULATOR_RT5025 + if (pdata && pdata->regulator[0]) { + RTINFO("mfd add regulators dev\n"); + #if (LINUX_VERSION_CODE>=KERNEL_VERSION(3,6,0)) + ret = mfd_add_devices(chip->dev, 0, ®ulator_devs[0], + ARRAY_SIZE(regulator_devs), + NULL, 0, NULL); + #else + ret = mfd_add_devices(chip->dev, 0, ®ulator_devs[0], + ARRAY_SIZE(regulator_devs), + NULL, 0); + #endif /* LINUX_VERSION_CODE>=KERNL_VERSION(3,6,0) */ + if (ret < 0) { + dev_err(chip->dev, "Failed to add regulator subdev\n"); + goto out_dev; + } + } + #endif /* CONFIG_REGULATOR_RT5025 */ + + #ifdef CONFIG_POWER_RT5025 + if (pdata && pdata->power_data) { + RTINFO("mfd add power dev\n"); + #if (LINUX_VERSION_CODE>=KERNEL_VERSION(3,6,0)) + ret = mfd_add_devices(chip->dev, 0, &power_devs[0], + ARRAY_SIZE(power_devs), + NULL, 0,NULL); + #else + ret = mfd_add_devices(chip->dev, 0, &power_devs[0], + ARRAY_SIZE(power_devs), + NULL, 0); + #endif + if (ret < 0) { + dev_err(chip->dev, "Failed to add power supply " + "subdev\n"); + goto out_dev; + } + } + #endif /* CONFIG_MFD_RT5025 */ + + //Initialize the RT5025_GPIO + #ifdef CONFIG_GPIO_RT5025 + if (pdata && pdata->gpio_data) { + RTINFO("mfd add gpios dev\n"); + #if (LINUX_VERSION_CODE>=KERNEL_VERSION(3,6,0)) + ret = mfd_add_devices(chip->dev, 0, &gpio_devs[0], + ARRAY_SIZE(gpio_devs), + NULL, 0, NULL); + #else + ret = mfd_add_devices(chip->dev, 0, &gpio_devs[0], + ARRAY_SIZE(gpio_devs), + NULL, 0); + #endif /* LINUX_VERSION_CODE>=KERNL_VERSION(3,6,0) */ + if (ret < 0) { + dev_err(chip->dev, "Failed to add gpio subdev\n"); + goto out_dev; + } + } + #endif /* CONFIG_GPIO_RT5025 */ +\ + #ifdef CONFIG_MFD_RT5025_MISC + if (pdata && pdata->misc_data) { + RTINFO("mfd add misc dev\n"); + #if (LINUX_VERSION_CODE>=KERNEL_VERSION(3,6,0)) + ret = mfd_add_devices(chip->dev, 0, &misc_devs[0], + ARRAY_SIZE(misc_devs), + NULL, 0, NULL); + #else + ret = mfd_add_devices(chip->dev, 0, &misc_devs[0], + ARRAY_SIZE(misc_devs), + NULL, 0); + #endif /* LINUX_VERSION_CODE>=KERNL_VERSION(3,6,0) */ + if (ret < 0) { + dev_err(chip->dev, "Failed to add misc subdev\n"); + goto out_dev; + } + } + #endif /* CONFIG_MFD_RT5025_MISC */ + + #ifdef CONFIG_MFD_RT5025_IRQ + if (pdata && pdata->irq_data) { + RTINFO("mfd add irq dev\n"); + #if (LINUX_VERSION_CODE>=KERNEL_VERSION(3,6,0)) + ret = mfd_add_devices(chip->dev, 0, &irq_devs[0], + ARRAY_SIZE(irq_devs), + NULL, 0, NULL); + #else + ret = mfd_add_devices(chip->dev, 0, &irq_devs[0], + ARRAY_SIZE(irq_devs), + NULL, 0); + #endif /* LINUX_VERSION_CODE>=KERNL_VERSION(3,6,0) */ + if (ret < 0) { + dev_err(chip->dev, "Failed to add irq subdev\n"); + goto out_dev; + } + } + #endif /* CONFIG_MFD_RT5025_IRQ */ +#if 0 + #ifdef CONFIG_MFD_RT5025_DEBUG + RTINFO("mfd add debug dev\n"); + printk("%s,line=%d\n", __func__,__LINE__); + #if (LINUX_VERSION_CODE>=KERNEL_VERSION(3,6,0)) + ret = mfd_add_devices(chip->dev, 0, &debug_devs[0], + ARRAY_SIZE(debug_devs), + NULL, 0, NULL); + #else + ret = mfd_add_devices(chip->dev, 0, &debug_devs[0], + ARRAY_SIZE(debug_devs), + NULL, 0); + #endif /* LINUX_VERSION_CODE>=KERNL_VERSION(3,6,0) */ + if (ret < 0) { + dev_err(chip->dev, "Failed to add debug subdev\n"); + goto out_dev; + } + #endif /* CONFIG_MFD_RT5025_DEBUG */ +#endif + + RTINFO("Initialize all device successfully\n"); + return ret; +out_dev: + mfd_remove_devices(chip->dev); + return ret; +} +subsys_initcall_sync(rt5025_core_init); + +int __devexit rt5025_core_deinit(struct rt5025_chip *chip) +{ + mfd_remove_devices(chip->dev); + return 0; +} +EXPORT_SYMBOL(rt5025_core_deinit); diff --git a/drivers/mfd/rt5025-debug.c b/drivers/mfd/rt5025-debug.c new file mode 100755 index 000000000000..bec09d68c25f --- /dev/null +++ b/drivers/mfd/rt5025-debug.c @@ -0,0 +1,191 @@ +/* + * drivers/mfd/rt5025-debug.c + * Driver foo Richtek RT5025 PMIC Debug + * + * Copyright (C) 2013 Richtek Electronics + * cy_huang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +struct rt5025_debug_info { + struct i2c_client *i2c; +}; + +static struct i2c_client *client; +static struct dentry *debugfs_rt_dent; +static struct dentry *debugfs_peek; +static struct dentry *debugfs_poke; + +static unsigned char read_data; + +static int reg_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static int get_parameters(char *buf, long int *param1, int num_of_par) +{ + char *token; + int base, cnt; + + token = strsep(&buf, " "); + + for (cnt = 0; cnt < num_of_par; cnt++) { + if (token != NULL) { + if ((token[1] == 'x') || (token[1] == 'X')) + base = 16; + else + base = 10; + + if (strict_strtoul(token, base, ¶m1[cnt]) != 0) + return -EINVAL; + + token = strsep(&buf, " "); + } + else + return -EINVAL; + } + return 0; +} + +static ssize_t reg_debug_read(struct file *filp, char __user *ubuf, + size_t count, loff_t *ppos) +{ + char lbuf[8]; + + snprintf(lbuf, sizeof(lbuf), "0x%x\n", read_data); + return simple_read_from_buffer(ubuf, count, ppos, lbuf, strlen(lbuf)); +} + +static ssize_t reg_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char *access_str = filp->private_data; + char lbuf[32]; + int rc; + long int param[5]; + + if (cnt > sizeof(lbuf) - 1) + return -EINVAL; + + rc = copy_from_user(lbuf, ubuf, cnt); + if (rc) + return -EFAULT; + + lbuf[cnt] = '\0'; + + if (!strcmp(access_str, "poke")) { + /* write */ + rc = get_parameters(lbuf, param, 2); + if ((param[0] <= 0xFF) && (param[1] <= 0xFF) && (rc == 0)) + { + rt5025_reg_write(client, param[0], (unsigned char)param[1]); + } + else + rc = -EINVAL; + } else if (!strcmp(access_str, "peek")) { + /* read */ + rc = get_parameters(lbuf, param, 1); + if ((param[0] <= 0xFF) && (rc == 0)) + { + read_data = rt5025_reg_read(client, param[0]); + } + else + rc = -EINVAL; + } + + if (rc == 0) + rc = cnt; + + return rc; +} + +static const struct file_operations reg_debug_ops = { + .open = reg_debug_open, + .write = reg_debug_write, + .read = reg_debug_read +}; + +static int __devinit rt5025_debug_probe(struct platform_device *pdev) +{ + struct rt5025_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct rt5025_debug_info *di; + + di = kzalloc(sizeof(*di), GFP_KERNEL); + if (!di) + return -ENOMEM; + + di->i2c = chip->i2c; + + RTINFO("add debugfs for core RT5025"); + client = chip->i2c; + debugfs_rt_dent = debugfs_create_dir("rt5025_dbg", 0); + if (!IS_ERR(debugfs_rt_dent)) { + debugfs_peek = debugfs_create_file("peek", + S_IFREG | S_IRUGO, debugfs_rt_dent, + (void *) "peek", ®_debug_ops); + + debugfs_poke = debugfs_create_file("poke", + S_IFREG | S_IRUGO, debugfs_rt_dent, + (void *) "poke", ®_debug_ops); + } + + platform_set_drvdata(pdev, di); + + return 0; +} + +static int __devexit rt5025_debug_remove(struct platform_device *pdev) +{ + struct rt5025_debug_info *di = platform_get_drvdata(pdev); + + if (!IS_ERR(debugfs_rt_dent)) + debugfs_remove_recursive(debugfs_rt_dent); + + kfree(di); + return 0; +} + +static struct platform_driver rt5025_debug_driver = +{ + .driver = { + .name = RT5025_DEVICE_NAME "-debug", + .owner = THIS_MODULE, + }, + .probe = rt5025_debug_probe, + .remove = __devexit_p(rt5025_debug_remove), +}; + +static int __init rt5025_debug_init(void) +{ + return platform_driver_register(&rt5025_debug_driver); +} +subsys_initcall_sync(rt5025_debug_init); + +static void __exit rt5025_debug_exit(void) +{ + platform_driver_unregister(&rt5025_debug_driver); +} +module_exit(rt5025_debug_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("CY Huang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +static inline int rt5025_read_device(struct i2c_client *i2c, + int reg, int bytes, void *dest) +{ + int ret; + if (bytes > 1) + ret = i2c_smbus_read_i2c_block_data(i2c, reg, bytes, dest); + else { + ret = i2c_smbus_read_byte_data(i2c, reg); + if (ret < 0) + return ret; + *(unsigned char *)dest = (unsigned char)ret; + } + + return ret; +} + +int rt5025_reg_block_read(struct i2c_client *i2c, \ + int reg, int bytes, void *dest) +{ + return rt5025_read_device(i2c, reg, bytes, dest); +} +EXPORT_SYMBOL(rt5025_reg_block_read); +#if 0 +int rt5025_reg_read(struct i2c_client *i2c, int reg) +{ + struct rt5025_chip* chip = i2c_get_clientdata(i2c); + int ret; + RTINFO("I2C Read (client : 0x%x) reg = 0x%x\n", + (unsigned int)i2c,(unsigned int)reg); + mutex_lock(&chip->io_lock); + ret = i2c_smbus_read_byte_data(i2c, reg); + mutex_unlock(&chip->io_lock); + return ret; +} +EXPORT_SYMBOL(rt5025_reg_read); + +int rt5025_reg_write(struct i2c_client *i2c, int reg, unsigned char data) +{ + struct rt5025_chip* chip = i2c_get_clientdata(i2c); + int ret; + printk("%s,line=%d\n", __func__,__LINE__); + +// RTINFO("I2C Write (client : 0x%x) reg = 0x%x, data = 0x%x\n", + // (unsigned int)i2c,(unsigned int)reg,(unsigned int)data); + mutex_lock(&chip->io_lock); +printk("%s,line=%d \n", __func__,__LINE__); + + ret = i2c_smbus_write_byte_data(i2c, reg, data); + mutex_unlock(&chip->io_lock); + printk("%s,line=%d %d \n", __func__,__LINE__,ret); + + return ret; +} +EXPORT_SYMBOL(rt5025_reg_write); +#else +int rt5025_reg_read(struct i2c_client *i2c, int reg) +{ + struct i2c_adapter *adap; + struct i2c_msg msgs[2]; + int ret; + unsigned char *dest; + u8 val; + + dest = &val; + + if(!i2c) + return ret; + + adap = i2c->adapter; + + msgs[0].addr = i2c->addr; + msgs[0].buf = ® + msgs[0].flags = i2c->flags; + msgs[0].len = 1; + msgs[0].scl_rate = 200*1000; + + msgs[1].buf = dest; + msgs[1].addr = i2c->addr; + msgs[1].flags = i2c->flags | I2C_M_RD; + msgs[1].len = 1; + msgs[1].scl_rate = 200*1000; + ret = i2c_transfer(adap, msgs, 2); + +// printk("***run in %s %d msgs[1].buf = %d\n",__FUNCTION__,__LINE__,*(msgs[1].buf)); + + return val; +} +EXPORT_SYMBOL(rt5025_reg_read); + +int rt5025_reg_write(struct i2c_client *i2c, int reg, unsigned char data) +{ + + int ret=-1; + + struct i2c_adapter *adap; + struct i2c_msg msg; + char tx_buf[2]; + + if(!i2c) + return ret; + + adap = i2c->adapter; + tx_buf[0] = reg; + tx_buf[1] = data; + + msg.addr = i2c->addr; + msg.buf = &tx_buf[0]; + msg.len = 1 +1; + msg.flags = i2c->flags; + msg.scl_rate = 200*1000; + + ret = i2c_transfer(adap, &msg, 1); + return ret; +} +EXPORT_SYMBOL(rt5025_reg_write); +#endif +int rt5025_assign_bits(struct i2c_client *i2c, int reg, + unsigned char mask, unsigned char data) +{ + struct rt5025_chip *chip = i2c_get_clientdata(i2c); + unsigned char value; + int ret; + mutex_lock(&chip->io_lock); + + ret = rt5025_read_device(i2c, reg, 1, &value); + + if (ret < 0) + goto out; + value &= ~mask; + value |= (data&mask); + ret = rt5025_reg_write(i2c,reg,value); +out: + mutex_unlock(&chip->io_lock); + return ret; +} +EXPORT_SYMBOL(rt5025_assign_bits); + +int rt5025_set_bits(struct i2c_client *i2c, int reg, + unsigned char mask) +{ + return rt5025_assign_bits(i2c,reg,mask,mask); +} +EXPORT_SYMBOL(rt5025_set_bits); + +int rt5025_clr_bits(struct i2c_client *i2c, int reg, + unsigned char mask) +{ + return rt5025_assign_bits(i2c,reg,mask,0); +} +EXPORT_SYMBOL(rt5025_clr_bits); + +static int __devinit rt5025_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct rt5025_platform_data *pdata = client->dev.platform_data; + struct rt5025_chip *chip; + int ret = 0; + u8 val; + + if (!pdata) + return -EINVAL; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->irq = client->irq; + chip->i2c = client; + chip->dev = &client->dev; + +#if 0 + if (pdata->event_callback) + { + chip->event_callback = kzalloc(sizeof(struct rt5025_event_callback), GFP_KERNEL); + memcpy(chip->event_callback, pdata->event_callback, sizeof(struct rt5025_event_callback)); + } +#endif /* #if 0 */ + + i2c_set_clientdata(client, chip); + mutex_init(&chip->io_lock); + + rt5025_read_device(client,0x00,1,&val); + if (val != 0x81){ + printk("The PMIC is not RT5025\n"); + return 0; + } + ret = rt5025_core_init(chip, pdata); + if (ret < 0) + dev_err(chip->dev, "rt5025_core_init_fail\n"); + else + pr_info("RT5025 Initialize successfully\n"); + + if (pdata && pdata->pre_init) { + ret = pdata->pre_init(chip); + if (ret != 0) { + dev_err(chip->dev, "pre_init() failed: %d\n", ret); + } + } + + if (pdata && pdata->post_init) { + ret = pdata->post_init(); + if (ret != 0) { + dev_err(chip->dev, "post_init() failed: %d\n", ret); + } + } + + return ret; + +} + +static int __devexit rt5025_i2c_remove(struct i2c_client *client) +{ + struct rt5025_chip *chip = i2c_get_clientdata(client); + rt5025_core_deinit(chip); + #if 0 + if (chip->event_callback) + kfree(chip->event_callback);a + #endif + kfree(chip); + return 0; +} + +static int rt5025_i2c_suspend(struct i2c_client *client, pm_message_t mesg) +{ + struct rt5025_chip *chip = i2c_get_clientdata(client); + chip->suspend = 1; + return 0; +} + +static int rt5025_i2c_resume(struct i2c_client *client) +{ + struct rt5025_chip *chip = i2c_get_clientdata(client); + chip->suspend = 0; + return 0; +} + +static const struct i2c_device_id rt5025_id_table[] = { + { RT5025_DEVICE_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, rt5025_id_table); + +static struct i2c_driver rt5025_driver = { + .driver = { + .name = RT5025_DEVICE_NAME, + .owner = THIS_MODULE, + }, + .probe = rt5025_i2c_probe, + .remove = __devexit_p(rt5025_i2c_remove), + .suspend = rt5025_i2c_suspend, + .resume = rt5025_i2c_resume, + .id_table = rt5025_id_table, +}; + +static int __init rt5025_i2c_init(void) +{ + int ret; + + ret = i2c_add_driver(&rt5025_driver); + if (ret != 0) + pr_err("Failed to register RT5025 I2C driver: %d\n", ret); + return ret; +} +subsys_initcall_sync(rt5025_i2c_init); + +static void __exit rt5025_i2c_exit(void) +{ + i2c_del_driver(&rt5025_driver); +} +module_exit(rt5025_i2c_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("I2C Driver for Richtek RT5025"); +MODULE_AUTHOR("CY Huang "); diff --git a/drivers/mfd/rt5025-irq.c b/drivers/mfd/rt5025-irq.c new file mode 100755 index 000000000000..82535e9f2b01 --- /dev/null +++ b/drivers/mfd/rt5025-irq.c @@ -0,0 +1,208 @@ +/* + * drivers/mfd/rt5025-irq.c + * Driver foo Richtek RT5025 PMIC irq + * + * Copyright (C) 2013 Richtek Electronics + * cy_huang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +struct rt5025_irq_info { + struct i2c_client *i2c; + struct device *dev; + struct rt5025_chip *chip; + struct workqueue_struct *wq; + struct rt5025_event_callback *event_cb; + struct delayed_work delayed_work; + int intr_pin; + int irq; +}; + +static void rt5025_work_func(struct work_struct *work) +{ + struct delayed_work *delayed_work = (struct delayed_work *)container_of(work, struct delayed_work, work); + struct rt5025_irq_info *ii = (struct rt5025_irq_info *)container_of(delayed_work, struct rt5025_irq_info, delayed_work); + unsigned char irq_stat[10] = {0}; + uint32_t chg_event = 0, pwr_event = 0; + + if (rt5025_reg_block_read(ii->i2c, RT5025_REG_IRQEN1, 10, irq_stat) >= 0) + { + RTINFO("irq1->%02x, irq2->%02x, irq3->%02x\n", irq_stat[1], irq_stat[3], irq_stat[5]); + RTINFO("irq4->%02x, irq5->%02x\n", irq_stat[7], irq_stat[9]); + RTINFO("stat value = %02x\n", rt5025_reg_read(ii->i2c, RT5025_REG_CHGSTAT)); + + chg_event = irq_stat[1]<<16 | irq_stat[3]<<8 | irq_stat[5]; + pwr_event = irq_stat[7]<<8 | irq_stat[9]; + #ifdef CONFIG_POWER_RT5025 + if (chg_event & CHARGER_DETECT_MASK) + rt5025_power_charge_detect(ii->chip->power_info); + #endif /* CONFIG_POWER_RT5025 */ + if (ii->event_cb) + { + if (chg_event) + ii->event_cb->charger_event_callback(chg_event); + if (pwr_event) + ii->event_cb->power_event_callkback(pwr_event); + } + } + else + dev_err(ii->dev, "read irq stat io fail\n"); + + #ifdef CONFIG_POWER_RT5025 + rt5025_power_passirq_to_gauge(ii->chip->power_info); + #endif /* CONFIG_POWER_RT5025 */ + + enable_irq(ii->irq); +} + +static irqreturn_t rt5025_interrupt(int irqno, void *param) +{ + struct rt5025_irq_info *ii = (struct rt5025_irq_info *)param; + + disable_irq_nosync(ii->irq); + queue_delayed_work(ii->wq, &ii->delayed_work, 0); + return IRQ_HANDLED; +} + +static int __devinit rt5025_interrupt_init(struct rt5025_irq_info* ii) +{ + int ret; + + RTINFO("\n"); + ii->wq = create_workqueue("rt5025_wq"); + INIT_DELAYED_WORK(&ii->delayed_work, rt5025_work_func); + + if (gpio_is_valid(ii->intr_pin)) + { + ret = gpio_request(ii->intr_pin, "rt5025_interrupt"); + if (ret) + return ret; + + ret = gpio_direction_input(ii->intr_pin); + if (ret) + return ret; + + if (request_irq(ii->irq, rt5025_interrupt, IRQ_TYPE_EDGE_FALLING|IRQF_DISABLED, "RT5025_IRQ", ii)) + { + dev_err(ii->dev, "couldn't allocate IRQ_NO(%d) !\n", ii->irq); + return -EINVAL; + } + enable_irq_wake(ii->irq); + + if (!gpio_get_value(ii->intr_pin)) + { + disable_irq_nosync(ii->irq); + queue_delayed_work(ii->wq, &ii->delayed_work, 0); + } + } + else + return -EINVAL; + + return 0; +} + +static void __devexit rt5025_interrupt_deinit(struct rt5025_irq_info* ii) +{ + if (ii->irq) + free_irq(ii->irq, ii); + + if (ii->wq) + { + cancel_delayed_work_sync(&ii->delayed_work); + flush_workqueue(ii->wq); + destroy_workqueue(ii->wq); + } +} + +static int __devinit rt5025_irq_reg_init(struct rt5025_irq_info* ii, struct rt5025_irq_data* irq_data) +{ + RTINFO("\n"); + // will just enable the interrupt that we want + rt5025_reg_write(ii->i2c, RT5025_REG_IRQEN1, irq_data->irq_enable1.val); + rt5025_reg_write(ii->i2c, RT5025_REG_IRQEN2, irq_data->irq_enable2.val); + rt5025_reg_write(ii->i2c, RT5025_REG_IRQEN3, irq_data->irq_enable3.val); + rt5025_reg_write(ii->i2c, RT5025_REG_IRQEN4, irq_data->irq_enable4.val); + rt5025_reg_write(ii->i2c, RT5025_REG_IRQEN5, irq_data->irq_enable5.val); + return 0; +} + +static int __devinit rt5025_irq_probe(struct platform_device *pdev) +{ + struct rt5025_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct rt5025_platform_data *pdata = chip->dev->platform_data; + struct rt5025_irq_info *ii; + printk("%s,line=%d\n", __func__,__LINE__); + + RTINFO("\n"); + ii = kzalloc(sizeof(*ii), GFP_KERNEL); + if (!ii) + return -ENOMEM; + + ii->i2c = chip->i2c; + ii->dev = &pdev->dev; + ii->chip = chip; + ii->intr_pin = pdata->intr_pin; + ii->irq = gpio_to_irq(pdata->intr_pin); + if (pdata->cb) + ii->event_cb = pdata->cb; + + rt5025_irq_reg_init(ii, pdata->irq_data); + rt5025_interrupt_init(ii); + + platform_set_drvdata(pdev, ii); + return 0; +} + +static int __devexit rt5025_irq_remove(struct platform_device *pdev) +{ + struct rt5025_irq_info *ii = platform_get_drvdata(pdev); + + rt5025_interrupt_deinit(ii); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver rt5025_irq_driver = +{ + .driver = { + .name = RT5025_DEVICE_NAME "-irq", + .owner = THIS_MODULE, + }, + .probe = rt5025_irq_probe, + .remove = __devexit_p(rt5025_irq_remove), +}; + +static int __init rt5025_irq_init(void) +{ + return platform_driver_register(&rt5025_irq_driver); +} +subsys_initcall_sync(rt5025_irq_init); + +static void __exit rt5025_irq_exit(void) +{ + platform_driver_unregister(&rt5025_irq_driver); +} +module_exit(rt5025_irq_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("CY Huang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +struct rt5025_misc_info { + struct i2c_client *i2c; +}; + +static struct i2c_client *g_shdn; +void rt5025_power_off(void) +{ + rt5025_set_bits(g_shdn, RT5025_SHDNCTRL_REG, RT5025_SHDNCTRL_MASK); +} +EXPORT_SYMBOL(rt5025_power_off); + +static int __devinit rt5025_misc_reg_init(struct i2c_client *client, struct rt5025_misc_data *md) +{ + int ret = 0; + + rt5025_reg_write(client, RT5025_RESETCTRL_REG, md->RSTCtrl.val); + rt5025_assign_bits(client, RT5025_VSYSULVO_REG, RT5025_VSYSOFF_MASK, md->VSYSCtrl.val); + rt5025_reg_write(client, RT5025_PWRONCTRL_REG, md->PwrOnCfg.val); + rt5025_reg_write(client, RT5025_SHDNCTRL_REG, md->SHDNCtrl.val); + rt5025_reg_write(client, RT5025_PWROFFEN_REG, md->PwrOffCond.val); + + return ret; +} + +static int __devinit rt5025_misc_probe(struct platform_device *pdev) +{ + struct rt5025_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct rt5025_platform_data *pdata = chip->dev->platform_data; + struct rt5025_misc_info *mi; + printk("%s,line=%d\n", __func__,__LINE__); + + mi = kzalloc(sizeof(*mi), GFP_KERNEL); + if (!mi) + return -ENOMEM; + + mi->i2c = chip->i2c; + rt5025_misc_reg_init(mi->i2c, pdata->misc_data); + + //for shutdown control + g_shdn = chip->i2c; + + platform_set_drvdata(pdev, mi); + return 0; +} + +static int __devexit rt5025_misc_remove(struct platform_device *pdev) +{ + struct rt5025_misc_info *mi = platform_get_drvdata(pdev); + + kfree(mi); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver rt5025_misc_driver = +{ + .driver = { + .name = RT5025_DEVICE_NAME "-misc", + .owner = THIS_MODULE, + }, + .probe = rt5025_misc_probe, + .remove = __devexit_p(rt5025_misc_remove), +}; + +static int __init rt5025_misc_init(void) +{ + return platform_driver_register(&rt5025_misc_driver); +} +subsys_initcall_sync(rt5025_misc_init); + +static void __exit rt5025_misc_exit(void) +{ + platform_driver_unregister(&rt5025_misc_driver); +} +module_exit(rt5025_misc_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("CY Huang +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RT5025_REG_IRQ_CTL 0x50 +#define RT5025_REG_IRQ_FLAG 0x51 +#define RT5025_REG_VALRT_MAXTH 0x53 +#define RT5025_REG_VALRT_MIN1TH 0x54 +#define RT5025_REG_VALRT_MIN2TH 0x55 +#define RT5025_REG_TALRT_MAXTH 0x56 +#define RT5025_REG_TALRT_MINTH 0x57 +#define RT5025_REG_VCELL_MSB 0x58 +#define RT5025_REG_VCELL_LSB 0x59 +#define RT5025_REG_INT_TEMPERATUE_MSB 0x5B +#define RT5025_REG_INT_TEMPERATUE_LSB 0x5C +#define RT5025_REG_EXT_TEMPERATUE_MSB 0x5E +#define RT5025_REG_EXT_TEMPERATUE_LSB 0x5F +#define RT5025_REG_TIMER 0x60 +#define RT5025_REG_CHANNEL_MSB 0x62 +#define RT5025_REG_CHANNEL_LSB 0x63 +#define RT5025_REG_CURRENT_MSB 0x76 +#define RT5025_REG_CURRENT_LSB 0x77 +#define RT5025_REG_QCHGH_MSB 0x78 +#define RT5025_REG_QCHGH_LSB 0x79 +#define RT5025_REG_QCHGL_MSB 0x7A +#define RT5025_REG_QCHGL_LSB 0x7B +#define RT5025_REG_QDCHGH_MSB 0x7C +#define RT5025_REG_QDCHGH_LSB 0x7D +#define RT5025_REG_QDCHGL_MSB 0x7E +#define RT5025_REG_QDCHGL_LSB 0x7F + +#define IRQ_CTL_BIT_TMX (1 << 5) +#define IRQ_CTL_BIT_TMN (1 << 4) +#define IRQ_CTL_BIT_VMX (1 << 2) +#define IRQ_CTL_BIT_VMN1 (1 << 1) +#define IRQ_CTL_BIT_VMN2 (1 << 0) + +#define IRQ_FLG_BIT_TMX (1 << 5) +#define IRQ_FLG_BIT_TMN (1 << 4) +#define IRQ_FLG_BIT_VMX (1 << 2) +#define IRQ_FLG_BIT_VMN1 (1 << 1) +#define IRQ_FLG_BIT_VMN2 (1 << 0) + +#define CHANNEL_H_BIT_CLRQDCHG (1 << 7) +#define CHANNEL_H_BIT_CLRQCHG (1 << 6) + +#define CHANNEL_L_BIT_CADC_EN (1 << 7) +#define CHANNEL_L_BIT_INTEMPCH (1 << 6) +#define CHANNEL_L_BIT_AINCH (1 << 2) +#define CHANNEL_L_BIT_VBATSCH (1 << 1) +#define CHANNEL_L_BIT_VADC_EN (1 << 0) + +#define NORMAL_POLL 10 /* 10 sec */ +#define SUSPEND_POLL (30*60) /* 30 min */ + +#define HIGH_TEMP_THRES 650 +#define HIGH_TEMP_RECOVER 430 +#define LOW_TEMP_THRES (-30) +#define LOW_TEMP_RECOVER 0 +#define TEMP_ABNORMAL_COUNT 3 + +#define TALRTMAX_VALUE 0x38 //65.39'C 0x9 +#define TALRTMIN_VALUE 0x11 //-18.75'C 0x17 +#define VALRTMAX_VALUE 0xDC //4297mV +#define VALRTMIN1_VALUE 0xB8 //3600mV +#define VALRTMIN2_VALUE 0x99 //3000mV +#define TRLS_VALUE 55 //5'C +#define VRLS_VALUE 100 //100mV + +#define IRQ_THRES_UNIT 1953 + +struct rt5025_gauge_chip { + struct i2c_client *client; + struct rt5025_power_info *info; + struct rt5025_gauge_callbacks cb; + + struct power_supply battery; + + struct delayed_work monitor_work; + struct wake_lock monitor_wake_lock; + struct alarm wakeup_alarm; + + bool suspend_poll; + ktime_t last_poll; + + /* battery voltage */ + u16 vcell; + /* battery current */ + s16 curr; + /* battery current offset */ + u8 curr_offset; + /* AIN voltage */ + u16 ain_volt; + /* battery external temperature */ + s16 ext_temp; + /* charge coulomb counter */ + u32 chg_cc; + u32 chg_cc_unuse; + /* discharge coulomb counter */ + u32 dchg_cc; + u32 dchg_cc_unuse; + /* battery capacity */ + u8 soc; + u16 time_interval; + u16 pre_gauge_timer; + + u8 online; + u8 status; + u8 health; + + /* IRQ flag */ + u8 irq_flag; + + /* max voltage IRQ flag */ + bool max_volt_irq; + /* min voltage1 IRQ flag */ + bool min_volt1_irq; + /* min voltage2 IRQ flag */ + bool min_volt2_irq; + /* max temperature IRQ flag */ + bool max_temp_irq; + /* min temperature IRQ flag */ + bool min_temp_irq; + + u8 temp_high_cnt; + u8 temp_low_cnt; + u8 temp_recover_cnt; +}; + +struct rt5025_gauge_chip *chip; +u8 irq_thres[LAST_TYPE]; + +void rt5025_set_status(int status) +{ + chip->status = status; +} + +static int rt5025_read_reg(struct i2c_client *client, + u8 reg, u8 *data, u8 len) +{ + struct i2c_adapter *adap = client->adapter; + struct i2c_msg msgs[2]; + int ret; + + msgs[0].addr = client->addr; + msgs[0].flags = client->flags; + msgs[0].len = 1; + msgs[0].buf = ® + msgs[0].scl_rate = 200*1000; + + msgs[1].addr = client->addr; + msgs[1].flags = client->flags | I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = data; + msgs[1].scl_rate = 200*1000; + + ret = i2c_transfer(adap, msgs, 2); + + return (ret == 2)? len : ret; +} + +static int rt5025_write_reg(struct i2c_client *client, + u8 reg, u8 *data, u8 len) +{ + struct i2c_adapter *adap = client->adapter; + struct i2c_msg msg; + int ret; + char *tx_buf = (char *)kmalloc(len + 1, GFP_KERNEL); + + if(!tx_buf) + return -ENOMEM; + tx_buf[0] = reg; + memcpy(tx_buf+1, data, len); + + msg.addr = client->addr; + msg.flags = client->flags; + msg.len = len + 1; + msg.buf = (char *)tx_buf; + msg.scl_rate = 200*1000; + ret = i2c_transfer(adap, &msg, 1); + kfree(tx_buf); + return (ret == 1) ? len : ret; +} + +static void rt5025_gauge_alarm(struct alarm *alarm) +{ + wake_lock(&chip->monitor_wake_lock); + schedule_delayed_work(&chip->monitor_work, 0); +} + +static void rt5025_program_alarm(int seconds) +{ + ktime_t low_interval = ktime_set(seconds, 0); + ktime_t slack = ktime_set(20, 0); + ktime_t next; + + next = ktime_add(chip->last_poll, low_interval); + alarm_start_range(&chip->wakeup_alarm, next, ktime_add(next, slack)); +} + +static int rt5025_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = chip->status; + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = chip->health; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = chip->online; + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = chip->ext_temp; + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = chip->vcell * 1000; //uV + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = chip->curr * 1000; //uA + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = chip->soc; + if (val->intval > 100) + val->intval = 100; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + default: + return -EINVAL; + } + return 0; +} + +static void rt5025_get_vcell(struct i2c_client *client) +{ + u8 data[2]; + + if (rt5025_read_reg(client, RT5025_REG_VCELL_MSB, data, 2) < 0){ + printk(KERN_ERR "%s: Failed to read Voltage\n", __func__); + } + + chip->vcell = ((data[0] << 8) + data[1]) * 61 / 100; + chip->curr_offset = (15444 * chip->vcell - 27444000) / 10000; + + RTINFO("[RT5025] vcell: %d, offset: %d\n", chip->vcell, chip->curr_offset); +} + +static void rt5025_get_current(struct i2c_client *client) +{ + u8 data[2]; + u16 temp; + int sign = 0; + + if (rt5025_read_reg(client, RT5025_REG_CURRENT_MSB, data, 2) < 0) { + printk(KERN_ERR "%s: Failed to read CURRENT\n", __func__); + } + + temp = (data[0]<<8) | data[1]; + if (data[0] & (1 << 7)) { + sign = 1; + temp = (((temp & 0x7FFF) * 3125) / 10 + chip->curr_offset) / 1000; + }else + temp = ((temp * 3125) / 10 - chip->curr_offset) / 1000; + + if (sign) + temp *= -1; + + chip->curr = temp; + RTINFO("[RT5025] current: %d\n", chip->curr); +} + +static void rt5025_get_external_temp(struct i2c_client *client) +{ + u8 data[2]; + long int temp; + + if (rt5025_read_reg(client, RT5025_REG_EXT_TEMPERATUE_MSB, data, 2) < 0) { + printk(KERN_ERR "%s: Failed to read TEMPERATURE\n", __func__); + } + chip->ain_volt = (data[0] * 256 + data[1]) * 61 / 100; + /// Check battery present + if (chip->ain_volt < 1150) + chip->online = true; + else + chip->online = false; + temp = (chip->ain_volt * (-91738) + 81521000) / 100000; + chip->ext_temp = (int)temp; + + if (chip->ext_temp >= HIGH_TEMP_THRES) { + if (chip->health != POWER_SUPPLY_HEALTH_OVERHEAT) + chip->temp_high_cnt++; + } else if (chip->ext_temp <= HIGH_TEMP_RECOVER && chip->ext_temp >= LOW_TEMP_RECOVER) { + if (chip->health == POWER_SUPPLY_HEALTH_OVERHEAT || + chip->health == POWER_SUPPLY_HEALTH_COLD) + chip->temp_recover_cnt++; + } else if (chip->ext_temp <= LOW_TEMP_THRES) { + if (chip->health != POWER_SUPPLY_HEALTH_COLD) + chip->temp_low_cnt++; + } else { + chip->temp_high_cnt = 0; + chip->temp_low_cnt = 0; + chip->temp_recover_cnt = 0; + } + + if (chip->temp_high_cnt >= TEMP_ABNORMAL_COUNT) { + chip->health = POWER_SUPPLY_HEALTH_OVERHEAT; + chip->temp_high_cnt = 0; + } else if (chip->temp_low_cnt >= TEMP_ABNORMAL_COUNT) { + chip->health = POWER_SUPPLY_HEALTH_COLD; + chip->temp_low_cnt = 0; + } else if (chip->temp_recover_cnt >= TEMP_ABNORMAL_COUNT) { + chip->health = POWER_SUPPLY_HEALTH_GOOD; + chip->temp_recover_cnt = 0; + } + RTINFO("[RT5025] external temperature: %d\n", chip->ext_temp); +} + +static void rt5025_clear_cc(operation_mode mode) +{ + u8 data[2]; + + if (rt5025_read_reg(chip->client, RT5025_REG_CHANNEL_MSB, data, 2) < 0){ + printk(KERN_ERR "%s: failed to read channel\n", __func__); + } + + if (mode == CHG) + data[0] = data[0] | CHANNEL_H_BIT_CLRQCHG; + else + data[0] = data[0] | CHANNEL_H_BIT_CLRQDCHG; + + if (rt5025_write_reg(chip->client, RT5025_REG_CHANNEL_MSB, data, 2) < 0){ + printk(KERN_ERR "%s: failed to write channel\n", __func__); + } +} + +static void rt5025_get_chg_cc(struct i2c_client *client) +{ + u8 data[4]; + u32 qh_old,ql_old,qh_new,ql_new; + u32 cc_masec,offset; + + if (rt5025_read_reg(client, RT5025_REG_QCHGH_MSB, data, 4) < 0){ + printk(KERN_ERR "%s: Failed to read QCHG\n", __func__); + } + qh_old = (data[0]<<8) + data[1]; + ql_old = (data[2]<<8) + data[3]; + + if (rt5025_read_reg(client, RT5025_REG_QCHGH_MSB, data, 4) < 0){ + printk(KERN_ERR "%s: Failed to read QCHG\n", __func__); + } + qh_new = (data[0]<<8) + data[1]; + ql_new = (data[2]<<8) + data[3]; + + if (qh_new > qh_old){ + cc_masec = (((qh_new<<16) + ql_new) * 50134) / 10; + }else if (qh_new == qh_old){ + if (ql_new >= ql_old){ + cc_masec = (((qh_new<<16) + ql_new) * 50134) / 10; + }else { + cc_masec = (((qh_old<<16) + ql_old) * 50134) / 10; + } + } + + offset = chip->curr_offset * chip->time_interval; + + if (cc_masec != 0){ + cc_masec = (cc_masec - offset) / 1000; + } + + RTINFO("[RT5025] chg_cc_mAsec: %d\n", cc_masec); + + chip->chg_cc = (cc_masec + chip->chg_cc_unuse) / 3600; + chip->chg_cc_unuse = (cc_masec + chip->chg_cc_unuse) % 3600; + RTINFO("[RT5025] chg_cc_mAH: %d\n", chip->chg_cc); + rt5025_clear_cc(CHG); +} + +static void rt5025_get_dchg_cc(struct i2c_client *client) +{ + u8 data[4]; + u32 qh_old,ql_old,qh_new,ql_new; + u32 cc_masec,offset; + + if (rt5025_read_reg(client, RT5025_REG_QDCHGH_MSB, data, 4) < 0){ + printk(KERN_ERR "%s: Failed to read QDCHG\n", __func__); + } + qh_old = (data[0]<<8) + data[1]; + ql_old = (data[2]<<8) + data[3]; + + if (rt5025_read_reg(client, RT5025_REG_QDCHGH_MSB, data, 4) < 0){ + printk(KERN_ERR "%s: Failed to read QDCHG\n", __func__); + } + qh_new = (data[0]<<8) + data[1]; + ql_new = (data[2]<<8) + data[3]; + + if (qh_new > qh_old){ + cc_masec = (((qh_new<<16) + ql_new) * 50134) / 10; + }else if (qh_new == qh_old){ + if (ql_new >= ql_old){ + cc_masec = (((qh_new<<16) + ql_new) * 50134) / 10; + }else { + cc_masec = (((qh_old<<16) + ql_old) * 50134) / 10; + } + } + + offset = chip->curr_offset * chip->time_interval; + + if (cc_masec != 0){ + cc_masec = (cc_masec - offset) / 1000; + } + + RTINFO("[RT5025] dchg_cc_mAsec: %d\n", cc_masec); + + chip->dchg_cc = (cc_masec + chip->dchg_cc_unuse) / 3600; + chip->dchg_cc_unuse = (cc_masec + chip->dchg_cc_unuse) % 3600; + RTINFO("[RT5025] dchg_cc_mAH: %d\n", chip->dchg_cc); + rt5025_clear_cc(DCHG); +} + +static void rt5025_get_irq_flag(struct i2c_client *client) +{ + u8 data[1]; + + if (rt5025_read_reg(client, RT5025_REG_IRQ_FLAG, data, 1) < 0){ + printk(KERN_ERR "%s: Failed to read irq_flag\n", __func__); + } + + chip->irq_flag = data[0]; + RTINFO("[RT5025] IRQ_FLG 0x%x\n", chip->irq_flag); +} + +static void rt5025_get_timer(struct i2c_client *client) +{ + u8 data[2]; + u16 gauge_timer; + + if (rt5025_read_reg(client, RT5025_REG_TIMER, data, 2) < 0){ + printk(KERN_ERR "%s: Failed to read Timer\n", __func__); + } + + gauge_timer = (data[0] << 8) + data[1]; + if (gauge_timer > chip->pre_gauge_timer) + chip->time_interval = gauge_timer - chip->pre_gauge_timer; + else + chip->time_interval = 65536 - chip->pre_gauge_timer + gauge_timer; + + chip->pre_gauge_timer = gauge_timer; + RTINFO("[RT5025] timer %d , interval %d\n", gauge_timer,chip->time_interval); +} + +static void rt5025_get_soc(struct i2c_client *client) +{ + chip->soc = 50; +} + +static void rt5025_channel_cc(bool enable) +{ + u8 data[1]; + + if (rt5025_read_reg(chip->client, RT5025_REG_CHANNEL_LSB, data, 1) < 0){ + printk(KERN_ERR "%s: failed to read channel\n", __func__); + } + + if (enable){ + data[0] = data[0] | 0x80; + }else { + data[0] = data[0] & 0x7F; + } + + if (rt5025_write_reg(chip->client, RT5025_REG_CHANNEL_LSB, data, 1) < 0){ + printk(KERN_ERR "%s: failed to write channel\n", __func__); + } +} + +static void rt5025_register_init(struct i2c_client *client) +{ + u8 data[1]; + + /* enable the channel of current,qc,ain,vbat and vadc */ + if (rt5025_read_reg(client, RT5025_REG_CHANNEL_LSB, data, 1) < 0){ + printk("%s: failed to read channel\n", __func__); + } + data[0] = data[0] | + CHANNEL_L_BIT_CADC_EN | + CHANNEL_L_BIT_AINCH | + CHANNEL_L_BIT_VBATSCH | + CHANNEL_L_BIT_VADC_EN; + if (rt5025_write_reg(client, RT5025_REG_CHANNEL_LSB, data, 1) < 0){ + printk("%s: failed to write channel\n", __func__); + } + /* set the alert threshold value */ + irq_thres[MAXTEMP] = TALRTMAX_VALUE; + irq_thres[MINTEMP] = TALRTMIN_VALUE; + irq_thres[MAXVOLT] = VALRTMAX_VALUE; + irq_thres[MINVOLT1] = VALRTMIN1_VALUE; + irq_thres[MINVOLT2] = VALRTMIN2_VALUE; + irq_thres[TEMP_RLS] = TRLS_VALUE; + irq_thres[VOLT_RLS] = VRLS_VALUE; + + chip->chg_cc_unuse = 0; + chip->dchg_cc_unuse = 0; + chip->pre_gauge_timer = 0; + chip->online = 1; + chip->status = POWER_SUPPLY_STATUS_DISCHARGING; + chip->health = POWER_SUPPLY_HEALTH_GOOD; + RTINFO("[RT5025] register initialized\n"); +} + +static void rt5025_alert_setting(alert_type type, bool enable) +{ + u8 data[1]; + + if (rt5025_read_reg(chip->client, RT5025_REG_IRQ_CTL, data, 1) < 0){ + printk(KERN_ERR "%s: Failed to read CONFIG\n", __func__); + } + + if(enable){ + switch(type){ + case MAXTEMP: + data[0] |= IRQ_CTL_BIT_TMX; //Enable max temperature alert + chip->max_temp_irq = true; + RTINFO("Enable min temperature alert"); + break; + case MINTEMP: + data[0] |= IRQ_CTL_BIT_TMN; //Enable min temperature alert + chip->min_temp_irq = true; + RTINFO("Enable max temperature alert"); + break; + case MAXVOLT: + data[0] |= IRQ_CTL_BIT_VMX; //Enable max voltage alert + chip->max_volt_irq = true; + RTINFO("Enable max voltage alert"); + break; + case MINVOLT1: + data[0] |= IRQ_CTL_BIT_VMN1; //Enable min1 voltage alert + chip->min_volt1_irq = true; + RTINFO("Enable min1 voltage alert"); + break; + case MINVOLT2: + data[0] |= IRQ_CTL_BIT_VMN2; //Enable min2 voltage alert + chip->min_volt2_irq = true; + RTINFO("Enable min2 voltage alert"); + break; + default: + break; + } + }else{ + switch(type){ + case MAXTEMP: + data[0] = data[0] &~ IRQ_CTL_BIT_TMX; //Disable max temperature alert + chip->max_temp_irq = false; + RTINFO("Disable min temperature alert"); + break; + case MINTEMP: + data[0] = data[0] &~ IRQ_CTL_BIT_TMN; //Disable min temperature alert + chip->min_temp_irq = false; + RTINFO("Disable max temperature alert"); + break; + case MAXVOLT: + data[0] = data[0] &~ IRQ_CTL_BIT_VMX; //Disable max voltage alert + chip->max_volt_irq = false; + RTINFO("Disable max voltage alert"); + break; + case MINVOLT1: + data[0] = data[0] &~ IRQ_CTL_BIT_VMN1; //Disable min1 voltage alert + chip->min_volt1_irq = false; + RTINFO("Disable min1 voltage alert"); + break; + case MINVOLT2: + data[0] = data[0] &~ IRQ_CTL_BIT_VMN2; //Disable min2 voltage alert + chip->min_volt2_irq = false; + RTINFO("Disable min2 voltage alert"); + break; + default: + break; + } + } + if (rt5025_write_reg(chip->client, RT5025_REG_IRQ_CTL, data, 1) < 0) + printk(KERN_ERR "%s: failed to write IRQ control\n", __func__); +} +static void rt5025_alert_threshold_init(struct i2c_client *client) +{ + u8 data[1]; + + /* TALRT MAX threshold setting */ + data[0] = irq_thres[MAXTEMP]; + if (rt5025_write_reg(client, RT5025_REG_TALRT_MAXTH, data, 1) < 0) + printk(KERN_ERR "%s: failed to write TALRT MAX threshold\n", __func__); + /* TALRT MIN threshold setting */ + data[0] = irq_thres[MINTEMP]; + if (rt5025_write_reg(client, RT5025_REG_TALRT_MINTH, data, 1) < 0) + printk(KERN_ERR "%s: failed to write TALRT MIN threshold\n", __func__); + /* VALRT MAX threshold setting */ + data[0] = irq_thres[MAXVOLT]; + if (rt5025_write_reg(client, RT5025_REG_VALRT_MAXTH, data, 1) < 0) + printk(KERN_ERR "%s: failed to write VALRT MAX threshold\n", __func__); + /* VALRT MIN1 threshold setting */ + data[0] = irq_thres[MINVOLT1]; + if (rt5025_write_reg(client, RT5025_REG_VALRT_MIN1TH, data, 1) < 0) + printk(KERN_ERR "%s: failed to write VALRT MIN1 threshold\n", __func__); + /* VALRT MIN2 threshold setting */ + data[0] = irq_thres[MINVOLT2]; + if (rt5025_write_reg(client, RT5025_REG_VALRT_MIN2TH, data, 1) < 0) + printk(KERN_ERR "%s: failed to write VALRT MIN2 threshold\n", __func__); +} + +static void rt5025_alert_init(struct i2c_client *client) +{ + /* Set RT5025 gauge alert configuration */ + rt5025_alert_threshold_init(client); + /* Enable gauge alert function */ + rt5025_alert_setting(MAXTEMP,true); + rt5025_alert_setting(MINTEMP,true); + rt5025_alert_setting(MAXVOLT,true); + rt5025_alert_setting(MINVOLT1,true); + rt5025_alert_setting(MINVOLT2,true); +} + +void rt5025_irq_handler(void) +{ + rt5025_get_irq_flag(chip->client); + + if ((chip->irq_flag) & IRQ_FLG_BIT_TMX){ + printk(KERN_INFO "[RT5025]: Min temperature IRQ received\n"); + rt5025_alert_setting(MAXTEMP,false); + chip->max_temp_irq = false; + } + if ((chip->irq_flag) & IRQ_FLG_BIT_TMN){ + printk(KERN_INFO "[RT5025]: Max temperature IRQ received\n"); + rt5025_alert_setting(MINTEMP,false); + chip->min_temp_irq = false; + } + if ((chip->irq_flag) & IRQ_FLG_BIT_VMX){ + printk(KERN_INFO "[RT5025]: Max voltage IRQ received\n"); + rt5025_alert_setting(MAXVOLT,false); + chip->max_volt_irq = false; + } + if ((chip->irq_flag) & IRQ_FLG_BIT_VMN1){ + printk(KERN_INFO "[RT5025]: Min voltage1 IRQ received\n"); + rt5025_alert_setting(MINVOLT1,false); + chip->min_volt1_irq = false; + } + if ((chip->irq_flag) & IRQ_FLG_BIT_VMN2){ + printk(KERN_INFO "[RT5025]: Min voltage2 IRQ received\n"); + rt5025_alert_setting(MINVOLT2,false); + chip->min_volt2_irq = false; + } + + wake_lock(&chip->monitor_wake_lock); + schedule_delayed_work(&chip->monitor_work, 0); +} + +static void rt5025_update(struct i2c_client *client) +{ + /* Update voltage */ + rt5025_get_vcell(client); + /* Update current */ + rt5025_get_current(client); + /* Update external temperature */ + rt5025_get_external_temp(client); + /* Read timer */ + rt5025_get_timer(client); + /* Update chg cc */ + rt5025_get_chg_cc(client); + /* Update dchg cc */ + rt5025_get_dchg_cc(client); + /* Update SOC */ + rt5025_get_soc(client); + + if ((chip->max_temp_irq == false) && + (((irq_thres[MAXTEMP] * IRQ_THRES_UNIT) / 100 - chip->ain_volt) > irq_thres[TEMP_RLS])){ + rt5025_alert_setting(MAXTEMP,true); + }else if ((chip->min_temp_irq == false) && + ((chip->ain_volt - (irq_thres[MINTEMP] * IRQ_THRES_UNIT) / 100) > irq_thres[TEMP_RLS])){ + rt5025_alert_setting(MINTEMP,true); + }else if ((chip->max_volt_irq == false) && + ((((irq_thres[MAXVOLT] * IRQ_THRES_UNIT) / 100) - chip->vcell) > irq_thres[VOLT_RLS])){ + rt5025_alert_setting(MAXVOLT,true); + }else if ((chip->min_volt1_irq == false) && + ((chip->vcell - ((irq_thres[MINVOLT1] * IRQ_THRES_UNIT) / 100)) > irq_thres[VOLT_RLS])){ + rt5025_alert_setting(MINVOLT1,true); + }else if ((chip->min_volt2_irq == false) && + ((chip->vcell - ((irq_thres[MINVOLT2] * IRQ_THRES_UNIT) / 100)) > irq_thres[VOLT_RLS])){ + rt5025_alert_setting(MINVOLT2,true); + } +} + +static void rt5025_update_work(struct work_struct *work) +{ + unsigned long flags; + + rt5025_update(chip->client); + + /* Update data to framework */ + power_supply_changed(&chip->battery); + + /* prevent suspend before starting the alarm */ + local_irq_save(flags); + chip->last_poll = alarm_get_elapsed_realtime(); + rt5025_program_alarm(NORMAL_POLL); + local_irq_restore(flags); + + wake_unlock(&chip->monitor_wake_lock); +} + +static enum power_supply_property rt5025_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TECHNOLOGY, +}; + +void rt5025_gauge_suspend(void) +{ + rt5025_channel_cc(false); + cancel_delayed_work(&chip->monitor_work); + + RTINFO("\n"); +} + +void rt5025_gauge_resume(void) +{ + rt5025_channel_cc(true); + wake_lock(&chip->monitor_wake_lock); + schedule_delayed_work(&chip->monitor_work, 0); + RTINFO("\n"); +} + +void rt5025_gauge_remove(void) +{ + chip->info->event_callback = NULL; + power_supply_unregister(&chip->battery); + cancel_delayed_work(&chip->monitor_work); + wake_lock_destroy(&chip->monitor_wake_lock); + kfree(chip); +} + +int rt5025_gauge_init(struct rt5025_power_info *info) +{ + int ret; + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->client = info->i2c; + chip->info = info; + chip->battery.name = "rt5025-battery"; + chip->battery.type = POWER_SUPPLY_TYPE_BATTERY; + chip->battery.get_property = rt5025_get_property; + chip->battery.properties = rt5025_battery_props; + chip->battery.num_properties = ARRAY_SIZE(rt5025_battery_props); + + ret = power_supply_register(info->dev, &chip->battery); + if (ret) { + printk(KERN_ERR "[RT5025] power supply register failed\n"); + goto err_wake_lock; + } + + chip->last_poll = alarm_get_elapsed_realtime(); + alarm_init(&chip->wakeup_alarm, ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP, + rt5025_gauge_alarm); + + INIT_DELAYED_WORK(&chip->monitor_work, rt5025_update_work); + + wake_lock_init(&chip->monitor_wake_lock, WAKE_LOCK_SUSPEND, + "rt-battery-monitor"); + /* enable channel */ + rt5025_register_init(info->i2c); + + /* enable gauge IRQ */ + rt5025_alert_init(info->i2c); + + /* register callback functions */ + chip->cb.rt5025_gauge_irq_handler = rt5025_irq_handler; + chip->cb.rt5025_gauge_set_status = rt5025_set_status; + chip->cb.rt5025_gauge_suspend = rt5025_gauge_suspend; + chip->cb.rt5025_gauge_resume = rt5025_gauge_resume; + chip->cb.rt5025_gauge_remove = rt5025_gauge_remove; + info->event_callback=&chip->cb; + + //rt_register_gauge_callbacks(info->i2c, &chip->cb); + + wake_lock(&chip->monitor_wake_lock); + schedule_delayed_work(&chip->monitor_work, msecs_to_jiffies(1000)); + + return 0; + +err_wake_lock: + wake_lock_destroy(&chip->monitor_wake_lock); + kfree(chip); + + return ret; +} diff --git a/drivers/power/rt5025-power.c b/drivers/power/rt5025-power.c new file mode 100755 index 000000000000..d37232f9abc3 --- /dev/null +++ b/drivers/power/rt5025-power.c @@ -0,0 +1,310 @@ +/* drivers/power/rt5025-power.c + * I2C Driver for Richtek RT5025 PMIC + * Multi function device - multi functional baseband PMIC Power part + * + * Copyright (C) 2013 + * Author: CY Huang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static enum power_supply_property rt5025_adap_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static char *rt5025_supply_list[] = { + "rt5025-battery", +}; + +static int rt5025_set_charging_current_switch (struct i2c_client *i2c, int onoff) +{ + int ret; + if (onoff) + ret = rt5025_set_bits(i2c, RT5025_REG_CHGCTL7, RT5025_CHGCEN_MASK); + else + ret = rt5025_clr_bits(i2c, RT5025_REG_CHGCTL7, RT5025_CHGCEN_MASK); + return ret; +} + +static int rt5025_set_charging_buck(struct i2c_client *i2c, int onoff) +{ + int ret; + if (onoff) + ret = rt5025_set_bits(i2c, RT5025_REG_CHGCTL2, RT5025_CHGBUCKEN_MASK); + else + ret = rt5025_clr_bits(i2c, RT5025_REG_CHGCTL2, RT5025_CHGBUCKEN_MASK); + return ret; +} + +static int rt5025_chgstat_changed(struct rt5025_power_info *info, unsigned new_val) +{ + int ret = 0; + switch (new_val) + { + case 0x00: + rt5025_set_charging_current_switch(info->i2c, 1); + rt5025_set_charging_buck(info->i2c, 1); + info->chg_stat = 0x00; + if (info->event_callback) + info->event_callback->rt5025_gauge_set_status(POWER_SUPPLY_STATUS_CHARGING); + break; + case 0x01: + //rt5025_set_charging_current_switch(info->i2c, 1); + info->chg_stat = 0x01; + if (info->event_callback) + info->event_callback->rt5025_gauge_set_status(POWER_SUPPLY_STATUS_CHARGING); + break; + case 0x02: + rt5025_set_charging_current_switch(info->i2c, 0); + info->chg_stat = 0x02; + if (info->event_callback) + info->event_callback->rt5025_gauge_set_status(POWER_SUPPLY_STATUS_FULL); + break; + case 0x03: + rt5025_set_charging_buck(info->i2c, 0); + rt5025_set_charging_current_switch(info->i2c, 0); + info->chg_stat = 0x03; + if (info->event_callback) + info->event_callback->rt5025_gauge_set_status(POWER_SUPPLY_STATUS_DISCHARGING); + break; + default: + break; + } + return ret; +} + +int rt5025_power_passirq_to_gauge(struct rt5025_power_info *info) +{ + if (info->event_callback) + info->event_callback->rt5025_gauge_irq_handler(); + return 0; +} +EXPORT_SYMBOL(rt5025_power_passirq_to_gauge); + +int rt5025_power_charge_detect(struct rt5025_power_info *info) +{ + int ret = 0; + unsigned char chgstatval = 0; + unsigned old_usbval, old_acval, old_chgval, new_usbval, new_acval, new_chgval; + + old_acval = info->ac_online; + old_usbval = info->usb_online; + old_chgval = info->chg_stat; + + ret = rt5025_reg_read(info->i2c, RT5025_REG_CHGSTAT); + if (ret<0) + { + dev_err(info->dev, "read chg stat reg fail\n"); + return ret; + } + chgstatval = ret; + + new_acval = (chgstatval&RT5025_CHG_ACONLINE)>>RT5025_CHG_ACSHIFT; + if (old_acval != new_acval) + { + info->ac_online = new_acval; + power_supply_changed(&info->ac); + } + new_usbval = (chgstatval&RT5025_CHG_USBONLINE)>>RT5025_CHG_USBSHIFT; + if (old_usbval != new_usbval) + { + info->usb_online = new_usbval; + power_supply_changed(&info->usb); + } + + new_chgval = (chgstatval&RT5025_CHGSTAT_MASK)>>RT5025_CHGSTAT_SHIFT; + if (new_acval || new_usbval) + { + if (old_chgval != new_chgval) + { + ret = rt5025_chgstat_changed(info, new_chgval); + } + } + else + { + rt5025_set_charging_buck(info->i2c, 0); + rt5025_set_charging_current_switch(info->i2c, 0); + info->chg_stat = RT5025_CHGSTAT_UNKNOWN; + if (info->event_callback) + info->event_callback->rt5025_gauge_set_status(POWER_SUPPLY_STATUS_NOT_CHARGING); + } + + return ret; +} +EXPORT_SYMBOL(rt5025_power_charge_detect); + +static int rt5025_adap_get_props(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct rt5025_power_info *info = dev_get_drvdata(psy->dev->parent); + switch(psp) + { + case POWER_SUPPLY_PROP_ONLINE: + if (psy->type == POWER_SUPPLY_TYPE_MAINS) + val->intval = info->ac_online; + else if (psy->type == POWER_SUPPLY_TYPE_USB) + val->intval = info->usb_online; + else + return -EINVAL; + break; + default: + return -EINVAL; + } + return 0; +} + +static int __devinit rt5025_init_charger(struct rt5025_power_info *info, struct rt5025_power_data* pd) +{ + info->ac_online = 0; + info->usb_online =0; + //init charger buckck & charger current en to disable stat + info->chg_stat = RT5025_CHGSTAT_UNKNOWN; + if (info->event_callback) + info->event_callback->rt5025_gauge_set_status(POWER_SUPPLY_STATUS_DISCHARGING); + rt5025_set_bits(info->i2c, RT5025_REG_CHGCTL4, RT5025_CHGRST_MASK); + udelay(200); + //init register setting + rt5025_reg_write(info->i2c, RT5025_REG_CHGCTL2, pd->CHGControl2.val); + rt5025_reg_write(info->i2c, RT5025_REG_CHGCTL3, pd->CHGControl3.val); + rt5025_reg_write(info->i2c, RT5025_REG_CHGCTL4, pd->CHGControl4.val); + rt5025_reg_write(info->i2c, RT5025_REG_CHGCTL5, pd->CHGControl5.val); + rt5025_reg_write(info->i2c, RT5025_REG_CHGCTL6, pd->CHGControl6.val); + rt5025_reg_write(info->i2c, RT5025_REG_CHGCTL7, pd->CHGControl7.val); + + rt5025_power_charge_detect(info); + + return 0; +} + +static int __devinit rt5025_power_probe(struct platform_device *pdev) +{ + struct rt5025_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct rt5025_platform_data *pdata = chip->dev->platform_data; + struct rt5025_power_info *pi; + int ret = 0; + printk("%s,line=%d\n", __func__,__LINE__); + + pi = kzalloc(sizeof(*pi), GFP_KERNEL); + if (!pi) + return -ENOMEM; + + pi->i2c = chip->i2c; + pi->dev = &pdev->dev; + + ret = rt5025_gauge_init(pi); + if (ret) + goto out; + + platform_set_drvdata(pdev, pi); + + pi->ac.name = "rt5025-ac"; + pi->ac.type = POWER_SUPPLY_TYPE_MAINS; + pi->ac.supplied_to = rt5025_supply_list; + pi->ac.properties = rt5025_adap_props; + pi->ac.num_properties = ARRAY_SIZE(rt5025_adap_props); + pi->ac.get_property = rt5025_adap_get_props; + ret = power_supply_register(&pdev->dev, &pi->ac); + if (ret) + goto out; + + pi->usb.name = "rt5025-usb"; + pi->usb.type = POWER_SUPPLY_TYPE_USB; + pi->ac.supplied_to = rt5025_supply_list; + pi->usb.properties = rt5025_adap_props; + pi->usb.num_properties = ARRAY_SIZE(rt5025_adap_props); + pi->usb.get_property = rt5025_adap_get_props; + ret = power_supply_register(&pdev->dev, &pi->usb); + if (ret) + goto out_usb; + + rt5025_init_charger(pi, pdata->power_data); + chip->power_info = pi; + + return ret; +out_usb: + power_supply_unregister(&pi->ac); +out: + kfree(pi); + + return ret; +} + +static int rt5025_power_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct rt5025_power_info *pi = platform_get_drvdata(pdev); + + if (pi->event_callback) + pi->event_callback->rt5025_gauge_suspend(); + return 0; +} + +static int rt5025_power_resume(struct platform_device *pdev) +{ + struct rt5025_power_info *pi = platform_get_drvdata(pdev); + if (pi->event_callback) + pi->event_callback->rt5025_gauge_resume(); + return 0; +} + +static int __devexit rt5025_power_remove(struct platform_device *pdev) +{ + struct rt5025_power_info *pi = platform_get_drvdata(pdev); + struct rt5025_chip *chip = dev_get_drvdata(pdev->dev.parent); + + if (pi->event_callback) + pi->event_callback->rt5025_gauge_remove(); + power_supply_unregister(&pi->usb); + power_supply_unregister(&pi->ac); + chip->power_info = NULL; + kfree(pi); + + return 0; +} + +static struct platform_driver rt5025_power_driver = +{ + .driver = { + .name = RT5025_DEVICE_NAME "-power", + .owner = THIS_MODULE, + }, + .probe = rt5025_power_probe, + .remove = __devexit_p(rt5025_power_remove), + .suspend = rt5025_power_suspend, + .resume = rt5025_power_resume, +}; + +static int __init rt5025_power_init(void) +{ + return platform_driver_register(&rt5025_power_driver); +} +subsys_initcall_sync(rt5025_power_init); + +static void __exit rt5025_power_exit(void) +{ + platform_driver_unregister(&rt5025_power_driver); +} +module_exit(rt5025_power_exit); + + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("CY Huang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct rt5025_regulator_info { + struct regulator_desc desc; + struct regulator_dev *regulator; + struct i2c_client *i2c; + struct rt5025_chip *chip; + const unsigned int *vol_output_list; + const int vol_output_size; + int min_uV; + int max_uV; + int vol_reg; + int vol_shift; + int vol_mask; + int enable_bit; + int enable_reg; +}; + +//for DCDC1 +static const unsigned int rt5025_vol_output_list1[] = +{ + 700*1000, 725*1000, 750*1000, 775*1000, 800*1000, 825*1000, 850*1000, 875*1000, + 900*1000, 925*1000, 950*1000, 975*1000, 1000*1000, 1025*1000, 1050*1000, 1075*1000, + 1100*1000, 1125*1000, 1150*1000, 1175*1000, 1200*1000, 1225*1000, 1250*1000, 1275*1000, + 1300*1000, 1325*1000, 1350*1000, 1375*1000, 1400*1000, 1425*1000, 1450*1000, 1475*1000, + 1500*1000, 1525*1000, 1550*1000, 1575*1000, 1600*1000, 1625*1000, 1650*1000, 1675*1000, + 1700*1000, 1725*1000, 1750*1000, 1775*1000, 1800*1000, 1825*1000, 1850*1000, 1875*1000, + 1900*1000, 1925*1000, 1950*1000, 1975*1000, 2000*2000, 2025*1000, 2050*1000, 2075*1000, + 2100*1000, 2125*1000, 2150*1000, 2175*1000, 2200*1000, 2225*1000, 2250*1000, 2275*1000, +}; +#define rt5025_vol_output_size1 ARRAY_SIZE(rt5025_vol_output_list1) + +//DCDC2, LDO1, LDO2 +static const unsigned int rt5025_vol_output_list2[] = +{ + 700*1000, 725*1000, 750*1000, 775*1000, 800*1000, 825*1000, 850*1000, 875*1000, + 900*1000, 925*1000, 950*1000, 975*1000, 1000*1000, 1025*1000, 1050*1000, 1075*1000, + 1100*1000, 1125*1000, 1150*1000, 1175*1000, 1200*1000, 1225*1000, 1250*1000, 1275*1000, + 1300*1000, 1325*1000, 1350*1000, 1375*1000, 1400*1000, 1425*1000, 1450*1000, 1475*1000, + 1500*1000, 1525*1000, 1550*1000, 1575*1000, 1600*1000, 1625*1000, 1650*1000, 1675*1000, + 1700*1000, 1725*1000, 1750*1000, 1775*1000, 1800*1000, 1825*1000, 1850*1000, 1875*1000, + 1900*1000, 1925*1000, 1950*1000, 1975*1000, 2000*2000, 2025*1000, 2050*1000, 2075*1000, + 2100*1000, 2125*1000, 2150*1000, 2175*1000, 2200*1000, 2225*1000, 2250*1000, 2275*1000, + 2300*1000, 2325*1000, 2350*1000, 2375*1000, 2400*1000, 2425*1000, 2450*1000, 2475*1000, + 2500*1000, 2525*1000, 2550*1000, 2575*1000, 2600*1000, 2625*1000, 2650*1000, 2675*1000, + 2700*1000, 2725*1000, 2750*1000, 2775*1000, 2800*1000, 2825*1000, 2850*1000, 2875*1000, + 2900*1000, 2925*1000, 2950*1000, 2975*1000, 3000*1000, 3025*1000, 3050*1000, 3075*1000, + 3100*1000, 3125*1000, 3150*1000, 3175*1000, 3200*1000, 3225*1000, 3250*1000, 3275*1000, + 3300*1000, 3325*1000, 3350*1000, 3375*1000, 3400*1000, 3425*1000, 3450*1000, 3475*1000, + 3500*1000, 3500*1000, 3500*1000, 3500*1000, 3500*1000, 3500*1000, 3500*1000, 3500*1000, + 3500*1000, 3500*1000, 3500*1000, 3500*1000, 3500*1000, 3500*1000, 3500*1000, 3500*1000, +}; +#define rt5025_vol_output_size2 ARRAY_SIZE(rt5025_vol_output_list2) + +//DCDC3 +static const unsigned int rt5025_vol_output_list3[] = +{ + 700*1000, 750*1000, 800*1000, 850*1000, 900*1000, 950*1000, 1000*1000, 1050*1000, + 1100*1000, 1150*1000, 1200*1000, 1250*1000, 1300*1000, 1350*1000, 1400*1000, 1450*1000, + 1500*1000, 1550*1000, 1600*1000, 1650*1000, 1700*1000, 1750*1000, 1800*1000, 1850*1000, + 1900*1000, 1950*1000, 2000*1000, 2050*1000, 2100*1000, 2150*1000, 2200*1000, 2250*1000, + 2300*1000, 2350*1000, 2400*1000, 2450*1000, 2500*1000, 2550*1000, 2600*1000, 2650*1000, + 2700*1000, 2750*1000, 2800*1000, 2850*1000, 2900*1000, 2950*1000, 3000*1000, 3050*1000, + 3100*1000, 3150*1000, 3200*1000, 3250*1000, 3300*1000, 3350*1000, 3400*1000, 3450*1000, + 3500*1000, 3500*1000, 3500*1000, 3500*1000, 3500*1000, 3500*1000, 3500*1000, 3500*1000, +}; +#define rt5025_vol_output_size3 ARRAY_SIZE(rt5025_vol_output_list3) + +//DCDC4 +static const unsigned int rt5025_vol_output_list4[] = +{ + 4500*1000, 4600*1000, 4700*1000, 4800*1000, 4900*1000, 5000*1000, 5100*1000, 5200*1000, + 5300*1000, 5400*1000, 5500*1000, 5500*1000, 5500*1000, 5500*1000, 5500*1000, 5500*1000, +}; +#define rt5025_vol_output_size4 ARRAY_SIZE(rt5025_vol_output_list4) + +//LDO3, LDO4, LDO5, LDO6 +static const unsigned int rt5025_vol_output_list5[] = +{ + 1000*1000, 1100*1000, 1200*1000, 1300*1000, 1400*1000, 1500*1000, 1600*1000, 1700*1000, + 1800*1000, 1900*1000, 2000*1000, 2100*1000, 2200*1000, 2300*1000, 2400*1000, 2500*1000, + 2600*1000, 2700*1000, 2800*1000, 2900*1000, 3000*1000, 3100*1000, 3200*1000, 3300*1000, + 3300*1000, 3300*1000, 3300*1000, 3300*1000, 3300*1000, 3300*1000, 3300*1000, 3300*1000, + 3300*1000, 3300*1000, 3300*1000, 3300*1000, 3300*1000, 3300*1000, 3300*1000, 3300*1000, +}; +#define rt5025_vol_output_size5 ARRAY_SIZE(rt5025_vol_output_list5) + +static inline int check_range(struct rt5025_regulator_info *info, + int min_uV, int max_uV) +{ + if (min_uV < info->min_uV || min_uV > info->max_uV) + return -EINVAL; + + return 0; +} + +static int rt5025_list_voltage(struct regulator_dev *rdev, unsigned index) +{ + struct rt5025_regulator_info *info = rdev_get_drvdata(rdev); + + return (index>=info->vol_output_size)? \ + -EINVAL: \ + info->vol_output_list[index ]; +} + +//#if (LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,38)) +static int rt5025_set_voltage_sel(struct regulator_dev *rdev, unsigned selector) +{ + struct rt5025_regulator_info *info = rdev_get_drvdata(rdev); + unsigned char data; + const int count = info->vol_output_size; + + if (selector>count) + return -EINVAL; + data = (unsigned char)selector; + data <<= info->vol_shift; + return rt5025_assign_bits(info->i2c, info->vol_reg, info->vol_mask, data); +} + +static int rt5025_get_voltage_sel(struct regulator_dev *rdev) +{ + struct rt5025_regulator_info *info = rdev_get_drvdata(rdev); + int ret; + ret = rt5025_reg_read(info->i2c, info->vol_reg); + if (ret < 0) + return ret; + return (ret & info->vol_mask) >> info->vol_shift; +} +//#else +static int rt5025_find_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct rt5025_regulator_info *info = rdev_get_drvdata(rdev); + int i=0; + const int count = info->vol_output_size; + for (i=0;ivol_output_list[i]>=min_uV) + && (info->vol_output_list[i]<=max_uV)) + return i; + } + return -EINVAL; +} + +static int rt5025_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned *selector) + +{ + struct rt5025_regulator_info *info = rdev_get_drvdata(rdev); + unsigned char data; + if (check_range(info, min_uV, max_uV)) { + dev_err(info->chip->dev, "invalid voltage range (%d, %d) uV\n", + min_uV, max_uV); + return -EINVAL; + } + data = rt5025_find_voltage(rdev,min_uV,max_uV); + data <<= info->vol_shift; + return rt5025_assign_bits(info->i2c, info->vol_reg, info->vol_mask, data); +} + + +static int rt5025_get_voltage(struct regulator_dev *rdev) +{ + int ret; + ret = rt5025_get_voltage_sel(rdev); + if (ret < 0) + return ret; + return rt5025_list_voltage(rdev, ret ); + +} +//#endif /* LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,38) */ + +static int rt5025_enable(struct regulator_dev *rdev) +{ + struct rt5025_regulator_info *info = rdev_get_drvdata(rdev); + + return rt5025_set_bits(info->i2c, info->enable_reg, + info->enable_bit); +} + +static int rt5025_disable(struct regulator_dev *rdev) +{ + struct rt5025_regulator_info *info = rdev_get_drvdata(rdev); + + return rt5025_clr_bits(info->i2c, info->enable_reg, + info->enable_bit); +} + +static int rt5025_is_enabled(struct regulator_dev *rdev) +{ + struct rt5025_regulator_info *info = rdev_get_drvdata(rdev); + int ret; + + ret = rt5025_reg_read(info->i2c, info->enable_reg); + if (ret < 0) + return ret; + + return (ret & (info->enable_bit))?1:0; +} +static int rt5025_dcdc_get_mode(struct regulator_dev *rdev) +{ + struct rt5025_regulator_info *info = rdev_get_drvdata(rdev); + int buck = rdev_get_id(rdev) - 0; + int ret; + uint8_t control; + + ret = rt5025_reg_read(info->i2c, 0x0c); + if (ret < 0) { + return ret; + } + if (buck ==0){ + control =(ret & 0x80)>>7; + } + else if (buck ==1){ + control =(ret & 0x40)>>6; + } + else if (buck ==2){ + control =(ret & 0x20)>>5; + } + else{ + return -1; + } + switch (control) { + case 0: + return REGULATOR_MODE_FAST; + case 1: + return REGULATOR_MODE_NORMAL; + default: + return -1; + } + +} +static int rt5025_dcdc_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct rt5025_regulator_info *info = rdev_get_drvdata(rdev); + int buck = rdev_get_id(rdev) - 0; + int ret; + uint8_t control; + + ret = rt5025_reg_read(info->i2c, 0x0c); + if (buck ==0){ + control =ret &(~ (1 <<7)); + } + else if (buck ==1){ + control =ret &(~ (1 <<6)); + } + else if (buck ==2){ + control =ret &(~ (1 <<5)); + } + else{ + return -1; + } + + switch(mode) + { + case REGULATOR_MODE_FAST: + return rt5025_reg_write(info->i2c, 0x0c,control); + case REGULATOR_MODE_NORMAL: + return rt5025_reg_write(info->i2c, 0x0c,(control | (1 <<(7-buck)))); + default: + printk("error:pmu_rt5025 only powersave pwm & auto mode\n"); + return -EINVAL; + } +} +static int rt5025_dcdc_set_voltage_time_sel(struct regulator_dev *rdev, unsigned int old_selector, + unsigned int new_selector) +{ + int old_volt, new_volt; + + old_volt = rt5025_list_voltage(rdev, old_selector); + if (old_volt < 0) + return old_volt; + + new_volt = rt5025_list_voltage(rdev, new_selector); + if (new_volt < 0) + return new_volt; + + return DIV_ROUND_UP(abs(old_volt - new_volt), 25000); +} + +static struct regulator_ops rt5025_regulator_ops = { + .list_voltage = rt5025_list_voltage, +//#if (LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,38)) +// .get_voltage_sel = rt5025_get_voltage_sel, +// .set_voltage_sel = rt5025_set_voltage_sel, +//#else + .set_voltage = rt5025_set_voltage, + .get_voltage = rt5025_get_voltage, +//#endif /* LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,38) */ + .enable = rt5025_enable, + .disable = rt5025_disable, + .is_enabled = rt5025_is_enabled, + .get_mode = rt5025_dcdc_get_mode, + .set_mode = rt5025_dcdc_set_mode, + .set_voltage_time_sel = rt5025_dcdc_set_voltage_time_sel, +}; + +#define RT5025_DCDCVOUT_LIST1 rt5025_vol_output_list1 +#define RT5025_DCDCVOUT_LIST2 rt5025_vol_output_list2 +#define RT5025_DCDCVOUT_LIST3 rt5025_vol_output_list3 +#define RT5025_DCDCVOUT_LIST4 rt5025_vol_output_list4 +#define RT5025_LDOVOUT_LIST1 rt5025_vol_output_list2 +#define RT5025_LDOVOUT_LIST2 rt5025_vol_output_list2 +#define RT5025_LDOVOUT_LIST3 rt5025_vol_output_list5 +#define RT5025_LDOVOUT_LIST4 rt5025_vol_output_list5 +#define RT5025_LDOVOUT_LIST5 rt5025_vol_output_list5 +#define RT5025_LDOVOUT_LIST6 rt5025_vol_output_list5 + +#define RT5025_DCDCVOUT_SIZE1 rt5025_vol_output_size1 +#define RT5025_DCDCVOUT_SIZE2 rt5025_vol_output_size2 +#define RT5025_DCDCVOUT_SIZE3 rt5025_vol_output_size3 +#define RT5025_DCDCVOUT_SIZE4 rt5025_vol_output_size4 +#define RT5025_LDOVOUT_SIZE1 rt5025_vol_output_size2 +#define RT5025_LDOVOUT_SIZE2 rt5025_vol_output_size2 +#define RT5025_LDOVOUT_SIZE3 rt5025_vol_output_size5 +#define RT5025_LDOVOUT_SIZE4 rt5025_vol_output_size5 +#define RT5025_LDOVOUT_SIZE5 rt5025_vol_output_size5 +#define RT5025_LDOVOUT_SIZE6 rt5025_vol_output_size5 + + +#define RT5025_DCDC(_id, min, max) \ +{ \ + .desc = { \ + .name = "rt5025-dcdc" #_id, \ + .n_voltages = RT5025_DCDCVOUT_SIZE##_id, \ + .ops = &rt5025_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = RT5025_ID_DCDC##_id, \ + .owner = THIS_MODULE, \ + }, \ + .vol_output_list= RT5025_DCDCVOUT_LIST##_id, \ + .vol_output_size= RT5025_DCDCVOUT_SIZE##_id, \ + .min_uV = min , \ + .max_uV = max , \ + .vol_reg = RT5025_DCDCVOUT##_id, \ + .vol_shift = RT5025_DCDCVOUT_SHIFT##_id, \ + .vol_mask = RT5025_DCDCVOUT_MASK##_id, \ + .enable_reg = RT5025_DCDC_OUTPUT_EN, \ + .enable_bit = RT5025_DCDCEN_MASK##_id, \ +} + +#define RT5025_LDO(_id, min, max) \ +{ \ + .desc = { \ + .name = "rt5025-ldo" #_id, \ + .n_voltages = RT5025_LDOVOUT_SIZE##_id, \ + .ops = &rt5025_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = RT5025_ID_LDO##_id, \ + .owner = THIS_MODULE, \ + }, \ + .vol_output_list= RT5025_LDOVOUT_LIST##_id, \ + .vol_output_size= RT5025_LDOVOUT_SIZE##_id, \ + .min_uV = min , \ + .max_uV = max, \ + .vol_reg = RT5025_LDOVOUT##_id, \ + .vol_shift = RT5025_LDOVOUT_SHIFT##_id, \ + .vol_mask = RT5025_LDOVOUT_MASK##_id, \ + .enable_reg = RT5025_LDO_OUTPUT_EN, \ + .enable_bit = RT5025_LDOEN_MASK##_id, \ +} + +static struct rt5025_regulator_info rt5025_regulator_info[] = +{ + RT5025_DCDC(1, 700000, 2275000), + RT5025_DCDC(2, 700000, 3500000), + RT5025_DCDC(3, 700000, 3500000), + RT5025_DCDC(4, 4500000, 5500000), + RT5025_LDO( 1, 700000, 3500000), + RT5025_LDO( 2, 700000, 3500000), + RT5025_LDO( 3, 1000000, 3300000), + RT5025_LDO( 4, 1000000, 3300000), + RT5025_LDO( 5, 1000000, 3300000), + RT5025_LDO( 6, 1000000, 3300000), +}; + +static struct rt5025_regulator_info * __devinit find_regulator_info(int id) +{ + struct rt5025_regulator_info *ri; + int i; + + for (i = 0; i < ARRAY_SIZE(rt5025_regulator_info); i++) { + ri = &rt5025_regulator_info[i]; + if (ri->desc.id == id) + return ri; + } + return NULL; +} + +inline struct regulator_dev* rt5025_regulator_register(struct regulator_desc *regulator_desc, + struct device *dev, struct regulator_init_data *init_data, + void *driver_data) +{ +#if (LINUX_VERSION_CODE>=KERNEL_VERSION(3,5,0)) + struct regulator_config config = { + .dev = dev, + .init_data = init_data, + .driver_data = driver_data, + }; + return regulator_register(®ulator_desc, &config); +#else + return regulator_register(regulator_desc,dev,init_data,driver_data); +#endif /* LINUX_VERSION_CODE>=KERNEL_VERSION(3,5,0)) */ +} + +static int __devinit rt5025_regulator_probe(struct platform_device *pdev) +{ + struct rt5025_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct rt5025_platform_data *pdata = chip->dev->platform_data; + struct rt5025_regulator_info *ri; + struct regulator_dev *rdev; + struct regulator_init_data* init_data; + + ri = find_regulator_info(pdev->id); + if (ri == NULL) { + dev_err(&pdev->dev, "invalid regulator ID specified\n"); + return -EINVAL; + } + init_data = pdata->regulator[pdev->id]; + if (init_data == NULL) { + dev_err(&pdev->dev, "no initializing data\n"); + return -EINVAL; + } + ri->i2c = chip->i2c; + ri->chip = chip; + + rdev = rt5025_regulator_register(&ri->desc, &pdev->dev, + init_data, ri); + if (IS_ERR(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 rt5025_regulator_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + regulator_unregister(rdev); + + return 0; +} + +static struct platform_driver rt5025_regulator_driver = +{ + .driver = { + .name = RT5025_DEVICE_NAME "-regulator", + .owner = THIS_MODULE, + }, + .probe = rt5025_regulator_probe, + .remove = __devexit_p(rt5025_regulator_remove), +}; + +static int __init rt5025_regulator_init(void) +{ + return platform_driver_register(&rt5025_regulator_driver); +} +subsys_initcall_sync(rt5025_regulator_init); + +static void __exit rt5025_regulator_exit(void) +{ + platform_driver_unregister(&rt5025_regulator_driver); +} +module_exit(rt5025_regulator_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("CY Huang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_RT5025_GPIO_H +#define __LINUX_RT5025_GPIO_H + +#define RT5025_REG_GPIO0 0x1C +#define RT5025_REG_GPIO1 0x1D +#define RT5025_REG_GPIO2 0x1E + +#define RT5025_GPIO_NR 3 + +#define RT5025_GPIO_INPUT 0x00 +#define RT5025_GPIO_OUTPUT 0x02 + +#define RT5025_GPIO_DIRSHIFT 6 +#define RT5025_GPIO_DIRMASK 0xC0 +#define RT5025_GPIO_OVALUESHIFT 4 +#define RT5025_GPIO_OVALUEMASK 0x10 +#define RT5025_GPIO_IVALUEMASK 0x08 + +#endif /* #ifndef __LINUX_RT5025_GPIO_H */ diff --git a/include/linux/mfd/rt5025-irq.h b/include/linux/mfd/rt5025-irq.h new file mode 100755 index 000000000000..96efd61ce45b --- /dev/null +++ b/include/linux/mfd/rt5025-irq.h @@ -0,0 +1,24 @@ +/* + * include/linux/mfd/rt5025-irq.h + * Include header file for Richtek RT5025 PMIC IRQ file + * + * Copyright (C) 2013 Richtek Electronics + * cy_huang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_RT5025_IRQ_H +#define __LINUX_RT5025_IRQ_H + +#define RT5025_REG_CHGSTAT 0x01 + +#define RT5025_REG_IRQEN1 0x30 +#define RT5025_REG_IRQEN2 0x32 +#define RT5025_REG_IRQEN3 0x34 +#define RT5025_REG_IRQEN4 0x36 +#define RT5025_REG_IRQEN5 0x38 + +#endif /* #ifndef __LINUX_RT5025_IRQ_H */ diff --git a/include/linux/mfd/rt5025-misc.h b/include/linux/mfd/rt5025-misc.h new file mode 100755 index 000000000000..dbd7e4e99d60 --- /dev/null +++ b/include/linux/mfd/rt5025-misc.h @@ -0,0 +1,25 @@ +/* + * include/linux/mfd/rt5025-misc.h + * Include header file for Richtek RT5025 PMIC Misc + * + * Copyright (C) 2013 Richtek Electronics + * cy_huang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_RT5025_MISC_H +#define __LINUX_RT5025_MISC_H + +#define RT5025_RESETCTRL_REG 0x15 +#define RT5025_VSYSULVO_REG 0x17 +#define RT5025_PWRONCTRL_REG 0x19 +#define RT5025_SHDNCTRL_REG 0x1A +#define RT5025_PWROFFEN_REG 0x1B + +#define RT5025_SHDNCTRL_MASK 0x80 +#define RT5025_VSYSOFF_MASK 0xE0 + +#endif /* #ifndef __LINUX_RT5025_MISC_H */ diff --git a/include/linux/mfd/rt5025.h b/include/linux/mfd/rt5025.h new file mode 100755 index 000000000000..77e76a43ff9f --- /dev/null +++ b/include/linux/mfd/rt5025.h @@ -0,0 +1,457 @@ +/* + * include/linux/mfd/rt5025.h + * Include header file for Richtek RT5025 Core file + * + * Copyright (C) 2013 Richtek Electronics + * cy_huang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_MFD_RT5025_H +#define __LINUX_MFD_RT5025_H + +#include + +#define RT5025_DEVICE_NAME "RT5025" + +enum { + RT5025_RSTDELAY1_100MS, + RT5025_RSTDELAY1_500MS, + RT5025_RSTDELAY1_1S, + RT5025_RSTDELAY1_2S, +}; + +enum { + RT5025_RSTDELAY2_100MS, + RT5025_RSTDELAY2_500MS, + RT5025_RSTDELAY2_1S, + RT5025_RSTDELAY2_2S, +}; + +enum { + RT5025_VOFF_2P8V, + RT5025_VOFF_2P9V, + RT5025_VOFF_3P0V, + RT5025_VOFF_3P1V, + RT5025_VOFF_3P2V, + RT5025_VOFF_3P3V, + RT5025_VOFF_3P4V, + RT5025_VOFF_3P5V, +}; + +enum { + RT5025_STARTIME_100MS, + RT5025_STARTIME_1S, + RT5025_STARTIME_2S, + RT5025_STARTIME_3S, +}; + +enum { + RT5025_LPRESS_1S, + RT5025_LPRESS_1P5S, + RT5025_LPRESS_2S, + RT5025_LPRESS_2P5S, +}; + +enum { + RT5025_SHDNPRESS_4S, + RT5025_SHDNPRESS_6S, + RT5025_SHDNPRESS_8S, + RT5025_SHDNPRESS_10S, +}; + +enum { + RT5025_PGDLY_10MS, + RT5025_PGDLY_50MS, + RT5025_PGDLY_100MS, + RT5025_PGDLY_200MS, +}; + +enum { + RT5025_SHDNDLY_100MS, + RT5025_SHDNDLY_500MS, + RT5025_SHDNDLY_1S, + RT5025_SHDNDLY_2S, +}; + +enum { + RT5025_CCCHG_TO_4H, + RT5025_CCCHG_TO_6H, + RT5025_CCCHG_TO_8H, + RT5025_CCCHG_TO_10H, +}; + +enum { + RT5025_PRECHG_TO_30M, + RT5025_PRECHG_TO_40M, + RT5025_PRECHG_TO_50M, + RT5025_PRECHG_TO_60M, +}; + +enum { + RT5025_ICC_0P5A, + RT5025_ICC_0P6A, + RT5025_ICC_0P7A, + RT5025_ICC_0P8A, + RT5025_ICC_0P9A, + RT5025_ICC_1A, + RT5025_ICC_1P1A, + RT5025_ICC_1P2A, + RT5025_ICC_1P3A, + RT5025_ICC_1P4A, + RT5025_ICC_1P5A, + RT5025_ICC_1P6A, + RT5025_ICC_1P7A, + RT5025_ICC_1P8A, + RT5025_ICC_1P9A, + RT5025_ICC_2A, + RT5025_ICC_MAX, +}; + +enum { + RT5025_AICR_100MA, + RT5025_AICR_500MA, + RT5025_AICR_1A, + RT5025_AICR_NOLIMIT, +}; + +enum { + RT5025_DPM_4V, + RT5025_DPM_4P25V, + RT5025_DPM_4P5V, + RT5025_DPM_DIS, +}; + +enum { + RT5025_VPREC_2V, + RT5025_VPREC_2P2V, + RT5025_VPREC_2P4V, + RT5025_VPREC_2P6V, + RT5025_VPREC_2P8V, + RT5025_VPREC_3V, + RT5025_VPREC_3V_1, + RT5025_VPREC_3V_2, +}; + +enum { + RT5025_IEOC_10P, + RT5025_IEOC_20P, +}; + +enum { + RT5025_IPREC_10P, + RT5025_IPREC_20P, +}; + +enum { + RT5025_ID_DCDC1, + RT5025_ID_DCDC2, + RT5025_ID_DCDC3, + RT5025_ID_DCDC4, + RT5025_ID_LDO1, + RT5025_ID_LDO2, + RT5025_ID_LDO3, + RT5025_ID_LDO4, + RT5025_ID_LDO5, + RT5025_ID_LDO6, + RT5025_MAX_REGULATOR, +}; + +struct rt5025_power_data { + union { + struct { + unsigned char Resv1:1; + unsigned char CHGBC_EN:1; + unsigned char TE:1; + unsigned char Resv2:1; + unsigned char CCCHG_TIMEOUT:2; + unsigned char PRECHG_TIMEOUT:2; + }bitfield; + unsigned char val; + }CHGControl2; + union { + struct { + unsigned char Resv:2; + unsigned char VOREG:6; + }bitfield; + unsigned char val; + }CHGControl3; + union { + struct { + unsigned char Resv:1; + unsigned char AICR:2; + unsigned char ICC:4; + unsigned char CHG_RST:1; + }bitfield; + unsigned char val; + }CHGControl4; + union { + struct { + unsigned char Resv1:4; + unsigned char DPM:2; + unsigned char Resv2:2; + }bitfield; + unsigned char val; + }CHGControl5; + union { + struct { + unsigned char IPREC:1; + unsigned char IEOC:1; + unsigned char VPREC:3; + unsigned char Resv:3; + }bitfield; + unsigned char val; + }CHGControl6; + union { + struct { + unsigned char Resv1:4; + unsigned char CHGC_EN:1; + unsigned char CHG_DCDC_MODE:1; + unsigned char BATD_EN:1; + unsigned char Resv2:1; + }bitfield; + unsigned char val; + }CHGControl7; +}; + +struct rt5025_gpio_data { + unsigned gpio_base; + unsigned irq_base; +}; + +struct rt5025_misc_data { + union { + struct { + unsigned char Action:2; + unsigned char Delayed1:2; + unsigned char Delayed2:2; + unsigned char Resv:2; + }bitfield; + unsigned char val; + }RSTCtrl; + union { + struct { + unsigned char Resv:5; + unsigned char VOFF:3; + }bitfield; + unsigned char val; + }VSYSCtrl; + union { + struct { + unsigned char PG_DLY:2; + unsigned char SHDN_PRESS:2; + unsigned char LPRESS_TIME:2; + unsigned char START_TIME:2; + }bitfield; + unsigned char val; + }PwrOnCfg; + union { + struct { + unsigned char Resv:4; + unsigned char SHDN_DLYTIME:2; + unsigned char SHDN_TIMING:1; + unsigned char SHDN_CTRL:1; + }bitfield; + unsigned char val; + }SHDNCtrl; + union { + struct { + unsigned char Resv:2; + unsigned char OT_ENSHDN:1; + unsigned char PWRON_ENSHDN:1; + unsigned char DCDC3LV_ENSHDN:1; + unsigned char DCDC2LV_ENSHDN:1; + unsigned char DCDC1LV_ENSHDN:1; + unsigned char SYSLV_ENSHDN:1; + }bitfield; + unsigned char val; + }PwrOffCond; +}; + +struct rt5025_irq_data { + union { + struct { + unsigned char BATABS:1; + unsigned char Resv1:2; + unsigned char INUSB_PLUGIN:1; + unsigned char INUSBOVP:1; + unsigned char Resv2:1; + unsigned char INAC_PLUGIN:1; + unsigned char INACOVP:1; + }bitfield; + unsigned char val; + }irq_enable1; + union { + struct { + unsigned char CHTERMI:1; + unsigned char CHBATOVI:1; + unsigned char CHGOODI_INUSB:1; + unsigned char CHBADI_INUSB:1; + unsigned char CHSLPI_INUSB:1; + unsigned char CHGOODI_INAC:1; + unsigned char CHBADI_INAC:1; + unsigned char CHSLPI_INAC:1; + }bitfield; + unsigned char val; + }irq_enable2; + union { + struct { + unsigned char TIMEOUT_CC:1; + unsigned char TIMEOUT_PC:1; + unsigned char Resv:3; + unsigned char CHVSREGI:1; + unsigned char CHTREGI:1; + unsigned char CHRCHGI:1; + }bitfield; + unsigned char val; + }irq_enable3; + union { + struct { + unsigned char SYSLV:1; + unsigned char DCDC4LVHV:1; + unsigned char PWRONLP:1; + unsigned char PWRONSP:1; + unsigned char DCDC3LV:1; + unsigned char DCDC2LV:1; + unsigned char DCDC1LV:1; + unsigned char OT:1; + }bitfield; + unsigned char val; + }irq_enable4; + union { + struct { + unsigned char Resv:1; + unsigned char GPIO0_IE:1; + unsigned char GPIO1_IE:1; + unsigned char GPIO2_IE:1; + unsigned char RESETB:1; + unsigned char PWRONF:1; + unsigned char PWRONR:1; + unsigned char KPSHDN:1; + }bitfield; + unsigned char val; + }irq_enable5; +}; + +#define CHG_EVENT_INACOVP (0x80<<16) +#define CHG_EVENT_INAC_PLUGIN (0x40<<16) +#define CHG_EVENT_INUSBOVP (0x10<<16) +#define CHG_EVENT_INUSB_PLUGIN (0x08<<16) +#define CHG_EVENT_BAT_ABS (0x01<<16) + +#define CHG_EVENT_CHSLPI_INAC (0x80<<8) +#define CHG_EVENT_CHBADI_INAC (0x40<<8) +#define CHG_EVENT_CHGOODI_INAC (0x20<<8) +#define CHG_EVENT_CHSLPI_INUSB (0x10<<8) +#define CHG_EVENT_CHBADI_INUSB (0x08<<8) +#define CHG_EVENT_CHGOODI_INUSB (0x04<<8) +#define CHG_EVENT_CHBATOVI (0x02<<8) +#define CHG_EVENT_CHTERMI (0x01<<8) + +#define CHG_EVENT_CHRCHGI (0x80<<0) +#define CHG_EVENT_CHTREGI (0x40<<0) +#define CHG_EVENT_CHVSREGI (0x20<<0) +#define CHG_EVENT_TIMEOUTPC (0x02<<0) +#define CHG_EVENT_TIMEOUTCC (0x01<<0) + +#define CHARGER_DETECT_MASK (CHG_EVENT_INAC_PLUGIN | CHG_EVENT_INUSB_PLUGIN | \ + CHG_EVENT_CHSLPI_INAC | CHG_EVENT_CHSLPI_INUSB | \ + CHG_EVENT_CHBADI_INAC | CHG_EVENT_CHBADI_INUSB) + +#define PWR_EVENT_OTIQ (0x80<<8) +#define PWR_EVENT_DCDC1LV (0x40<<8) +#define PWR_EVENT_DCDC2LV (0x20<<8) +#define PWR_EVENT_DCDC3LV (0x10<<8) +#define PWR_EVENT_PWRONSP (0x08<<8) +#define PWR_EVENT_PWRONLP (0x04<<8) +#define PWR_EVENT_DCDC4LVHV (0x02<<8) +#define PWR_EVENT_SYSLV (0x01<<8) + +#define PWR_EVENT_KPSHDN (0x80<<0) +#define PWR_EVNET_PWRONR (0x40<<0) +#define PWR_EVENT_PWRONF (0x20<<0) +#define PWR_EVENT_RESETB (0x10<<0) +#define PWR_EVENT_GPIO2IE (0x08<<0) +#define PWR_EVENT_GPIO1IE (0x04<<0) +#define PWR_EVENT_GPIO0IE (0x02<<0) + +struct rt5025_event_callback { + #if 1 + void (*charger_event_callback)(uint32_t detected); + void (*power_event_callkback)(uint32_t detected); + #else + void (*over_temperature_callback)(uint8_t detected); + void (*charging_complete_callback)(void); + void (*over_voltage_callback)(uint8_t detected); + void (*under_voltage_callback)(uint8_t detected); + void (*charge_fault_callback)(uint8_t detected); + void (*charge_warning_callback)(uint8_t detected); + #endif +}; + +struct rt5025_power_info { + struct i2c_client *i2c; + struct device *dev; + struct rt5025_gauge_callbacks *event_callback; + struct power_supply ac; + struct power_supply usb; + unsigned ac_online:1; + unsigned usb_online:1; + unsigned chg_stat:3; +}; + +struct rt5025_chip { + struct i2c_client *i2c; + struct workqueue_struct *wq; + struct device *dev; + struct rt5025_power_info *power_info; + int suspend; + int irq; + struct delayed_work delayed_work; + struct mutex io_lock; +}; + +struct rt5025_platform_data { + struct regulator_init_data* regulator[RT5025_MAX_REGULATOR]; + struct rt5025_power_data* power_data; + struct rt5025_gpio_data* gpio_data; + struct rt5025_misc_data* misc_data; + struct rt5025_irq_data* irq_data; + struct rt5025_event_callback *cb; + int (*pre_init)(struct rt5025_chip *rt5025_chip); + /** Called after subdevices are set up */ + int (*post_init)(void); + int intr_pin; +}; + +#ifdef CONFIG_MFD_RT5025_MISC +extern void rt5025_power_off(void); +#endif /* CONFIG_MFD_RT5025_MISC */ + +#ifdef CONFIG_POWER_RT5025 +extern int rt5025_gauge_init(struct rt5025_power_info *); +extern int rt5025_power_passirq_to_gauge(struct rt5025_power_info *); +extern int rt5025_power_charge_detect(struct rt5025_power_info *); +#endif /* CONFIG_POEWR_RT5025 */ + +extern int rt5025_reg_block_read(struct i2c_client *, int, int, void *); +extern int rt5025_reg_read(struct i2c_client *, int); +extern int rt5025_reg_write(struct i2c_client *, int, unsigned char); +extern int rt5025_assign_bits(struct i2c_client *, int, unsigned char, unsigned char); +extern int rt5025_set_bits(struct i2c_client *, int, unsigned char); +extern int rt5025_clr_bits(struct i2c_client *, int, unsigned char); + +extern int rt5025_core_init(struct rt5025_chip *, struct rt5025_platform_data *); +extern int rt5025_core_deinit(struct rt5025_chip *); + +#ifdef CONFIG_MFD_RT_SHOW_INFO +#define RTINFO(format, args...) \ + printk(KERN_INFO "%s:%s() line-%d: " format, RT5025_DEVICE_NAME,__FUNCTION__,__LINE__, ##args) +#else +#define RTINFO(format,args...) +#endif /* CONFIG_MFD_RT_SHOW_INFO */ + +#endif /* __LINUX_MFD_RT5025_H */ diff --git a/include/linux/power/rt5025-gauge.h b/include/linux/power/rt5025-gauge.h new file mode 100755 index 000000000000..4ca38cbfa6a1 --- /dev/null +++ b/include/linux/power/rt5025-gauge.h @@ -0,0 +1,37 @@ +/* + * rt5025_gauge.h + * fuel-gauge driver + * revision 0.1 + */ + +#ifndef __LINUX_RT5025_GAUGE_H_ +#define __LINUX_RT5025_GAUGE_H_ + +#define GPIO_GAUGE_ALERT 4 + +struct rt5025_gauge_callbacks { + void (*rt5025_gauge_irq_handler)(void); + void (*rt5025_gauge_set_status)(int status); + void (*rt5025_gauge_suspend)(void); + void (*rt5025_gauge_resume)(void); + void (*rt5025_gauge_remove)(void); +}; + + +typedef enum{ + CHG, + DCHG +}operation_mode; + +typedef enum{ + MAXTEMP, + MINTEMP, + MAXVOLT, + MINVOLT1, + MINVOLT2, + TEMP_RLS, + VOLT_RLS, + LAST_TYPE +}alert_type; + +#endif /* #ifndef __LINUX_RT5025_GAUGE_H_ */ diff --git a/include/linux/power/rt5025-power.h b/include/linux/power/rt5025-power.h new file mode 100755 index 000000000000..1700ec7415d6 --- /dev/null +++ b/include/linux/power/rt5025-power.h @@ -0,0 +1,38 @@ +/* + * include/linux/power/rt5025-power.h + * Include header file for Richtek RT5025 Core Power Driver + * + * Copyright (C) 2013 Richtek Electronics + * cy_huang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_RT5025_POWER_H +#define __LINUX_RT5025_POWER_H + +#define RT5025_REG_CHGSTAT 0x01 +#define RT5025_REG_CHGCTL2 0x02 +#define RT5025_REG_CHGCTL3 0x03 +#define RT5025_REG_CHGCTL4 0x04 +#define RT5025_REG_CHGCTL5 0x05 +#define RT5025_REG_CHGCTL6 0x06 +#define RT5025_REG_CHGCTL7 0x07 + +#define RT5025_CHGRST_MASK 0x80 + +#define RT5025_CHGBUCKEN_MASK 0x02 +#define RT5025_CHGCEN_MASK 0x10 + +#define RT5025_CHGSTAT_MASK 0x30 +#define RT5025_CHGSTAT_SHIFT 4 +#define RT5025_CHGSTAT_UNKNOWN 0x04 + +#define RT5025_CHG_ACONLINE 0x02 +#define RT5025_CHG_ACSHIFT 1 +#define RT5025_CHG_USBONLINE 0x01 +#define RT5025_CHG_USBSHIFT 0 + +#endif /* #ifndef __LINUX_RT5025_POWER_H */ diff --git a/include/linux/regulator/rt5025-regulator.h b/include/linux/regulator/rt5025-regulator.h new file mode 100755 index 000000000000..317e3f6a1dc8 --- /dev/null +++ b/include/linux/regulator/rt5025-regulator.h @@ -0,0 +1,76 @@ +/* + * include/linux/regulator/rt5025-regulator.h + * Include header file to Richtek RT5025 Regulator driver + * + * Copyright (C) 2013 Richtek Electronics + * cy_huang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_RT5025_REGULATOR_H +#define __LINUX_RT5025_REGULATOR_H + +#define RT5025_REG_DCDCCTRL1 0x08 +#define RT5025_REG_DCDCCTRL2 0x09 +#define RT5025_REG_DCDCCTRL3 0x0A +#define RT5025_REG_DCDCCTRL4 0x0B +#define RT5025_REG_LDOCTRL1 0x0D +#define RT5025_REG_LDOCTRL2 0x0E +#define RT5025_REG_LDOCTRL3 0x0F +#define RT5025_REG_LDOCTRL4 0x10 +#define RT5025_REG_LDOCTRL5 0x11 +#define RT5025_REG_LDOCTRL6 0x12 +#define RT5025_REG_DCDCEN 0x17 +#define RT5025_REG_LDOEN 0x18 + +#define RT5025_DCDCVOUT1 RT5025_REG_DCDCCTRL1 +#define RT5025_DCDCVOUT2 RT5025_REG_DCDCCTRL2 +#define RT5025_DCDCVOUT3 RT5025_REG_DCDCCTRL3 +#define RT5025_DCDCVOUT4 RT5025_REG_DCDCCTRL4 +#define RT5025_LDOVOUT1 RT5025_REG_LDOCTRL1 +#define RT5025_LDOVOUT2 RT5025_REG_LDOCTRL2 +#define RT5025_LDOVOUT3 RT5025_REG_LDOCTRL3 +#define RT5025_LDOVOUT4 RT5025_REG_LDOCTRL4 +#define RT5025_LDOVOUT5 RT5025_REG_LDOCTRL5 +#define RT5025_LDOVOUT6 RT5025_REG_LDOCTRL6 + +#define RT5025_DCDCVOUT_SHIFT1 2 +#define RT5025_DCDCVOUT_SHIFT2 1 +#define RT5025_DCDCVOUT_SHIFT3 2 +#define RT5025_DCDCVOUT_SHIFT4 0 +#define RT5025_LDOVOUT_SHIFT1 0 +#define RT5025_LDOVOUT_SHIFT2 0 +#define RT5025_LDOVOUT_SHIFT3 3 +#define RT5025_LDOVOUT_SHIFT4 3 +#define RT5025_LDOVOUT_SHIFT5 3 +#define RT5025_LDOVOUT_SHIFT6 3 + +#define RT5025_DCDCVOUT_MASK1 0xFC +#define RT5025_DCDCVOUT_MASK2 0xFE +#define RT5025_DCDCVOUT_MASK3 0xFC +#define RT5025_DCDCVOUT_MASK4 0x0F +#define RT5025_LDOVOUT_MASK1 0x7F +#define RT5025_LDOVOUT_MASK2 0x7F +#define RT5025_LDOVOUT_MASK3 0xF8 +#define RT5025_LDOVOUT_MASK4 0xF8 +#define RT5025_LDOVOUT_MASK5 0xF8 +#define RT5025_LDOVOUT_MASK6 0xF8 + +#define RT5025_DCDC_OUTPUT_EN RT5025_REG_DCDCEN +#define RT5025_LDO_OUTPUT_EN RT5025_REG_LDOEN + +#define RT5025_DCDCEN_MASK1 0x01 +#define RT5025_DCDCEN_MASK2 0x02 +#define RT5025_DCDCEN_MASK3 0x04 +#define RT5025_DCDCEN_MASK4 0x08 +#define RT5025_LDOEN_MASK1 0x01 +#define RT5025_LDOEN_MASK2 0x02 +#define RT5025_LDOEN_MASK3 0x04 +#define RT5025_LDOEN_MASK4 0x08 +#define RT5025_LDOEN_MASK5 0x10 +#define RT5025_LDOEN_MASK6 0x20 + +#endif /* __LINUX_RT5025_REGULATOR_H */