From 356ca54463b296088c6504151e559f427d0c6410 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E5=BC=A0=E6=99=B4?= Date: Wed, 16 Apr 2014 16:23:17 +0800 Subject: [PATCH] rk3288:pmic:rk818:support rk818 regulator and rtc --- arch/arm/boot/dts/rk3288-tb.dts | 168 ++++ arch/arm/boot/dts/rk818.dtsi | 108 +++ drivers/mfd/Kconfig | 9 + drivers/mfd/Makefile | 1 + drivers/mfd/rk818-irq.c | 243 ++++++ drivers/mfd/rk818.c | 1331 +++++++++++++++++++++++++++++++ drivers/rtc/Kconfig | 6 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-rk818.c | 831 +++++++++++++++++++ 9 files changed, 2698 insertions(+) create mode 100644 arch/arm/boot/dts/rk818.dtsi create mode 100755 drivers/mfd/rk818-irq.c create mode 100755 drivers/mfd/rk818.c create mode 100755 drivers/rtc/rtc-rk818.c diff --git a/arch/arm/boot/dts/rk3288-tb.dts b/arch/arm/boot/dts/rk3288-tb.dts index 9ff8b3037ec6..0092e9d0570e 100644 --- a/arch/arm/boot/dts/rk3288-tb.dts +++ b/arch/arm/boot/dts/rk3288-tb.dts @@ -308,6 +308,10 @@ reg = <0x1b>; status = "okay"; }; + rk818: rk818@1c { + reg = <0x1c>; + status = "okay"; + }; syb827b: syb827b@40 { compatible = "silergy,syb827"; reg = <0x40>; @@ -965,6 +969,170 @@ rockchip,power_type = ; }; }; }; +/include/ "rk818.dtsi" +&rk818 { + gpios =<&gpio0 GPIO_A4 GPIO_ACTIVE_HIGH>,<&gpio0 GPIO_B3 GPIO_ACTIVE_LOW>; + rk818,system-power-controller; + + regulators { + + rk818_dcdc1_reg: regulator@0{ + regulator-name= "vdd_logic"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1500000>; + regulator-initial-mode = <0x2>; + regulator-initial-state = <3>; + regulator-state-mem { + regulator-state-mode = <0x2>; + regulator-state-enabled; + regulator-state-uv = <900000>; + }; + }; + + rk818_dcdc2_reg: regulator@1 { + regulator-name= "rk818_dcdc2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = <0x2>; + regulator-initial-state = <3>; + regulator-state-mem { + regulator-state-mode = <0x2>; + regulator-state-enabled; + regulator-state-uv = <1200000>; + }; + }; + + rk818_dcdc3_reg: regulator@2 { + regulator-name= "rk818_dcdc3"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = <0x2>; + regulator-initial-state = <3>; + regulator-state-mem { + regulator-state-mode = <0x2>; + regulator-state-enabled; + regulator-state-uv = <1200000>; + }; + }; + + rk818_dcdc4_reg: regulator@3 { + regulator-name= "vccio"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-initial-mode = <0x2>; + regulator-initial-state = <3>; + regulator-state-mem { + regulator-state-mode = <0x2>; + regulator-state-enabled; + regulator-state-uv = <2800000>; + }; + }; + + rk818_ldo1_reg: regulator@4 { + regulator-name= "rk818_ldo1"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-initial-state = <3>; + regulator-state-mem { + regulator-state-enabled; + regulator-state-uv = <3300000>; + }; + }; + + rk818_ldo2_reg: regulator@5 { + regulator-name= "rk818_ldo2"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-initial-state = <3>; + regulator-state-mem { + regulator-state-enabled; + regulator-state-uv = <3300000>; + }; + }; + + rk818_ldo3_reg: regulator@6 { + regulator-name= "rk818_ldo3"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-initial-state = <3>; + regulator-state-mem { + regulator-state-enabled; + regulator-state-uv = <1000000>; + }; + }; + + rk818_ldo4_reg:regulator@7 { + regulator-name= "rk818_ldo4"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-state = <3>; + regulator-state-mem { + regulator-state-disabled; + regulator-state-uv = <1800000>; + }; + }; + + rk818_ldo5_reg: regulator@8 { + regulator-name= "rk818_ldo5"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-initial-state = <3>; + regulator-state-mem { + regulator-state-enabled; + regulator-state-uv = <3300000>; + }; + }; + + rk818_ldo6_reg: regulator@9 { + regulator-name= "rk818_ldo6"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-initial-state = <3>; + regulator-state-mem { + regulator-state-disabled; + regulator-state-uv = <1000000>; + }; + }; + + rk818_ldo7_reg: regulator@10 { + regulator-name= "rk818_ldo7"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-state = <3>; + regulator-state-mem { + regulator-state-enabled; + regulator-state-uv = <1800000>; + }; + }; + + rk818_ldo8_reg: regulator@11 { + regulator-name= "rk818_ldo8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-state = <3>; + regulator-state-mem { + regulator-state-enabled; + regulator-state-uv = <1800000>; + }; + }; + rk818_ldo9_reg: regulator@12 { + regulator-name= "vcc_sd"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-initial-state = <3>; + regulator-state-mem { + regulator-state-enabled; + regulator-state-uv = <3300000>; + }; + }; + rk818_ldo10_reg: regulator@13 { + regulator-name= "rk818_ldo10"; + regulator-state-mem { + regulator-state-disabled; + }; + }; + }; +}; &lcdc_vdd_domain { regulator-name = "vcc30_lcd"; diff --git a/arch/arm/boot/dts/rk818.dtsi b/arch/arm/boot/dts/rk818.dtsi new file mode 100644 index 000000000000..bc1b5d318135 --- /dev/null +++ b/arch/arm/boot/dts/rk818.dtsi @@ -0,0 +1,108 @@ + +&rk818 { + compatible = "rockchip,rk818"; + + regulators { + #address-cells = <1>; + #size-cells = <0>; + + rk818_dcdc1_reg: regulator@0 { + reg = <0>; + regulator-compatible = "rk818_dcdc1"; + regulator-always-on; + regulator-boot-on; + }; + + rk818_dcdc2_reg: regulator@1 { + reg = <1>; + regulator-compatible = "rk818_dcdc2"; + regulator-always-on; + regulator-boot-on; + }; + + rk818_dcdc3_reg: regulator@2 { + reg = <2>; + regulator-compatible = "rk818_dcdc3"; + regulator-always-on; + regulator-boot-on; + }; + + rk818_dcdc4_reg: regulator@3 { + reg = <3>; + regulator-compatible = "rk818_dcdc4"; + regulator-always-on; + regulator-boot-on; + }; + + rk818_ldo1_reg: regulator@4 { + reg = <4>; + regulator-compatible = "rk818_ldo1"; + regulator-always-on; + regulator-boot-on; + }; + + rk818_ldo2_reg: regulator@5 { + reg = <5>; + regulator-compatible = "rk818_ldo2"; + regulator-always-on; + regulator-boot-on; + }; + + rk818_ldo3_reg: regulator@6 { + reg = <6>; + regulator-compatible = "rk818_ldo3"; + regulator-always-on; + regulator-boot-on; + }; + + rk818_ldo4_reg: regulator@7{ + reg = <7>; + regulator-compatible = "rk818_ldo4"; + regulator-always-on; + regulator-boot-on; + }; + + rk818_ldo5_reg: regulator@8{ + reg = <8>; + regulator-compatible = "rk818_ldo5"; + regulator-always-on; + regulator-boot-on; + }; + + rk818_ldo6_reg: regulator@9{ + reg = <9>; + regulator-compatible = "rk818_ldo6"; + regulator-always-on; + regulator-boot-on; + }; + + rk818_ldo7_reg: regulator@10 { + reg = <10>; + regulator-compatible = "rk818_ldo7"; + regulator-always-on; + regulator-boot-on; + }; + + rk818_ldo8_reg: regulator@11{ + reg = <11>; + regulator-compatible = "rk818_ldo8"; + regulator-always-on; + regulator-boot-on; + }; + + rk818_ldo9_reg: regulator@12{ + reg = <12>; + regulator-compatible = "rk818_ldo9"; + regulator-always-on; + regulator-boot-on; + }; + + rk818_ldo10_reg: regulator@13{ + reg = <13>; + regulator-compatible = "rk818_ldo10"; + regulator-always-on; + regulator-boot-on; + }; + + }; +}; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 2301511ec409..44eac5114383 100755 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -358,6 +358,15 @@ config MFD_RK808 if you say yes here you get support for the RK808 series of Power Management chips. +config MFD_RK818 + bool "RK818 Power Management chip" + depends on I2C=y + select MFD_CORE + select RTC_RK818 + help + if you say yes here you get support for the RK818 series of + Power Management chips. + config EZX_PCAP bool "Motorola EZXPCAP Support" depends on GENERIC_HARDIRQS && SPI_MASTER diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 94150e881d86..160e4d908873 100755 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -145,6 +145,7 @@ obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o obj-$(CONFIG_MFD_TPS65090) += tps65090.o obj-$(CONFIG_MFD_RK808) += rk808.o rk808-irq.o +obj-$(CONFIG_MFD_RK818) += rk818.o rk818-irq.o obj-$(CONFIG_MFD_RICOH619) += ricoh619.o ricoh619-irq.o obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o diff --git a/drivers/mfd/rk818-irq.c b/drivers/mfd/rk818-irq.c new file mode 100755 index 000000000000..8b15cb7700e4 --- /dev/null +++ b/drivers/mfd/rk818-irq.c @@ -0,0 +1,243 @@ +/* + * rk818-irq.c + * + * Author: zhangqing + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static inline int irq_to_rk818_irq(struct rk818 *rk818, + int irq) +{ + return (irq - rk818->chip_irq); +} + +/* + * This is a threaded IRQ handler so can access I2C/SPI. Since all + * interrupts are clear on read the IRQ line will be reasserted and + * the physical IRQ will be handled again if another interrupt is + * asserted while we run - in the normal course of events this is a + * rare occurrence so we save I2C/SPI reads. We're also assuming that + * it's rare to get lots of interrupts firing simultaneously so try to + * minimise I/O. + */ +static irqreturn_t rk818_irq(int irq, void *irq_data) +{ + struct rk818 *rk818 = irq_data; + u32 irq_sts; + u32 irq_mask; + u8 reg; + int i; + //printk(" rk818 irq %d \n",irq); + wake_lock(&rk818->irq_wake); + rk818_i2c_read(rk818, RK818_INT_STS_REG1, 1, ®); + irq_sts = reg; + rk818_i2c_read(rk818, RK818_INT_STS_REG2, 1, ®); + irq_sts |= reg << 8; + + rk818_i2c_read(rk818, RK818_INT_STS_MSK_REG1, 1, ®); + irq_mask = reg; + rk818_i2c_read(rk818, RK818_INT_STS_MSK_REG2, 1, ®); + irq_mask |= reg << 8; + + irq_sts &= ~irq_mask; + + if (!irq_sts) + { + wake_unlock(&rk818->irq_wake); + return IRQ_NONE; + } + + for (i = 0; i < rk818->irq_num; i++) { + + if (!(irq_sts & (1 << i))) + continue; + + handle_nested_irq(rk818->irq_base + i); + } + + /* Write the STS register back to clear IRQs we handled */ + reg = irq_sts & 0xFF; + irq_sts >>= 8; + rk818_i2c_write(rk818, RK818_INT_STS_REG1, 1, reg); + reg = irq_sts & 0xFF; + rk818_i2c_write(rk818, RK818_INT_STS_REG2, 1, reg); + wake_unlock(&rk818->irq_wake); + return IRQ_HANDLED; +} + +static void rk818_irq_lock(struct irq_data *data) +{ + struct rk818 *rk818 = irq_data_get_irq_chip_data(data); + + mutex_lock(&rk818->irq_lock); +} + +static void rk818_irq_sync_unlock(struct irq_data *data) +{ + struct rk818 *rk818 = irq_data_get_irq_chip_data(data); + u32 reg_mask; + u8 reg; + + rk818_i2c_read(rk818, RK818_INT_STS_MSK_REG1, 1, ®); + reg_mask = reg; + rk818_i2c_read(rk818, RK818_INT_STS_MSK_REG2, 1, ®); + reg_mask |= reg << 8; + + if (rk818->irq_mask != reg_mask) { + reg = rk818->irq_mask & 0xff; +// rk818_i2c_write(rk818, RK818_INT_STS_MSK_REG1, 1, reg); + reg = rk818->irq_mask >> 8 & 0xff; +// rk818_i2c_write(rk818, RK818_INT_STS_MSK_REG2, 1, reg); + } + mutex_unlock(&rk818->irq_lock); +} + +static void rk818_irq_enable(struct irq_data *data) +{ + struct rk818 *rk818 = irq_data_get_irq_chip_data(data); + + rk818->irq_mask &= ~( 1 << irq_to_rk818_irq(rk818, data->irq)); +} + +static void rk818_irq_disable(struct irq_data *data) +{ + struct rk818 *rk818 = irq_data_get_irq_chip_data(data); + + rk818->irq_mask |= ( 1 << irq_to_rk818_irq(rk818, data->irq)); +} + +#ifdef CONFIG_PM_SLEEP +static int rk818_irq_set_wake(struct irq_data *data, unsigned int enable) +{ + struct rk818 *rk818 = irq_data_get_irq_chip_data(data); + return irq_set_irq_wake(rk818->chip_irq, enable); +} +#else +#define rk818_irq_set_wake NULL +#endif + +static struct irq_chip rk818_irq_chip = { + .name = "rk818", + .irq_bus_lock = rk818_irq_lock, + .irq_bus_sync_unlock = rk818_irq_sync_unlock, + .irq_disable = rk818_irq_disable, + .irq_enable = rk818_irq_enable, + .irq_set_wake = rk818_irq_set_wake, +}; + +static int rk818_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + struct rk818 *rk818 = d->host_data; + + irq_set_chip_data(irq, rk818); + irq_set_chip_and_handler(irq, &rk818_irq_chip, handle_edge_irq); + irq_set_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif + return 0; +} + +static struct irq_domain_ops rk818_irq_domain_ops = { + .map = rk818_irq_domain_map, +}; + +int rk818_irq_init(struct rk818 *rk818, int irq,struct rk818_board *pdata) +{ + struct irq_domain *domain; + int ret,val,irq_type,flags; + u8 reg; + +// printk("%s,line=%d\n", __func__,__LINE__); + if (!irq) { + dev_warn(rk818->dev, "No interrupt support, no core IRQ\n"); + return 0; + } + + /* Clear unattended interrupts */ + rk818_i2c_read(rk818, RK818_INT_STS_REG1, 1, ®); + rk818_i2c_write(rk818, RK818_INT_STS_REG1, 1, reg); + rk818_i2c_read(rk818, RK818_INT_STS_REG2, 1, ®); + rk818_i2c_write(rk818, RK818_INT_STS_REG2, 1, reg); + rk818_i2c_read(rk818, RK818_RTC_STATUS_REG, 1, ®); + rk818_i2c_write(rk818, RK818_RTC_STATUS_REG, 1, reg);//clear alarm and timer interrupt + + /* Mask top level interrupts */ + rk818->irq_mask = 0xFFFFFF; + mutex_init(&rk818->irq_lock); + wake_lock_init(&rk818->irq_wake, WAKE_LOCK_SUSPEND, "rk818_irq_wake"); + rk818->irq_num = RK818_NUM_IRQ; + rk818->irq_gpio = pdata->irq_gpio; + if (rk818->irq_gpio && !rk818->chip_irq) { + rk818->chip_irq = gpio_to_irq(rk818->irq_gpio); + + if (rk818->irq_gpio) { + ret = gpio_request(rk818->irq_gpio, "rk818_pmic_irq"); + if (ret < 0) { + dev_err(rk818->dev, + "Failed to request gpio %d with ret:" + "%d\n", rk818->irq_gpio, ret); + return IRQ_NONE; + } + gpio_direction_input(rk818->irq_gpio); + val = gpio_get_value(rk818->irq_gpio); + if (val){ + irq_type = IRQ_TYPE_LEVEL_LOW; + flags = IRQF_TRIGGER_FALLING; + } + else{ + irq_type = IRQ_TYPE_LEVEL_HIGH; + flags = IRQF_TRIGGER_RISING; + } + gpio_free(rk818->irq_gpio); + pr_info("%s: rk818_pmic_irq=%x\n", __func__, val); + } + } + + domain = irq_domain_add_linear(NULL, RK818_NUM_IRQ, + &rk818_irq_domain_ops, rk818); + if (!domain) { + dev_err(rk818->dev, "could not create irq domain\n"); + return -ENODEV; + } + rk818->irq_domain = domain; + + ret = request_threaded_irq(rk818->chip_irq, NULL, rk818_irq, flags | IRQF_ONESHOT, "rk818", rk818); + + irq_set_irq_type(rk818->chip_irq, irq_type); + enable_irq_wake(rk818->chip_irq); + if (ret != 0) + dev_err(rk818->dev, "Failed to request IRQ: %d\n", ret); + + return ret; +} + +int rk818_irq_exit(struct rk818 *rk818) +{ + if (rk818->chip_irq) + free_irq(rk818->chip_irq, rk818); + return 0; +} diff --git a/drivers/mfd/rk818.c b/drivers/mfd/rk818.c new file mode 100755 index 000000000000..4b73da29ff96 --- /dev/null +++ b/drivers/mfd/rk818.c @@ -0,0 +1,1331 @@ +/* + * Regulator driver for rk818 PMIC chip for rk31xx + * + * Based on rk818.c that is work by zhangqing + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if 0 +#define DBG(x...) printk(KERN_INFO x) +#else +#define DBG(x...) +#endif +#if 1 +#define DBG_INFO(x...) printk(KERN_INFO x) +#else +#define DBG_INFO(x...) +#endif +#define PM_CONTROL + +struct rk818 *g_rk818; + +static struct mfd_cell rk818s[] = { + { + .name = "rk818-rtc", + }, + /* + { + .name = "rk818-battery", + }, + { + .name = "rk818-power", + }, + */ +}; + +#define BUCK_VOL_MASK 0x3f +#define LDO_VOL_MASK 0x3f +#define LDO9_VOL_MASK 0x1f +#define BOOST_VOL_MASK 0xe0 + +#define VOL_MIN_IDX 0x00 +#define VOL_MAX_IDX 0x3f +#define RK818_I2C_ADDR_RATE 200*1000 + +const static int buck_set_vol_base_addr[] = { + RK818_BUCK1_ON_REG, + RK818_BUCK2_ON_REG, + RK818_BUCK3_CONFIG_REG, + RK818_BUCK4_ON_REG, +}; +const static int buck_set_slp_vol_base_addr[] = { + RK818_BUCK1_SLP_REG, + RK818_BUCK2_SLP_REG, + RK818_BUCK3_CONFIG_REG, + RK818_BUCK4_SLP_VSEL_REG, +}; +const static int buck_contr_base_addr[] = { + RK818_BUCK1_CONFIG_REG, + RK818_BUCK2_CONFIG_REG, + RK818_BUCK3_CONFIG_REG, + RK818_BUCK4_CONFIG_REG, +}; +#define rk818_BUCK_SET_VOL_REG(x) (buck_set_vol_base_addr[x]) +#define rk818_BUCK_CONTR_REG(x) (buck_contr_base_addr[x]) +#define rk818_BUCK_SET_SLP_VOL_REG(x) (buck_set_slp_vol_base_addr[x]) + + +const static int ldo_set_vol_base_addr[] = { + RK818_LDO1_ON_VSEL_REG, + RK818_LDO2_ON_VSEL_REG, + RK818_LDO3_ON_VSEL_REG, + RK818_LDO4_ON_VSEL_REG, + RK818_LDO5_ON_VSEL_REG, + RK818_LDO6_ON_VSEL_REG, + RK818_LDO7_ON_VSEL_REG, + RK818_LDO8_ON_VSEL_REG, + RK818_BOOST_LDO9_ON_VSEL_REG, +}; +const static int ldo_set_slp_vol_base_addr[] = { + RK818_LDO1_SLP_VSEL_REG, + RK818_LDO2_SLP_VSEL_REG, + RK818_LDO3_SLP_VSEL_REG, + RK818_LDO4_SLP_VSEL_REG, + RK818_LDO5_SLP_VSEL_REG, + RK818_LDO6_SLP_VSEL_REG, + RK818_LDO7_SLP_VSEL_REG, + RK818_LDO8_SLP_VSEL_REG, + RK818_BOOST_LDO9_SLP_VSEL_REG, +}; + +#define rk818_LDO_SET_VOL_REG(x) (ldo_set_vol_base_addr[x]) +#define rk818_LDO_SET_SLP_VOL_REG(x) (ldo_set_slp_vol_base_addr[x]) + +const static int buck_voltage_map[] = { + 700, 712, 725, 737, 750, 762, 775, 787, 800, + 812, 825, 837, 850,862, 875, 887, 900, 912, + 925, 937, 950, 962, 975, 987, 1000, 1012, 1025, + 1037, 1050,1062, 1075, 1087, 1100, 1112, 1125, 1137, + 1150,1162, 1175, 1187, 1200, 1212, 1225, 1237, 1250, + 1262, 1275, 1287, 1300, 1312, 1325, 1337, 1350,1362, + 1375, 1387, 1400, 1412, 1425, 1437, 1450,1462, 1475, + 1487, 1500, +}; + +const static int buck4_voltage_map[] = { + 1800, 1900, 2000, 2100, 2200, 2300, 2400, 2500, 2600, + 2700, 2800, 2900, 3000, 3100, 3200,3300, 3400,3500,3600, +}; + +const static int ldo_voltage_map[] = { + 1800, 1900, 2000, 2100, 2200, 2300, 2400, 2500, 2600, + 2700, 2800, 2900, 3000, 3100, 3200,3300, 3400, +}; +const static int ldo3_voltage_map[] = { + 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600, + 1700, 1800, 1900, 2000,2100, 2200, 2500, +}; +const static int ldo6_voltage_map[] = { + 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600, + 1700, 1800, 1900, 2000,2100, 2200, 2300,2400,2500, +}; +const static int boost_voltage_map[] = { + 4700,4800,4900,5000,5100,5200,5300,5400, +}; + +static int rk818_ldo_list_voltage(struct regulator_dev *dev, unsigned index) +{ + int ldo= rdev_get_id(dev) - RK818_LDO1; + if (ldo == 2){ + if (index >= ARRAY_SIZE(ldo3_voltage_map)) + return -EINVAL; + return 1000 * ldo3_voltage_map[index]; + } + else if (ldo == 5 || ldo ==6){ + if (index >= ARRAY_SIZE(ldo6_voltage_map)) + return -EINVAL; + return 1000 * ldo6_voltage_map[index]; + } + else{ + if (index >= ARRAY_SIZE(ldo_voltage_map)) + return -EINVAL; + return 1000 * ldo_voltage_map[index]; + } +} +static int rk818_ldo_is_enabled(struct regulator_dev *dev) +{ + struct rk818 *rk818 = rdev_get_drvdata(dev); + int ldo= rdev_get_id(dev) - RK818_LDO1; + u16 val; + + if (ldo == 8){ + val = rk818_reg_read(rk818, RK818_DCDC_EN_REG); //ldo9 + if (val < 0) + return val; + if (val & (1 << 5)) + return 1; + else + return 0; + } + val = rk818_reg_read(rk818, RK818_LDO_EN_REG); + if (val < 0) + return val; + if (val & (1 << ldo)) + return 1; + else + return 0; +} +static int rk818_ldo_enable(struct regulator_dev *dev) +{ + struct rk818 *rk818 = rdev_get_drvdata(dev); + int ldo= rdev_get_id(dev) - RK818_LDO1; + + if (ldo == 8) + rk818_set_bits(rk818, RK818_DCDC_EN_REG, 1 << 5, 1 << 5); //ldo9 + else if (ldo ==9) + rk818_set_bits(rk818, RK818_DCDC_EN_REG, 1 << 6, 1 << 6); //ldo10 switch + else + rk818_set_bits(rk818, RK818_LDO_EN_REG, 1 << ldo, 1 << ldo); + + return 0; +} +static int rk818_ldo_disable(struct regulator_dev *dev) +{ + struct rk818 *rk818 = rdev_get_drvdata(dev); + int ldo= rdev_get_id(dev) - RK818_LDO1; + + if (ldo == 8) + rk818_set_bits(rk818, RK818_DCDC_EN_REG, 1 << 5, 1 << 0); //ldo9 + else if(ldo ==9) + rk818_set_bits(rk818, RK818_DCDC_EN_REG, 1 << 6, 1 << 0); //ldo10 switch + else + rk818_set_bits(rk818, RK818_LDO_EN_REG, 1 << ldo, 0); + + return 0; +} +static int rk818_ldo_get_voltage(struct regulator_dev *dev) +{ + struct rk818 *rk818 = rdev_get_drvdata(dev); + int ldo= rdev_get_id(dev) - RK818_LDO1; + u16 reg = 0; + int val; + + if (ldo ==9){ + reg = rk818_reg_read(rk818,rk818_BUCK_SET_VOL_REG(3)); + reg &= BUCK_VOL_MASK; + val = 1000 * buck4_voltage_map[reg]; + } + else{ + reg = rk818_reg_read(rk818,rk818_LDO_SET_VOL_REG(ldo)); + if (ldo == 8){ + reg &= LDO9_VOL_MASK; + } + else + reg &= LDO_VOL_MASK; + + if (ldo ==2){ + val = 1000 * ldo3_voltage_map[reg]; + } + else if (ldo == 5 || ldo ==6){ + val = 1000 * ldo6_voltage_map[reg]; + } + else{ + val = 1000 * ldo_voltage_map[reg]; + } + } + return val; +} +static int rk818_ldo_suspend_enable(struct regulator_dev *dev) +{ + struct rk818 *rk818 = rdev_get_drvdata(dev); + int ldo= rdev_get_id(dev) - RK818_LDO1; + + if (ldo == 8) + return rk818_set_bits(rk818, RK818_SLEEP_SET_OFF_REG1, 1 << 5, 0); //ldo9 + else if (ldo ==9) + return rk818_set_bits(rk818, RK818_SLEEP_SET_OFF_REG1, 1 << 6, 0); //ldo10 switch + else + return rk818_set_bits(rk818, RK818_SLEEP_SET_OFF_REG2, 1 << ldo, 0); + +} +static int rk818_ldo_suspend_disable(struct regulator_dev *dev) +{ + struct rk818 *rk818 = rdev_get_drvdata(dev); + int ldo= rdev_get_id(dev) - RK818_LDO1; + + if (ldo == 8) + return rk818_set_bits(rk818, RK818_SLEEP_SET_OFF_REG1, 1 << 5, 1 << 5); //ldo9 + else if (ldo ==9) + return rk818_set_bits(rk818, RK818_SLEEP_SET_OFF_REG1, 1 << 6, 1 << 6); //ldo10 switch + else + return rk818_set_bits(rk818, RK818_SLEEP_SET_OFF_REG2, 1 << ldo, 1 << ldo); + +} +static int rk818_ldo_set_sleep_voltage(struct regulator_dev *dev, + int uV) +{ + struct rk818 *rk818 = rdev_get_drvdata(dev); + int ldo= rdev_get_id(dev) - RK818_LDO1; + const int *vol_map = ldo_voltage_map; + int min_vol = uV / 1000; + u16 val; + int ret = 0,num =0; + + if (ldo ==2){ + vol_map = ldo3_voltage_map; + num = 15; + } + else if (ldo == 5 || ldo ==6){ + vol_map = ldo6_voltage_map; + num = 17; + } + else { + num = 16; + } + + if (min_vol < vol_map[0] || + min_vol > vol_map[num]) + return -EINVAL; + + for (val = 0; val <= num; val++){ + if (vol_map[val] >= min_vol) + break; + } + + if (ldo == 8){ + ret = rk818_set_bits(rk818, rk818_LDO_SET_SLP_VOL_REG(ldo),LDO9_VOL_MASK, val); + } + else + ret = rk818_set_bits(rk818, rk818_LDO_SET_SLP_VOL_REG(ldo),LDO_VOL_MASK, val); + + return ret; +} + +static int rk818_ldo_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV,unsigned *selector) +{ + struct rk818 *rk818 = rdev_get_drvdata(dev); + int ldo= rdev_get_id(dev) - RK818_LDO1; + const int *vol_map; + int min_vol = min_uV / 1000; + u16 val; + int ret = 0,num =0; + + if (ldo ==2){ + vol_map = ldo3_voltage_map; + num = 15; + } + else if (ldo == 5 || ldo ==6){ + vol_map = ldo6_voltage_map; + num = 17; + } + else { + vol_map = ldo_voltage_map; + num = 16; + } + + if (min_vol < vol_map[0] || + min_vol > vol_map[num]) + return -EINVAL; + + for (val = 0; val <= num; val++){ + if (vol_map[val] >= min_vol) + break; + } + + if (ldo == 8){ + ret = rk818_set_bits(rk818, rk818_LDO_SET_VOL_REG(ldo),LDO9_VOL_MASK, val); + } + else + ret = rk818_set_bits(rk818, rk818_LDO_SET_VOL_REG(ldo),LDO_VOL_MASK, val); + + return ret; + +} + +static struct regulator_ops rk818_ldo_ops = { + .set_voltage = rk818_ldo_set_voltage, + .get_voltage = rk818_ldo_get_voltage, + .list_voltage = rk818_ldo_list_voltage, + .is_enabled = rk818_ldo_is_enabled, + .enable = rk818_ldo_enable, + .disable = rk818_ldo_disable, + .set_suspend_enable =rk818_ldo_suspend_enable, + .set_suspend_disable =rk818_ldo_suspend_disable, + .set_suspend_voltage = rk818_ldo_set_sleep_voltage, +}; + +static int rk818_dcdc_list_voltage(struct regulator_dev *dev, unsigned selector) +{ + int volt; + int buck = rdev_get_id(dev) - RK818_DCDC1; + + if (selector < 0x0 ||selector > BUCK_VOL_MASK ) + return -EINVAL; + + switch (buck) { + case 0: + case 1: + volt = 700000 + selector * 12500; + break; + case 3: + volt = 1800000 + selector * 100000; + break; + case 2: + volt = 1200000; + break; + default: + BUG(); + return -EINVAL; + } + + return volt ; +} +static int rk818_dcdc_is_enabled(struct regulator_dev *dev) +{ + struct rk818 *rk818 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - RK818_DCDC1; + u16 val; + + val = rk818_reg_read(rk818, RK818_DCDC_EN_REG); + if (val < 0) + return val; + if (val & (1 << buck)) + return 1; + else + return 0; +} +static int rk818_dcdc_enable(struct regulator_dev *dev) +{ + struct rk818 *rk818 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - RK818_DCDC1; + + return rk818_set_bits(rk818, RK818_DCDC_EN_REG, 1 << buck, 1 << buck); + +} +static int rk818_dcdc_disable(struct regulator_dev *dev) +{ + struct rk818 *rk818 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - RK818_DCDC1; + + return rk818_set_bits(rk818, RK818_DCDC_EN_REG, 1 << buck , 0); +} +static int rk818_dcdc_get_voltage(struct regulator_dev *dev) +{ + struct rk818 *rk818 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - RK818_DCDC1; + u16 reg = 0; + int val; + + reg = rk818_reg_read(rk818,rk818_BUCK_SET_VOL_REG(buck)); + + reg &= BUCK_VOL_MASK; + val = rk818_dcdc_list_voltage(dev,reg); + return val; +} +static int rk818_dcdc_select_min_voltage(struct regulator_dev *dev, + int min_uV, int max_uV ,int buck) +{ + u16 vsel =0; + + if (buck == 0 || buck == 1){ + if (min_uV < 700000) + vsel = 0; + else if (min_uV <= 1500000) + vsel = ((min_uV - 700000) / 12500) ; + else + return -EINVAL; + } + else if (buck ==3){ + if (min_uV < 1800000) + vsel = 0; + else if (min_uV <= 3300000) + vsel = ((min_uV - 1800000) / 100000) ; + else + return -EINVAL; + } + if (rk818_dcdc_list_voltage(dev, vsel) > max_uV) + return -EINVAL; + return vsel; +} + +static int rk818_dcdc_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV,unsigned *selector) +{ + struct rk818 *rk818 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - RK818_DCDC1; + u16 val; + int ret = 0,old_voltage =0,vol_temp =0; + + if (buck ==2){ + return 0; + }else if (buck==3){ + val = rk818_dcdc_select_min_voltage(dev,min_uV,max_uV,buck); + ret = rk818_set_bits(rk818, rk818_BUCK_SET_VOL_REG(buck), BUCK_VOL_MASK, val); + } + val = rk818_dcdc_select_min_voltage(dev,min_uV,max_uV,buck); + ret = rk818_set_bits(rk818, rk818_BUCK_SET_VOL_REG(buck), BUCK_VOL_MASK, val); + return ret; +} +static int rk818_dcdc_set_sleep_voltage(struct regulator_dev *dev, + int uV) +{ + struct rk818 *rk818 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - RK818_DCDC1; + u16 val; + int ret = 0; + + if (buck ==2){ + return 0; + }else{ + val = rk818_dcdc_select_min_voltage(dev,uV,uV,buck); + ret = rk818_set_bits(rk818, rk818_BUCK_SET_SLP_VOL_REG(buck) , BUCK_VOL_MASK, val); + } + return ret; +} +static unsigned int rk818_dcdc_get_mode(struct regulator_dev *dev) +{ + struct rk818 *rk818 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - RK818_DCDC1; + u16 mask = 0x80; + u16 val; + val = rk818_reg_read(rk818, rk818_BUCK_SET_VOL_REG(buck)); + if (val < 0) { + return val; + } + val=val & mask; + if (val== mask) + return REGULATOR_MODE_FAST; + else + return REGULATOR_MODE_NORMAL; + +} +static int rk818_dcdc_set_mode(struct regulator_dev *dev, unsigned int mode) +{ + struct rk818 *rk818 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - RK818_DCDC1; + u16 mask = 0x80; + switch(mode) + { + case REGULATOR_MODE_FAST: + return rk818_set_bits(rk818, rk818_BUCK_SET_VOL_REG(buck), mask, mask); + case REGULATOR_MODE_NORMAL: + return rk818_set_bits(rk818, rk818_BUCK_SET_VOL_REG(buck), mask, 0); + default: + printk("error:pmu_rk818 only powersave and pwm mode\n"); + return -EINVAL; + } + +} +static int rk818_dcdc_set_voltage_time_sel(struct regulator_dev *dev, unsigned int old_selector, + unsigned int new_selector) +{ + int old_volt, new_volt; + + old_volt = rk818_dcdc_list_voltage(dev, old_selector); + if (old_volt < 0) + return old_volt; + + new_volt = rk818_dcdc_list_voltage(dev, new_selector); + if (new_volt < 0) + return new_volt; + + return DIV_ROUND_UP(abs(old_volt - new_volt)*2, 2500); +} + +static int rk818_dcdc_suspend_enable(struct regulator_dev *dev) +{ + struct rk818 *rk818 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - RK818_DCDC1; + + return rk818_set_bits(rk818, RK818_SLEEP_SET_OFF_REG1, 1 << buck, 0); + +} +static int rk818_dcdc_suspend_disable(struct regulator_dev *dev) +{ + struct rk818 *rk818 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - RK818_DCDC1; + + return rk818_set_bits(rk818, RK818_SLEEP_SET_OFF_REG1, 1 << buck , 1 << buck); +} +static int rk818_dcdc_set_suspend_mode(struct regulator_dev *dev, unsigned int mode) +{ + struct rk818 *rk818 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - RK818_DCDC1; + u16 mask = 0x80; + + switch(mode) + { + case REGULATOR_MODE_FAST: + return rk818_set_bits(rk818, (rk818_BUCK_SET_VOL_REG(buck) + 0x01), mask, mask); + case REGULATOR_MODE_NORMAL: + return rk818_set_bits(rk818, (rk818_BUCK_SET_VOL_REG(buck) + 0x01), mask, 0); + default: + printk("error:pmu_rk818 only powersave and pwm mode\n"); + return -EINVAL; + } + +} +static struct regulator_ops rk818_dcdc_ops = { + .set_voltage = rk818_dcdc_set_voltage, + .get_voltage = rk818_dcdc_get_voltage, + .list_voltage= rk818_dcdc_list_voltage, + .is_enabled = rk818_dcdc_is_enabled, + .enable = rk818_dcdc_enable, + .disable = rk818_dcdc_disable, + .get_mode = rk818_dcdc_get_mode, + .set_mode = rk818_dcdc_set_mode, + .set_suspend_enable =rk818_dcdc_suspend_enable, + .set_suspend_disable =rk818_dcdc_suspend_disable, + .set_suspend_mode = rk818_dcdc_set_suspend_mode, + .set_suspend_voltage = rk818_dcdc_set_sleep_voltage, + .set_voltage_time_sel = rk818_dcdc_set_voltage_time_sel, +}; +static struct regulator_desc regulators[] = { + + { + .name = "RK818_DCDC1", + .id = 0, + .ops = &rk818_dcdc_ops, + .n_voltages = ARRAY_SIZE(buck_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "RK818_DCDC2", + .id = 1, + .ops = &rk818_dcdc_ops, + .n_voltages = ARRAY_SIZE(buck_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "RK818_DCDC3", + .id = 2, + .ops = &rk818_dcdc_ops, + .n_voltages = ARRAY_SIZE(buck4_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "RK818_DCDC4", + .id = 3, + .ops = &rk818_dcdc_ops, + .n_voltages = ARRAY_SIZE(buck4_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + + { + .name = "RK818_LDO1", + .id =4, + .ops = &rk818_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "RK818_LDO2", + .id = 5, + .ops = &rk818_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "RK818_LDO3", + .id = 6, + .ops = &rk818_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo3_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "RK818_LDO4", + .id = 7, + .ops = &rk818_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + + { + .name = "RK818_LDO5", + .id =8, + .ops = &rk818_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "RK818_LDO6", + .id = 9, + .ops = &rk818_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo6_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "RK818_LDO7", + .id = 10, + .ops = &rk818_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo6_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "RK818_LDO8", + .id = 11, + .ops = &rk818_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "RK818_LDO9", + .id = 12, + .ops = &rk818_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "RK818_LDO10", + .id = 13, + .ops = &rk818_ldo_ops, + .n_voltages = ARRAY_SIZE(buck4_voltage_map), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + +}; + +/* + * + */ + int rk818_i2c_read(struct rk818 *rk818, char reg, int count,u8 *dest) +{ + struct i2c_client *i2c = rk818->i2c; + + int ret; + struct i2c_adapter *adap; + struct i2c_msg msgs[2]; + + if(!i2c) + return ret; + + if (count != 1) + return -EIO; + + adap = i2c->adapter; + + msgs[0].addr = i2c->addr; + msgs[0].buf = ® + msgs[0].flags = 0; + msgs[0].len = 1; + msgs[0].scl_rate = 200*1000; + + msgs[1].buf = dest; + msgs[1].addr = i2c->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = count; + msgs[1].scl_rate = RK818_I2C_ADDR_RATE; + + ret = i2c_transfer(adap, msgs, 2); + + DBG("***run in %s %x % x\n",__FUNCTION__,i2c->addr,msgs[0].buf); + return 0; +} + +int rk818_i2c_write(struct rk818 *rk818, char reg, int count, const u8 src) +{ + int ret=-1; + struct i2c_client *i2c = rk818->i2c; + struct i2c_adapter *adap; + struct i2c_msg msg; + char tx_buf[2]; + + if(!i2c) + return ret; + if (count != 1) + return -EIO; + + adap = i2c->adapter; + tx_buf[0] = reg; + tx_buf[1] = src; + + msg.addr = i2c->addr; + msg.buf = &tx_buf[0]; + msg.len = 1 +1; + msg.flags = i2c->flags; + msg.scl_rate = RK818_I2C_ADDR_RATE; + + ret = i2c_transfer(adap, &msg, 1); + return ret; +} + +u8 rk818_reg_read(struct rk818 *rk818, u8 reg) +{ + u8 val = 0; + + mutex_lock(&rk818->io_lock); + + rk818_i2c_read(rk818, reg, 1, &val); + + DBG("reg read 0x%02x -> 0x%02x\n", (int)reg, (unsigned)val&0xff); + + mutex_unlock(&rk818->io_lock); + + return val & 0xff; +} +EXPORT_SYMBOL_GPL(rk818_reg_read); + +int rk818_reg_write(struct rk818 *rk818, u8 reg, u8 val) +{ + int err =0; + + mutex_lock(&rk818->io_lock); + + err = rk818_i2c_write(rk818, reg, 1,val); + if (err < 0) + dev_err(rk818->dev, "Write for reg 0x%x failed\n", reg); + + mutex_unlock(&rk818->io_lock); + return err; +} +EXPORT_SYMBOL_GPL(rk818_reg_write); + + int rk818_set_bits(struct rk818 *rk818, u8 reg, u8 mask, u8 val) +{ + u8 tmp; + int ret; + + mutex_lock(&rk818->io_lock); + + ret = rk818_i2c_read(rk818, reg, 1, &tmp); + DBG("1 reg read 0x%02x -> 0x%02x\n", (int)reg, (unsigned)tmp&0xff); + tmp = (tmp & ~mask) | val; + if (ret == 0) { + ret = rk818_i2c_write(rk818, reg, 1, tmp); + DBG("reg write 0x%02x -> 0x%02x\n", (int)reg, (unsigned)val&0xff); + } + rk818_i2c_read(rk818, reg, 1, &tmp); + DBG("2 reg read 0x%02x -> 0x%02x\n", (int)reg, (unsigned)tmp&0xff); + mutex_unlock(&rk818->io_lock); + + return 0;//ret; +} +EXPORT_SYMBOL_GPL(rk818_set_bits); + +int rk818_clear_bits(struct rk818 *rk818, u8 reg, u8 mask) +{ + u8 data; + int err; + + mutex_lock(&rk818->io_lock); + err = rk818_i2c_read(rk818, reg, 1, &data); + if (err <0) { + dev_err(rk818->dev, "read from reg %x failed\n", reg); + goto out; + } + + data &= ~mask; + err = rk818_i2c_write(rk818, reg, 1, data); + if (err <0) + dev_err(rk818->dev, "write to reg %x failed\n", reg); + +out: + mutex_unlock(&rk818->io_lock); + return err; +} +EXPORT_SYMBOL_GPL(rk818_clear_bits); + +#if 1 +static ssize_t rk818_test_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n) +{ + u32 getdata[8]; + u16 regAddr; + u8 data; + char cmd; + const char *buftmp = buf; + struct rk818 *rk818 = g_rk818; + /** + * W Addr(8Bit) regAddr(8Bit) data0(8Bit) data1(8Bit) data2(8Bit) data3(8Bit) + * :data can be less than 4 byte + * R regAddr(8Bit) + * C gpio_name(poweron/powerhold/sleep/boot0/boot1) value(H/L) + */ + regAddr = (u16)(getdata[0] & 0xff); + if (strncmp(buf, "start", 5) == 0) { + + + } else if (strncmp(buf, "stop", 4== 0) ){ + + } else{ + sscanf(buftmp, "%c ", &cmd); + printk("------zhangqing: get cmd = %c\n", cmd); + switch(cmd) { + + case 'w': + sscanf(buftmp, "%c %x %x ", &cmd, &getdata[0],&getdata[1]); + regAddr = (u16)(getdata[0] & 0xff); + data = (u8)(getdata[1] & 0xff); + printk("get value = %x\n", data); + + rk818_i2c_write(rk818, regAddr, 1, data); + rk818_i2c_read(rk818, regAddr, 1, &data); + printk("%x %x\n", getdata[1],data); + + break; + + case 'r': + sscanf(buftmp, "%c %x ", &cmd, &getdata[0]); + printk("CMD : %c %x\n", cmd, getdata[0]); + + regAddr = (u16)(getdata[0] & 0xff); + rk818_i2c_read(rk818, regAddr, 1, &data); + printk("%x %x\n", getdata[0],data); + + break; + + default: + printk("Unknown command\n"); + break; + } +} + return n; + +} +static ssize_t rk818_test_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + char *s = buf; + buf = "hello"; + return sprintf(s, "%s\n", buf); + +} + +static struct kobject *rk818_kobj; +struct rk818_attribute { + struct attribute attr; + ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr, + char *buf); + ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n); +}; + +static struct rk818_attribute rk818_attrs[] = { + /* node_name permision show_func store_func */ + __ATTR(rk818_test, S_IRUGO | S_IWUSR, rk818_test_show, rk818_test_store), +}; +#endif + +extern void rk_send_wakeup_key(void); +static irqreturn_t rk818_vbat_lo_irq(int irq, void *data) +{ + printk("rk818 vbat low %s:irq=%d\n",__func__,irq); + rk818_set_bits(g_rk818,0x4c,(0x1 << 1),(0x1 <<1)); + rk_send_wakeup_key(); + return IRQ_HANDLED; +} + +#ifdef CONFIG_OF +static struct of_device_id rk818_of_match[] = { + { .compatible = "rockchip,rk818"}, + { }, +}; +MODULE_DEVICE_TABLE(of, rk818_of_match); +#endif + +#ifdef CONFIG_OF +static struct of_regulator_match rk818_reg_matches[] = { + { .name = "rk818_dcdc1", .driver_data = (void *)0 }, + { .name = "rk818_dcdc2", .driver_data = (void *)1 }, + { .name = "rk818_dcdc3", .driver_data = (void *)2 }, + { .name = "rk818_dcdc4", .driver_data = (void *)3 }, + { .name = "rk818_ldo1", .driver_data = (void *)4 }, + { .name = "rk818_ldo2", .driver_data = (void *)5 }, + { .name = "rk818_ldo3", .driver_data = (void *)6 }, + { .name = "rk818_ldo4", .driver_data = (void *)7 }, + { .name = "rk818_ldo5", .driver_data = (void *)8 }, + { .name = "rk818_ldo6", .driver_data = (void *)9 }, + { .name = "rk818_ldo7", .driver_data = (void *)10 }, + { .name = "rk818_ldo8", .driver_data = (void *)11 }, + { .name = "rk818_ldo9", .driver_data = (void *)12 }, + { .name = "rk818_ldo10", .driver_data = (void *)13 }, +}; + +static struct rk818_board *rk818_parse_dt(struct rk818 *rk818) +{ + struct rk818_board *pdata; + struct device_node *regs,*rk818_pmic_np; + int i, count; + + rk818_pmic_np = of_node_get(rk818->dev->of_node); + if (!rk818_pmic_np) { + printk("could not find pmic sub-node\n"); + return NULL; + } + + regs = of_find_node_by_name(rk818_pmic_np, "regulators"); + if (!regs) + return NULL; + + count = of_regulator_match(rk818->dev, regs, rk818_reg_matches, + rk818_NUM_REGULATORS); + of_node_put(regs); + if ((count < 0) || (count > rk818_NUM_REGULATORS)) + return NULL; + + pdata = devm_kzalloc(rk818->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + for (i = 0; i < count; i++) { + if (!rk818_reg_matches[i].init_data || !rk818_reg_matches[i].of_node) + continue; + + pdata->rk818_init_data[i] = rk818_reg_matches[i].init_data; + pdata->of_node[i] = rk818_reg_matches[i].of_node; + } + pdata->irq = rk818->chip_irq; + pdata->irq_base = -1; + + pdata->irq_gpio = of_get_named_gpio(rk818_pmic_np,"gpios",0); + if (!gpio_is_valid(pdata->irq_gpio)) { + printk("invalid gpio: %d\n", pdata->irq_gpio); + return NULL; + } + + pdata->pmic_sleep_gpio = of_get_named_gpio(rk818_pmic_np,"gpios",1); + if (!gpio_is_valid(pdata->pmic_sleep_gpio)) { + printk("invalid gpio: %d\n", pdata->pmic_sleep_gpio); + } + pdata->pmic_sleep = true; + pdata->pm_off = of_property_read_bool(rk818_pmic_np,"rk818,system-power-controller"); + + return pdata; +} + +#else +static struct rk818_board *rk818_parse_dt(struct i2c_client *i2c) +{ + return NULL; +} +#endif + +int rk818_device_shutdown(void) +{ + int ret; + int err = -1; + struct rk818 *rk818 = g_rk818; + + printk("%s\n",__func__); + ret = rk818_set_bits(rk818, RK818_INT_STS_MSK_REG1,(0x3<<5),(0x3<<5)); //close rtc int when power off + ret = rk818_clear_bits(rk818, RK818_RTC_INT_REG,(0x3<<2)); //close rtc int when power off + ret = rk818_reg_read(rk818,RK818_DEVCTRL_REG); + ret = rk818_set_bits(rk818, RK818_DEVCTRL_REG,(0x1<<0),(0x1<<0)); +// ret = rk818_set_bits(rk818, RK818_DEVCTRL_REG,(0x1<<4),(0x1<<4)); + if (ret < 0) { + printk("rk818 power off error!\n"); + return err; + } + return 0; +} +EXPORT_SYMBOL_GPL(rk818_device_shutdown); + +__weak void rk818_device_suspend(void) {} +__weak void rk818_device_resume(void) {} +#ifdef CONFIG_PM +static int rk818_suspend(struct i2c_client *i2c, pm_message_t mesg) +{ + rk818_device_suspend(); + return 0; +} + +static int rk818_resume(struct i2c_client *i2c) +{ + rk818_device_resume(); + return 0; +} +#else +static int rk818_suspend(struct i2c_client *i2c, pm_message_t mesg) +{ + return 0; +} + +static int rk818_resume(struct i2c_client *i2c) +{ + return 0; +} +#endif + +static int rk818_pre_init(struct rk818 *rk818) +{ + int ret,val; + printk("%s,line=%d\n", __func__,__LINE__); + + ret = rk818_set_bits(rk818, 0xa1,(0x7<<4),(0x7<<4)); //close charger when usb low then 3.4V + ret = rk818_set_bits(rk818, 0x52,(0x1<<1),(0x1<<1)); //no action when vref + + /*******enable switch and boost***********/ + val = rk818_reg_read(rk818,RK818_DCDC_EN_REG); + val |= (0x3 << 5); //enable switch1/2 + val |= (0x1 << 4); //enable boost + ret = rk818_reg_write(rk818,RK818_DCDC_EN_REG,val); + if (ret <0) { + printk(KERN_ERR "Unable to write RK818_DCDC_EN_REG reg\n"); + return ret; + } + /****************************************/ + + /****************set vbat low **********/ + val = rk818_reg_read(rk818,RK818_VB_MON_REG); + val &=(~(VBAT_LOW_VOL_MASK | VBAT_LOW_ACT_MASK)); + val |= (RK818_VBAT_LOW_3V5 | EN_VBAT_LOW_IRQ); + ret = rk818_reg_write(rk818,RK818_VB_MON_REG,val); + if (ret <0) { + printk(KERN_ERR "Unable to write RK818_VB_MON_REG reg\n"); + return ret; + } + /**************************************/ + + /**********mask int****************/ + val = rk818_reg_read(rk818,RK818_INT_STS_MSK_REG1); + val |= (0x1<<0); //mask vout_lo_int + ret = rk818_reg_write(rk818,RK818_INT_STS_MSK_REG1,val); + if (ret <0) { + printk(KERN_ERR "Unable to write RK818_INT_STS_MSK_REG1 reg\n"); + return ret; + } + /**********************************/ + /**********enable clkout2****************/ + ret = rk818_reg_write(rk818,RK818_CLK32OUT_REG,0x01); + if (ret <0) { + printk(KERN_ERR "Unable to write RK818_CLK32OUT_REG reg\n"); + return ret; + } + /**********************************/ + ret = rk818_clear_bits(rk818, RK818_INT_STS_MSK_REG1,(0x3<<5)); //open rtc int when power on + ret = rk818_set_bits(rk818, RK818_RTC_INT_REG,(0x1<<3),(0x1<<3)); //open rtc int when power on + return 0; +} + +static int rk818_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + struct rk818 *rk818; + struct rk818_board *pdev; + const struct of_device_id *match; + struct regulator_config config = { }; + struct regulator_dev *rk818_rdev; + struct regulator_init_data *reg_data; + const char *rail_name = NULL; + int ret,vlow_irq,i=0; + + printk("%s,line=%d\n", __func__,__LINE__); + + if (i2c->dev.of_node) { + match = of_match_device(rk818_of_match, &i2c->dev); + if (!match) { + dev_err(&i2c->dev,"Failed to find matching dt id\n"); + return -EINVAL; + } + } + + rk818 = devm_kzalloc(&i2c->dev,sizeof(struct rk818), GFP_KERNEL); + if (rk818 == NULL) { + ret = -ENOMEM; + goto err; + } + rk818->i2c = i2c; + rk818->dev = &i2c->dev; + i2c_set_clientdata(i2c, rk818); + + mutex_init(&rk818->io_lock); + + ret = rk818_reg_read(rk818,0x2f); + if ((ret < 0) || (ret == 0xff)){ + printk("The device is not rk818 %d\n",ret); + goto err; + } + + ret = rk818_pre_init(rk818); + if (ret < 0){ + printk("The rk818_pre_init failed %d\n",ret); + goto err; + } + + if (rk818->dev->of_node) + pdev = rk818_parse_dt(rk818); + + /******************************set sleep vol & dcdc mode******************/ + #ifdef CONFIG_OF + rk818->pmic_sleep_gpio = pdev->pmic_sleep_gpio; + if (rk818->pmic_sleep_gpio) { + ret = gpio_request(rk818->pmic_sleep_gpio, "rk818_pmic_sleep"); + if (ret < 0) { + dev_err(rk818->dev,"Failed to request gpio %d with ret:""%d\n", rk818->pmic_sleep_gpio, ret); + return IRQ_NONE; + } + gpio_direction_output(rk818->pmic_sleep_gpio,0); + ret = gpio_get_value(rk818->pmic_sleep_gpio); + gpio_free(rk818->pmic_sleep_gpio); + pr_info("%s: rk818_pmic_sleep=%x\n", __func__, ret); + } + #endif + /**********************************************************/ + + if (pdev) { + rk818->num_regulators = rk818_NUM_REGULATORS; + rk818->rdev = kcalloc(rk818_NUM_REGULATORS,sizeof(struct regulator_dev *), GFP_KERNEL); + if (!rk818->rdev) { + return -ENOMEM; + } + /* Instantiate the regulators */ + for (i = 0; i < rk818_NUM_REGULATORS; i++) { + reg_data = pdev->rk818_init_data[i]; + if (!reg_data) + continue; + config.dev = rk818->dev; + config.driver_data = rk818; + if (rk818->dev->of_node) + config.of_node = pdev->of_node[i]; + if (reg_data && reg_data->constraints.name) + rail_name = reg_data->constraints.name; + else + rail_name = regulators[i].name; + reg_data->supply_regulator = rail_name; + + config.init_data =reg_data; + + rk818_rdev = regulator_register(®ulators[i],&config); + if (IS_ERR(rk818_rdev)) { + printk("failed to register %d regulator\n",i); + goto err; + } + rk818->rdev[i] = rk818_rdev; + } + } + + rk818->irq_gpio = pdev->irq_gpio; + ret = rk818_irq_init(rk818, rk818->irq_gpio, pdev); + if (ret < 0) + goto err; + + ret = mfd_add_devices(rk818->dev, -1, + rk818s, ARRAY_SIZE(rk818s), + NULL, 0,NULL); + #if 0 + /********************vbat low int**************/ + vlow_irq = irq_create_mapping(rk818->irq_domain, RK818_IRQ_VB_LO); + ret = devm_request_threaded_irq(&i2c->dev,vlow_irq, NULL, rk818_vbat_lo_irq, + IRQF_ONESHOT, "rk818_vbatlow", + rk818); + if (ret != 0) { + dev_err(rk818->dev, "Failed to request periodic IRQ %d: %d\n", + vlow_irq+ RK818_IRQ_VB_LO, ret); + + } + #endif + /*********************************************/ + + g_rk818 = rk818; + if (pdev->pm_off && !pm_power_off) { + pm_power_off = rk818_device_shutdown; + } + + #if 1 + rk818_kobj = kobject_create_and_add("rk818", NULL); + if (!rk818_kobj) + return -ENOMEM; + for (i = 0; i < ARRAY_SIZE(rk818_attrs); i++) { + ret = sysfs_create_file(rk818_kobj, &rk818_attrs[i].attr); + if (ret != 0) { + printk("create index %d error\n", i); + return ret; + } + } + #endif + + return 0; + +err: + mfd_remove_devices(rk818->dev); + return ret; + +} + +static int rk818_i2c_remove(struct i2c_client *i2c) +{ + struct rk818 *rk818 = i2c_get_clientdata(i2c); + int i; + + for (i = 0; i < rk818->num_regulators; i++) + if (rk818->rdev[i]) + regulator_unregister(rk818->rdev[i]); + i2c_set_clientdata(i2c, NULL); + kfree(rk818); + + return 0; +} + +static const struct i2c_device_id rk818_i2c_id[] = { + { "rk818", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, rk818_i2c_id); + +static struct i2c_driver rk818_i2c_driver = { + .driver = { + .name = "rk818", + .owner = THIS_MODULE, + .of_match_table =of_match_ptr(rk818_of_match), + }, + .probe = rk818_i2c_probe, + .remove = rk818_i2c_remove, + .id_table = rk818_i2c_id, + #ifdef CONFIG_PM + .suspend = rk818_suspend, + .resume = rk818_resume, + #endif +}; + +static int __init rk818_module_init(void) +{ + int ret; + ret = i2c_add_driver(&rk818_i2c_driver); + if (ret != 0) + pr_err("Failed to register I2C driver: %d\n", ret); + return ret; +} + +subsys_initcall_sync(rk818_module_init); + +static void __exit rk818_module_exit(void) +{ + i2c_del_driver(&rk818_i2c_driver); +} +module_exit(rk818_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("zhangqing "); +MODULE_DESCRIPTION("rk818 PMIC driver"); + diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 33b6eb48a266..65138a897514 100755 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -420,6 +420,12 @@ config RK808_RTC help enable rk808 rtc for system +config RK818_RTC + tristate "rk818 rtc for rk" + depends on MFD_RK818 + help + enable rk818 rtc for system + config RTC_DRV_TPS80031 tristate "TI TPS80031/TPS80032 RTC driver" depends on MFD_TPS80031 diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 8cd446a70387..c2b838e98980 100755 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -127,6 +127,7 @@ obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o obj-$(CONFIG_RTC_DRV_VT8500) += rtc-vt8500.o obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o obj-$(CONFIG_RK808_RTC) += rtc-rk808.o +obj-$(CONFIG_RK818_RTC) += rtc-rk818.o obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o obj-$(CONFIG_RTC_DRV_RC5T619) += rtc-ricoh619.o diff --git a/drivers/rtc/rtc-rk818.c b/drivers/rtc/rtc-rk818.c new file mode 100755 index 000000000000..29082ef9c921 --- /dev/null +++ b/drivers/rtc/rtc-rk818.c @@ -0,0 +1,831 @@ +/* + * Real Time Clock driver for rk818 + * + * Author: zhangqing + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* RTC Definitions */ +/* RTC_CTRL_REG bitfields */ +#define BIT_RTC_CTRL_REG_STOP_RTC_M 0x01 +#define BIT_RTC_CTRL_REG_ROUND_30S_M 0x02 +#define BIT_RTC_CTRL_REG_AUTO_COMP_M 0x04 +#define BIT_RTC_CTRL_REG_MODE_12_24_M 0x08 +#define BIT_RTC_CTRL_REG_TEST_MODE_M 0x10 +#define BIT_RTC_CTRL_REG_SET_32_COUNTER_M 0x20 +#define BIT_RTC_CTRL_REG_GET_TIME_M 0x40 +#define BIT_RTC_CTRL_REG_RTC_V_OPT_M 0x80 + +/* RTC_STATUS_REG bitfields */ +#define BIT_RTC_STATUS_REG_RUN_M 0x02 +#define BIT_RTC_STATUS_REG_1S_EVENT_M 0x04 +#define BIT_RTC_STATUS_REG_1M_EVENT_M 0x08 +#define BIT_RTC_STATUS_REG_1H_EVENT_M 0x10 +#define BIT_RTC_STATUS_REG_1D_EVENT_M 0x20 +#define BIT_RTC_STATUS_REG_ALARM_M 0x40 +#define BIT_RTC_STATUS_REG_POWER_UP_M 0x80 + +/* RTC_INTERRUPTS_REG bitfields */ +#define BIT_RTC_INTERRUPTS_REG_EVERY_M 0x03 +#define BIT_RTC_INTERRUPTS_REG_IT_TIMER_M 0x04 +#define BIT_RTC_INTERRUPTS_REG_IT_ALARM_M 0x08 + +/* DEVCTRL bitfields */ +#define BIT_RTC_PWDN 0x40 + +/* REG_SECONDS_REG through REG_YEARS_REG is how many registers? */ +#define ALL_TIME_REGS 7 +#define ALL_ALM_REGS 6 + + +#define RTC_SET_TIME_RETRIES 5 +#define RTC_GET_TIME_RETRIES 5 + + +struct rk818_rtc { + struct rk818 *rk818; + struct rtc_device *rtc; + unsigned int alarm_enabled:1; +}; + +/* + * Read current time and date in RTC + */ +static int rk818_rtc_readtime(struct device *dev, struct rtc_time *tm) +{ + struct rk818_rtc *rk818_rtc = dev_get_drvdata(dev); + struct rk818 *rk818 = rk818_rtc->rk818; + int ret; + int count = 0; + unsigned char rtc_data[ALL_TIME_REGS + 1]; + u8 rtc_ctl; + + /*Dummy read*/ + ret = rk818_reg_read(rk818, RK818_RTC_CTRL_REG); + + /* Has the RTC been programmed? */ + ret = rk818_reg_read(rk818, RK818_RTC_CTRL_REG); + if (ret < 0) { + dev_err(dev, "Failed to read RTC control: %d\n", ret); + return ret; + } + + rtc_ctl = ret & (~BIT_RTC_CTRL_REG_RTC_V_OPT_M); + + ret = rk818_reg_write(rk818, RK818_RTC_CTRL_REG, rtc_ctl); + if (ret < 0) { + dev_err(dev, "Failed to write RTC control: %d\n", ret); + return ret; + } + +#if 0 + /* Read twice to make sure we don't read a corrupt, partially + * incremented, value. + */ + do { + ret = rk818_bulk_read(rk818, RK818_SECONDS_REG, + ALL_TIME_REGS, rtc_data); + if (ret != 0) + continue; + + tm->tm_sec = bcd2bin(rtc_data[0]); + tm->tm_min = bcd2bin(rtc_data[1]); + tm->tm_hour = bcd2bin(rtc_data[2]) ; + tm->tm_mday = bcd2bin(rtc_data[3]); + tm->tm_mon = bcd2bin(rtc_data[4]) - 1; + tm->tm_year = bcd2bin(rtc_data[5]) + 100; + tm->tm_wday = bcd2bin(rtc_data[6]); + + printk( "RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, tm->tm_wday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + return ret; + + } while (++count < RTC_GET_TIME_RETRIES); + dev_err(dev, "Timed out reading current time\n"); +#else + rtc_data[0] = rk818_reg_read(rk818,0x00); + rtc_data[1] = rk818_reg_read(rk818,0x01); + rtc_data[2] = rk818_reg_read(rk818,0x02); + rtc_data[3] = rk818_reg_read(rk818,0x03); + rtc_data[4] = rk818_reg_read(rk818,0x04); + rtc_data[5] = rk818_reg_read(rk818,0x05); + rtc_data[6] = rk818_reg_read(rk818,0x06); + + tm->tm_sec = bcd2bin(rtc_data[0]); + tm->tm_min = bcd2bin(rtc_data[1]); + tm->tm_hour = bcd2bin(rtc_data[2]) ; + tm->tm_mday = bcd2bin(rtc_data[3]); + tm->tm_mon = bcd2bin(rtc_data[4]) - 1; + tm->tm_year = bcd2bin(rtc_data[5]) + 100; + tm->tm_wday = bcd2bin(rtc_data[6]); + + dev_dbg(dev, "RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, tm->tm_wday,tm->tm_hour , tm->tm_min, tm->tm_sec); + +#endif + return 0; + +} + +/* + * Set current time and date in RTC + */ +static int rk818_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rk818_rtc *rk818_rtc = dev_get_drvdata(dev); + struct rk818 *rk818 = rk818_rtc->rk818; + int ret; + u8 rtc_ctl; + unsigned char rtc_data[ALL_TIME_REGS + 1]; + + rtc_data[0] = bin2bcd(tm->tm_sec); + rtc_data[1] = bin2bcd(tm->tm_min); + rtc_data[2] = bin2bcd(tm->tm_hour ); + rtc_data[3] = bin2bcd(tm->tm_mday); + rtc_data[4] = bin2bcd(tm->tm_mon + 1); + rtc_data[5] = bin2bcd(tm->tm_year - 100); + rtc_data[6] = bin2bcd(tm->tm_wday); + + dev_dbg(dev, "set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, tm->tm_wday,tm->tm_hour , tm->tm_min, tm->tm_sec); + + /*Dummy read*/ + ret = rk818_reg_read(rk818, RK818_RTC_CTRL_REG); + + /* Stop RTC while updating the TC registers */ + ret = rk818_reg_read(rk818, RK818_RTC_CTRL_REG); + if (ret < 0) { + dev_err(dev, "Failed to read RTC control: %d\n", ret); + return ret; + } + + rtc_ctl = ret | (BIT_RTC_CTRL_REG_STOP_RTC_M); + + ret = rk818_reg_write(rk818, RK818_RTC_CTRL_REG, rtc_ctl); + if (ret < 0) { + dev_err(dev, "Failed to write RTC control: %d\n", ret); + return ret; + } +#if 0 + /* update all the time registers in one shot */ + ret = rk818_bulk_write(rk818, RK818_SECONDS_REG, + ALL_TIME_REGS, rtc_data); + if (ret < 0) { + dev_err(dev, "Failed to read RTC times: %d\n", ret); + return ret; + } +#else + rk818_reg_write(rk818,0x00,rtc_data[0]); + rk818_reg_write(rk818,0x01,rtc_data[1]); + rk818_reg_write(rk818,0x02,rtc_data[2]); + rk818_reg_write(rk818,0x03,rtc_data[3]); + rk818_reg_write(rk818,0x04,rtc_data[4]); + rk818_reg_write(rk818,0x05,rtc_data[5]); + rk818_reg_write(rk818,0x06,rtc_data[6]); + +#endif + /*Dummy read*/ + ret = rk818_reg_read(rk818, RK818_RTC_CTRL_REG); + + /* Start RTC again */ + ret = rk818_reg_read(rk818, RK818_RTC_CTRL_REG); + if (ret < 0) { + dev_err(dev, "Failed to read RTC control: %d\n", ret); + return ret; + } + + rtc_ctl = ret &(~ BIT_RTC_CTRL_REG_STOP_RTC_M); + + ret = rk818_reg_write(rk818, RK818_RTC_CTRL_REG, rtc_ctl); + if (ret < 0) { + dev_err(dev, "Failed to write RTC control: %d\n", ret); + return ret; + } + + return 0; +} + +/* + * Read alarm time and date in RTC + */ +static int rk818_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rk818_rtc *rk818_rtc = dev_get_drvdata(dev); + int ret; + unsigned char alrm_data[ALL_ALM_REGS + 1]; +#if 0 + ret = rk818_bulk_read(rk818_rtc->rk818, RK818_ALARM_SECONDS_REG, + ALL_ALM_REGS, alrm_data); + if (ret != 0) { + dev_err(dev, "Failed to read alarm time: %d\n", ret); + return ret; + } +#else + alrm_data[0] = rk818_reg_read(rk818_rtc->rk818,0x08); + alrm_data[1] = rk818_reg_read(rk818_rtc->rk818,0x09); + alrm_data[2] = rk818_reg_read(rk818_rtc->rk818,0x0a); + alrm_data[3] = rk818_reg_read(rk818_rtc->rk818,0x0b); + alrm_data[4] = rk818_reg_read(rk818_rtc->rk818,0x0c); + alrm_data[5] = rk818_reg_read(rk818_rtc->rk818,0x0d); + + +#endif + /* some of these fields may be wildcard/"match all" */ + alrm->time.tm_sec = bcd2bin(alrm_data[0]); + alrm->time.tm_min = bcd2bin(alrm_data[1]); + alrm->time.tm_hour = bcd2bin(alrm_data[2]); + alrm->time.tm_mday = bcd2bin(alrm_data[3]); + alrm->time.tm_mon = bcd2bin(alrm_data[4]) - 1; + alrm->time.tm_year = bcd2bin(alrm_data[5]) + 100; + + ret = rk818_reg_read(rk818_rtc->rk818, RK818_RTC_INT_REG); + if (ret < 0) { + dev_err(dev, "Failed to read RTC control: %d\n", ret); + return ret; + } + dev_dbg(dev,"alrm read RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + alrm->time.tm_year, alrm->time.tm_mon + 1, alrm->time.tm_mday, alrm->time.tm_wday, alrm->time.tm_hour, alrm->time.tm_min, alrm->time.tm_sec); + + + + if (ret & BIT_RTC_INTERRUPTS_REG_IT_ALARM_M) + alrm->enabled = 1; + else + alrm->enabled = 0; + + return 0; +} + +static int rk818_rtc_stop_alarm(struct rk818_rtc *rk818_rtc) +{ + rk818_rtc->alarm_enabled = 0; + + return rk818_clear_bits(rk818_rtc->rk818, RK818_RTC_INT_REG, + BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); + +} + +static int rk818_rtc_start_alarm(struct rk818_rtc *rk818_rtc) +{ + rk818_rtc->alarm_enabled = 1; + + return rk818_set_bits(rk818_rtc->rk818, RK818_RTC_INT_REG, + BIT_RTC_INTERRUPTS_REG_IT_ALARM_M,BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); + +} + +static int rk818_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rk818_rtc *rk818_rtc = dev_get_drvdata(dev); + int ret; + unsigned char alrm_data[ALL_TIME_REGS + 1]; + + ret = rk818_rtc_stop_alarm(rk818_rtc); + if (ret < 0) { + dev_err(dev, "Failed to stop alarm: %d\n", ret); + return ret; + } + + dev_dbg(dev,"alrm set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + alrm->time.tm_year, alrm->time.tm_mon + 1, alrm->time.tm_mday, alrm->time.tm_wday, alrm->time.tm_hour, alrm->time.tm_min, alrm->time.tm_sec); + + alrm_data[0] = bin2bcd(alrm->time.tm_sec); + alrm_data[1] = bin2bcd(alrm->time.tm_min); + alrm_data[2] = bin2bcd(alrm->time.tm_hour ); + alrm_data[3] = bin2bcd(alrm->time.tm_mday); + alrm_data[4] = bin2bcd(alrm->time.tm_mon + 1); + alrm_data[5] = bin2bcd(alrm->time.tm_year - 100); +#if 0 + ret = rk818_bulk_write(rk818_rtc->rk818, RK818_ALARM_SECONDS_REG, + ALL_ALM_REGS, alrm_data); + if (ret != 0) { + dev_err(dev, "Failed to read alarm time: %d\n", ret); + return ret; + } +#else + rk818_reg_write(rk818_rtc->rk818,0x08,alrm_data[0]); + rk818_reg_write(rk818_rtc->rk818,0x09,alrm_data[1]); + rk818_reg_write(rk818_rtc->rk818,0x0a,alrm_data[2]); + rk818_reg_write(rk818_rtc->rk818,0x0b,alrm_data[3]); + rk818_reg_write(rk818_rtc->rk818,0x0c,alrm_data[4]); + rk818_reg_write(rk818_rtc->rk818,0x0d,alrm_data[5]); + +#endif + if (alrm->enabled) { + ret = rk818_rtc_start_alarm(rk818_rtc); + if (ret < 0) { + dev_err(dev, "Failed to start alarm: %d\n", ret); + return ret; + } + } + + return 0; +} + +static int rk818_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct rk818_rtc *rk818_rtc = dev_get_drvdata(dev); + + if (enabled) + return rk818_rtc_start_alarm(rk818_rtc); + else + return rk818_rtc_stop_alarm(rk818_rtc); +} + +static int rk818_rtc_update_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct rk818_rtc *rk818_rtc = dev_get_drvdata(dev); + + if (enabled) + return rk818_set_bits(rk818_rtc->rk818, RK818_RTC_INT_REG, + BIT_RTC_INTERRUPTS_REG_IT_TIMER_M,BIT_RTC_INTERRUPTS_REG_IT_TIMER_M); + else + return rk818_clear_bits(rk818_rtc->rk818, RK818_RTC_INT_REG, + BIT_RTC_INTERRUPTS_REG_IT_TIMER_M); +} + +/* + * We will just handle setting the frequency and make use the framework for + * reading the periodic interupts. + * + * @freq: Current periodic IRQ freq: + * bit 0: every second + * bit 1: every minute + * bit 2: every hour + * bit 3: every day + */ +static int rk818_rtc_irq_set_freq(struct device *dev, int freq) +{ + struct rk818_rtc *rk818_rtc = dev_get_drvdata(dev); + int ret; + u8 rtc_ctl; + + if (freq < 0 || freq > 3) + return -EINVAL; + + ret = rk818_reg_read(rk818_rtc->rk818, RK818_RTC_INT_REG); + if (ret < 0) { + dev_err(dev, "Failed to read RTC interrupt: %d\n", ret); + return ret; + } + + rtc_ctl = ret | freq; + + ret = rk818_reg_write(rk818_rtc->rk818, RK818_RTC_INT_REG, rtc_ctl); + if (ret < 0) { + dev_err(dev, "Failed to write RTC control: %d\n", ret); + return ret; + } + + return ret; +} + +static irqreturn_t rk818_alm_irq(int irq, void *data) +{ + struct rk818_rtc *rk818_rtc = data; + int ret; + u8 rtc_ctl; + + /*Dummy read -- mandatory for status register*/ + ret = rk818_reg_read(rk818_rtc->rk818, RK818_RTC_STATUS_REG); + if (ret < 0) { + printk("%s:Failed to read RTC status: %d\n", __func__, ret); + return ret; + } + + ret = rk818_reg_read(rk818_rtc->rk818, RK818_RTC_STATUS_REG); + if (ret < 0) { + printk("%s:Failed to read RTC status: %d\n", __func__, ret); + return ret; + } + rtc_ctl = ret&0xff; + + //The alarm interrupt keeps its low level, until the micro-controller write 1 in the ALARM bit of the RTC_STATUS_REG register. + ret = rk818_reg_write(rk818_rtc->rk818, RK818_RTC_STATUS_REG,rtc_ctl); + if (ret < 0) { + printk("%s:Failed to read RTC status: %d\n", __func__, ret); + return ret; + } + + rtc_update_irq(rk818_rtc->rtc, 1, RTC_IRQF | RTC_AF); + + printk("%s:irq=%d,rtc_ctl=0x%x\n",__func__,irq,rtc_ctl); + return IRQ_HANDLED; +} + +static irqreturn_t rk818_per_irq(int irq, void *data) +{ + struct rk818_rtc *rk818_rtc = data; + + rtc_update_irq(rk818_rtc->rtc, 1, RTC_IRQF | RTC_UF); + + //printk("%s:irq=%d\n",__func__,irq); + return IRQ_HANDLED; +} + +static const struct rtc_class_ops rk818_rtc_ops = { + .read_time = rk818_rtc_readtime, + //.set_mmss = rk818_rtc_set_mmss, + .set_time = rk818_rtc_set_time, + .read_alarm = rk818_rtc_readalarm, + .set_alarm = rk818_rtc_setalarm, + .alarm_irq_enable = rk818_rtc_alarm_irq_enable, + //.update_irq_enable = rk818_rtc_update_irq_enable, + //.irq_set_freq = rk818_rtc_irq_set_freq, +}; + +#ifdef CONFIG_PM +/* Turn off the alarm if it should not be a wake source. */ +static int rk818_rtc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rk818_rtc *rk818_rtc = dev_get_drvdata(&pdev->dev); + int ret; + + if (rk818_rtc->alarm_enabled && device_may_wakeup(&pdev->dev)) + ret = rk818_rtc_start_alarm(rk818_rtc); + else + ret = rk818_rtc_stop_alarm(rk818_rtc); + + if (ret != 0) + dev_err(&pdev->dev, "Failed to update RTC alarm: %d\n", ret); + + return 0; +} + +/* Enable the alarm if it should be enabled (in case it was disabled to + * prevent use as a wake source). + */ +static int rk818_rtc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rk818_rtc *rk818_rtc = dev_get_drvdata(&pdev->dev); + int ret; + + if (rk818_rtc->alarm_enabled) { + ret = rk818_rtc_start_alarm(rk818_rtc); + if (ret != 0) + dev_err(&pdev->dev, + "Failed to restart RTC alarm: %d\n", ret); + } + + return 0; +} + +/* Unconditionally disable the alarm */ +static int rk818_rtc_freeze(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rk818_rtc *rk818_rtc = dev_get_drvdata(&pdev->dev); + int ret; + + ret = rk818_rtc_stop_alarm(rk818_rtc); + if (ret != 0) + dev_err(&pdev->dev, "Failed to stop RTC alarm: %d\n", ret); + + return 0; +} +#else +#define rk818_rtc_suspend NULL +#define rk818_rtc_resume NULL +#define rk818_rtc_freeze NULL +#endif +extern struct rk818 *g_rk818; +struct platform_device *rk818_pdev; +struct rtc_time rk818_tm_def = { // 2012.1.1 12:00:00 Saturday + .tm_wday = 6, + .tm_year = 112, + .tm_mon = 0, + .tm_mday = 1, + .tm_hour = 12, + .tm_min = 0, + .tm_sec = 0, +}; + +static int rk818_rtc_probe(struct platform_device *pdev) +{ + struct rk818 *rk818 = dev_get_drvdata(pdev->dev.parent); + struct rk818_rtc *rk818_rtc; + struct rtc_time tm; + int per_irq; + int alm_irq; + int ret = 0; + u8 rtc_ctl; + + printk("%s,line=%d\n", __func__,__LINE__); + + rk818_rtc = devm_kzalloc(&pdev->dev,sizeof(*rk818_rtc), GFP_KERNEL); + if (rk818_rtc == NULL) + return -ENOMEM; + + platform_set_drvdata(pdev, rk818_rtc); + rk818_rtc->rk818 = rk818; + + /* Take rtc out of reset */ + /* + ret = rk818_reg_read(rk818, RK818_DEVCTRL); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to read RK818_DEVCTRL: %d\n", ret); + return ret; + } + + if(ret & BIT_RTC_PWDN) + { + rtc_ctl = ret & (~BIT_RTC_PWDN); + + ret = rk818_reg_write(rk818, RK818_DEVCTRL, rtc_ctl); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to write RTC control: %d\n", ret); + return ret; + } + } + */ + /*start rtc default*/ + ret = rk818_reg_read(rk818, RK818_RTC_CTRL_REG); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to read RTC control: %d\n", ret); + return ret; + } + rtc_ctl = ret & (~BIT_RTC_CTRL_REG_STOP_RTC_M); + + ret = rk818_reg_write(rk818, RK818_RTC_CTRL_REG, rtc_ctl); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to write RTC control: %d\n", ret); + return ret; + } + + ret = rk818_reg_read(rk818, RK818_RTC_STATUS_REG); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to read RTC status: %d\n", ret); + return ret; + } + rk818_reg_write(rk818,RK818_RTC_STATUS_REG,0xfe); + /*set init time*/ + + ret = rk818_rtc_readtime(&pdev->dev, &tm); + if (ret<0) + { + dev_err(&pdev->dev, "Failed to read RTC time\n"); + return ret; + } + + ret = rtc_valid_tm(&tm); + if (ret) { + dev_err(&pdev->dev,"invalid date/time and init time\n"); + rk818_rtc_set_time(&pdev->dev, &rk818_tm_def); // 2012-01-01 12:00:00 +// DBG( "set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",1900 + tm_def.tm_year, tm_def.tm_mon + 1, tm_def.tm_mday, tm_def.tm_wday,tm_def.tm_hour, tm_def.tm_min, tm_def.tm_sec); + } + + device_init_wakeup(&pdev->dev, 1); + + rk818_rtc->rtc = rtc_device_register("rk818", &pdev->dev, + &rk818_rtc_ops, THIS_MODULE); + if (IS_ERR(rk818_rtc->rtc)) { + ret = PTR_ERR(rk818_rtc->rtc); + goto err; + } + + per_irq = irq_create_mapping(rk818->irq_domain, RK818_IRQ_RTC_PERIOD); + alm_irq = irq_create_mapping(rk818->irq_domain, RK818_IRQ_RTC_ALARM); + + /*request rtc and alarm irq of rk818*/ + ret = devm_request_threaded_irq(rk818->dev,per_irq, NULL, rk818_per_irq, + IRQF_TRIGGER_RISING, "RTC period", + rk818_rtc); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request periodic IRQ %d: %d\n", + per_irq, ret); + } + + ret = devm_request_threaded_irq(rk818->dev,alm_irq, NULL, rk818_alm_irq, + IRQF_TRIGGER_RISING, "RTC alarm", + rk818_rtc); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n", + alm_irq, ret); + } + + //for rtc irq test + /* + rk818_set_bits(rk818_rtc->rk818, RK818_RTC_STATUS_REG,(0x1<< 6),(0x1 <<6)); + rk818_set_bits(rk818_rtc->rk818, RK818_RTC_INT_REG,0x0c,0x0c); + rk818_set_bits(rk818_rtc->rk818,RK818_INT_STS_REG1,(0x3 << 5),(0x3 <<5)); + rk818_set_bits(rk818_rtc->rk818, RK818_INT_STS_MSK_REG1,(0x3 <<5),0); +*/ + +// enable_irq_wake(alm_irq); // so rk818 alarm irq can wake up system + rk818_pdev = pdev; + + printk("%s:ok\n",__func__); + + return 0; + +err: + return ret; +} + +static int rk818_rtc_remove(struct platform_device *pdev) +{ + struct rk818_rtc *rk818_rtc = platform_get_drvdata(pdev); + int per_irq = rk818_rtc->rk818->irq_base + RK818_IRQ_RTC_PERIOD; + int alm_irq = rk818_rtc->rk818->irq_base + RK818_IRQ_RTC_ALARM; + + free_irq(alm_irq, rk818_rtc); + free_irq(per_irq, rk818_rtc); + rtc_device_unregister(rk818_rtc->rtc); + + return 0; +} + +static const struct dev_pm_ops rk818_rtc_pm_ops = { + .suspend = rk818_rtc_suspend, + .resume = rk818_rtc_resume, + + .freeze = rk818_rtc_freeze, + .thaw = rk818_rtc_resume, + .restore = rk818_rtc_resume, + + .poweroff = rk818_rtc_suspend, +}; + +static struct platform_driver rk818_rtc_driver = { + .probe = rk818_rtc_probe, + .remove = rk818_rtc_remove, + .driver = { + .name = "rk818-rtc", + .pm = &rk818_rtc_pm_ops, + }, +}; + +static ssize_t rtc_rk818_test_write(struct file *file, + const char __user *buf, size_t count, loff_t *offset) +{ + char nr_buf[8]; + int nr = 0, ret; + struct platform_device *pdev; + struct rtc_time tm; + struct rtc_wkalrm alrm; + struct rk818_rtc *rk818_rtc; + + if(count > 3) + return -EFAULT; + ret = copy_from_user(nr_buf, buf, count); + if(ret < 0) + return -EFAULT; + + sscanf(nr_buf, "%d", &nr); + if(nr > 5 || nr < 0) + { + printk("%s:data is error\n",__func__); + return -EFAULT; + } + + if(!rk818_pdev) + return -EFAULT; + else + pdev = rk818_pdev; + + + rk818_rtc = dev_get_drvdata(&pdev->dev); + + //test rtc time + if(nr == 0) + { + tm.tm_wday = 6; + tm.tm_year = 111; + tm.tm_mon = 0; + tm.tm_mday = 1; + tm.tm_hour = 12; + tm.tm_min = 0; + tm.tm_sec = 0; + + ret = rk818_rtc_set_time(&pdev->dev, &tm); // 2011-01-01 12:00:00 + if (ret) + { + dev_err(&pdev->dev, "Failed to set RTC time\n"); + return -EFAULT; + } + + } + + /*set init time*/ + ret = rk818_rtc_readtime(&pdev->dev, &tm); + if (ret) + dev_err(&pdev->dev, "Failed to read RTC time\n"); + else + dev_info(&pdev->dev, "RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + tm.tm_year, tm.tm_mon + 1, tm.tm_mday, tm.tm_wday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + + if(!ret) + printk("%s:ok\n",__func__); + else + printk("%s:error\n",__func__); + + + //test rtc alarm + if(nr == 2) + { + //2000-01-01 00:00:30 + if(tm.tm_sec < 30) + { + alrm.time.tm_sec = tm.tm_sec+30; + alrm.time.tm_min = tm.tm_min; + } + else + { + alrm.time.tm_sec = tm.tm_sec-30; + alrm.time.tm_min = tm.tm_min+1; + } + alrm.time.tm_hour = tm.tm_hour; + alrm.time.tm_mday = tm.tm_mday; + alrm.time.tm_mon = tm.tm_mon; + alrm.time.tm_year = tm.tm_year; + rk818_rtc_alarm_irq_enable(&pdev->dev, 1); + rk818_rtc_setalarm(&pdev->dev, &alrm); + + dev_info(&pdev->dev, "Set alarm %4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + alrm.time.tm_year, alrm.time.tm_mon + 1, alrm.time.tm_mday, alrm.time.tm_wday, + alrm.time.tm_hour, alrm.time.tm_min, alrm.time.tm_sec); + } + + + if(nr == 3) + { + ret = rk818_reg_read(rk818_rtc->rk818, RK818_RTC_STATUS_REG); + if (ret < 0) { + printk("%s:Failed to read RTC status: %d\n", __func__, ret); + return ret; + } + printk("%s:ret=0x%x\n",__func__,ret&0xff); + + ret = rk818_reg_write(rk818_rtc->rk818, RK818_RTC_STATUS_REG, ret&0xff); + if (ret < 0) { + printk("%s:Failed to read RTC status: %d\n", __func__, ret); + return ret; + } + } + + if(nr == 4) + rk818_rtc_update_irq_enable(&pdev->dev, 1); + + if(nr == 5) + rk818_rtc_update_irq_enable(&pdev->dev, 0); + + return count; +} + +static const struct file_operations rtc_rk818_test_fops = { + .write = rtc_rk818_test_write, +}; + +static struct miscdevice rtc_rk818_test_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "rtc_rk818_test", + .fops = &rtc_rk818_test_fops, +}; + + +static int __init rk818_rtc_init(void) +{ + misc_register(&rtc_rk818_test_misc); + return platform_driver_register(&rk818_rtc_driver); +} +subsys_initcall_sync(rk818_rtc_init); + +static void __exit rk818_rtc_exit(void) +{ + misc_deregister(&rtc_rk818_test_misc); + platform_driver_unregister(&rk818_rtc_driver); +} +module_exit(rk818_rtc_exit); + +MODULE_DESCRIPTION("RTC driver for the rk818 series PMICs"); +MODULE_AUTHOR("ZHANGQING "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rk818-rtc"); -- 2.34.1