From 2eedcbfc0612c87e22c6325fde49ecf140e5873a Mon Sep 17 00:00:00 2001 From: Wadim Egorov Date: Mon, 29 Aug 2016 13:07:58 +0200 Subject: mfd: rk808: Add RK818 support The RK818 chip is a Power Management IC (PMIC) for multimedia and handheld devices. It contains the following components: - Regulators - RTC - Clocking - Battery support Both RK808 and RK818 chips are using a similar register map, so we can reuse the RTC and Clocking functionality. Signed-off-by: Wadim Egorov Tested-by: Andy Yan Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 4 +- drivers/mfd/rk808.c | 226 +++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 197 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 2d1fb6420592..a55be9520b76 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -852,13 +852,13 @@ config MFD_RC5T583 different functionality of the device. config MFD_RK808 - tristate "Rockchip RK808 Power Management chip" + tristate "Rockchip RK808/RK818 Power Management Chip" depends on I2C && OF select MFD_CORE select REGMAP_I2C select REGMAP_IRQ help - If you say yes here you get support for the RK808 + If you say yes here you get support for the RK808 and RK818 Power Management chips. This driver provides common support for accessing the device through I2C interface. The device supports multiple sub-devices diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c index 49d7f624fc94..0f8acc5882a4 100644 --- a/drivers/mfd/rk808.c +++ b/drivers/mfd/rk808.c @@ -1,11 +1,15 @@ /* - * MFD core driver for Rockchip RK808 + * MFD core driver for Rockchip RK808/RK818 * * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd * * Author: Chris Zhong * Author: Zhang Qing * + * Copyright (C) 2016 PHYTEC Messtechnik GmbH + * + * Author: Wadim Egorov + * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. @@ -21,6 +25,7 @@ #include #include #include +#include #include struct rk808_reg_data { @@ -57,6 +62,14 @@ static bool rk808_is_volatile_reg(struct device *dev, unsigned int reg) return false; } +static const struct regmap_config rk818_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = RK818_USB_CTRL_REG, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = rk808_is_volatile_reg, +}; + static const struct regmap_config rk808_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -79,11 +92,21 @@ static const struct mfd_cell rk808s[] = { { .name = "rk808-rtc", .num_resources = ARRAY_SIZE(rtc_resources), - .resources = &rtc_resources[0], + .resources = rtc_resources, }, }; -static const struct rk808_reg_data pre_init_reg[] = { +static const struct mfd_cell rk818s[] = { + { .name = "rk808-clkout", }, + { .name = "rk808-regulator", }, + { + .name = "rk808-rtc", + .num_resources = ARRAY_SIZE(rtc_resources), + .resources = rtc_resources, + }, +}; + +static const struct rk808_reg_data rk808_pre_init_reg[] = { { RK808_BUCK3_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_150MA }, { RK808_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_200MA }, { RK808_BOOST_CONFIG_REG, BOOST_ILMIN_MASK, BOOST_ILMIN_100MA }, @@ -94,6 +117,24 @@ static const struct rk808_reg_data pre_init_reg[] = { VB_LO_SEL_3500MV }, }; +static const struct rk808_reg_data rk818_pre_init_reg[] = { + /* improve efficiency */ + { RK818_BUCK2_CONFIG_REG, BUCK2_RATE_MASK, BUCK_ILMIN_250MA }, + { RK818_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_250MA }, + { RK818_BOOST_CONFIG_REG, BOOST_ILMIN_MASK, BOOST_ILMIN_100MA }, + { RK818_USB_CTRL_REG, RK818_USB_ILIM_SEL_MASK, + RK818_USB_ILMIN_2000MA }, + /* close charger when usb lower then 3.4V */ + { RK818_USB_CTRL_REG, RK818_USB_CHG_SD_VSEL_MASK, + (0x7 << 4) }, + /* no action when vref */ + { RK818_H5V_EN_REG, BIT(1), RK818_REF_RDY_CTRL }, + /* enable HDMI 5V */ + { RK818_H5V_EN_REG, BIT(0), RK818_H5V_EN }, + { RK808_VB_MON_REG, MASK_ALL, VB_LO_ACT | + VB_LO_SEL_3500MV }, +}; + static const struct regmap_irq rk808_irqs[] = { /* INT_STS */ [RK808_IRQ_VOUT_LO] = { @@ -136,6 +177,76 @@ static const struct regmap_irq rk808_irqs[] = { }, }; +static const struct regmap_irq rk818_irqs[] = { + /* INT_STS */ + [RK818_IRQ_VOUT_LO] = { + .mask = RK818_IRQ_VOUT_LO_MSK, + .reg_offset = 0, + }, + [RK818_IRQ_VB_LO] = { + .mask = RK818_IRQ_VB_LO_MSK, + .reg_offset = 0, + }, + [RK818_IRQ_PWRON] = { + .mask = RK818_IRQ_PWRON_MSK, + .reg_offset = 0, + }, + [RK818_IRQ_PWRON_LP] = { + .mask = RK818_IRQ_PWRON_LP_MSK, + .reg_offset = 0, + }, + [RK818_IRQ_HOTDIE] = { + .mask = RK818_IRQ_HOTDIE_MSK, + .reg_offset = 0, + }, + [RK818_IRQ_RTC_ALARM] = { + .mask = RK818_IRQ_RTC_ALARM_MSK, + .reg_offset = 0, + }, + [RK818_IRQ_RTC_PERIOD] = { + .mask = RK818_IRQ_RTC_PERIOD_MSK, + .reg_offset = 0, + }, + [RK818_IRQ_USB_OV] = { + .mask = RK818_IRQ_USB_OV_MSK, + .reg_offset = 0, + }, + + /* INT_STS2 */ + [RK818_IRQ_PLUG_IN] = { + .mask = RK818_IRQ_PLUG_IN_MSK, + .reg_offset = 1, + }, + [RK818_IRQ_PLUG_OUT] = { + .mask = RK818_IRQ_PLUG_OUT_MSK, + .reg_offset = 1, + }, + [RK818_IRQ_CHG_OK] = { + .mask = RK818_IRQ_CHG_OK_MSK, + .reg_offset = 1, + }, + [RK818_IRQ_CHG_TE] = { + .mask = RK818_IRQ_CHG_TE_MSK, + .reg_offset = 1, + }, + [RK818_IRQ_CHG_TS1] = { + .mask = RK818_IRQ_CHG_TS1_MSK, + .reg_offset = 1, + }, + [RK818_IRQ_TS2] = { + .mask = RK818_IRQ_TS2_MSK, + .reg_offset = 1, + }, + [RK818_IRQ_CHG_CVTLIM] = { + .mask = RK818_IRQ_CHG_CVTLIM_MSK, + .reg_offset = 1, + }, + [RK818_IRQ_DISCHG_ILIM] = { + .mask = RK818_IRQ_DISCHG_ILIM_MSK, + .reg_offset = 1, + }, +}; + static struct regmap_irq_chip rk808_irq_chip = { .name = "rk808", .irqs = rk808_irqs, @@ -148,6 +259,18 @@ static struct regmap_irq_chip rk808_irq_chip = { .init_ack_masked = true, }; +static struct regmap_irq_chip rk818_irq_chip = { + .name = "rk818", + .irqs = rk818_irqs, + .num_irqs = ARRAY_SIZE(rk818_irqs), + .num_regs = 2, + .irq_reg_stride = 2, + .status_base = RK818_INT_STS_REG1, + .mask_base = RK818_INT_STS_MSK_REG1, + .ack_base = RK818_INT_STS_REG1, + .init_ack_masked = true, +}; + static struct i2c_client *rk808_i2c_client; static void rk808_device_shutdown(void) { @@ -167,55 +290,100 @@ static void rk808_device_shutdown(void) dev_err(&rk808_i2c_client->dev, "power off error!\n"); } +static const struct of_device_id rk808_of_match[] = { + { .compatible = "rockchip,rk808" }, + { .compatible = "rockchip,rk818" }, + { }, +}; +MODULE_DEVICE_TABLE(of, rk808_of_match); + static int rk808_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device_node *np = client->dev.of_node; struct rk808 *rk808; + const struct rk808_reg_data *pre_init_reg; + const struct mfd_cell *cells; + int nr_pre_init_regs; + int nr_cells; int pm_off = 0; int ret; int i; - if (!client->irq) { - dev_err(&client->dev, "No interrupt support, no core IRQ\n"); - return -EINVAL; - } - rk808 = devm_kzalloc(&client->dev, sizeof(*rk808), GFP_KERNEL); if (!rk808) return -ENOMEM; - rk808->regmap = devm_regmap_init_i2c(client, &rk808_regmap_config); + rk808->variant = i2c_smbus_read_word_data(client, RK808_ID_MSB); + if (rk808->variant < 0) { + dev_err(&client->dev, "Failed to read the chip id at 0x%02x\n", + RK808_ID_MSB); + return rk808->variant; + } + + dev_dbg(&client->dev, "Chip id: 0x%x\n", (unsigned int)rk808->variant); + + switch (rk808->variant) { + case RK808_ID: + rk808->regmap_cfg = &rk808_regmap_config; + rk808->regmap_irq_chip = &rk808_irq_chip; + pre_init_reg = rk808_pre_init_reg; + nr_pre_init_regs = ARRAY_SIZE(rk808_pre_init_reg); + cells = rk808s; + nr_cells = ARRAY_SIZE(rk808s); + break; + case RK818_ID: + rk808->regmap_cfg = &rk818_regmap_config; + rk808->regmap_irq_chip = &rk818_irq_chip; + pre_init_reg = rk818_pre_init_reg; + nr_pre_init_regs = ARRAY_SIZE(rk818_pre_init_reg); + cells = rk818s; + nr_cells = ARRAY_SIZE(rk818s); + break; + default: + dev_err(&client->dev, "Unsupported RK8XX ID %lu\n", + rk808->variant); + return -EINVAL; + } + + rk808->i2c = client; + i2c_set_clientdata(client, rk808); + + rk808->regmap = devm_regmap_init_i2c(client, rk808->regmap_cfg); if (IS_ERR(rk808->regmap)) { dev_err(&client->dev, "regmap initialization failed\n"); return PTR_ERR(rk808->regmap); } - for (i = 0; i < ARRAY_SIZE(pre_init_reg); i++) { - ret = regmap_update_bits(rk808->regmap, pre_init_reg[i].addr, - pre_init_reg[i].mask, - pre_init_reg[i].value); - if (ret) { - dev_err(&client->dev, - "0x%x write err\n", pre_init_reg[i].addr); - return ret; - } + if (!client->irq) { + dev_err(&client->dev, "No interrupt support, no core IRQ\n"); + return -EINVAL; } ret = regmap_add_irq_chip(rk808->regmap, client->irq, IRQF_ONESHOT, -1, - &rk808_irq_chip, &rk808->irq_data); + rk808->regmap_irq_chip, &rk808->irq_data); if (ret) { dev_err(&client->dev, "Failed to add irq_chip %d\n", ret); return ret; } - rk808->i2c = client; - i2c_set_clientdata(client, rk808); + for (i = 0; i < nr_pre_init_regs; i++) { + ret = regmap_update_bits(rk808->regmap, + pre_init_reg[i].addr, + pre_init_reg[i].mask, + pre_init_reg[i].value); + if (ret) { + dev_err(&client->dev, + "0x%x write err\n", + pre_init_reg[i].addr); + return ret; + } + } - ret = devm_mfd_add_devices(&client->dev, -1, - rk808s, ARRAY_SIZE(rk808s), NULL, 0, - regmap_irq_get_domain(rk808->irq_data)); + ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE, + cells, nr_cells, NULL, 0, + regmap_irq_get_domain(rk808->irq_data)); if (ret) { dev_err(&client->dev, "failed to add MFD devices %d\n", ret); goto err_irq; @@ -245,14 +413,9 @@ static int rk808_remove(struct i2c_client *client) return 0; } -static const struct of_device_id rk808_of_match[] = { - { .compatible = "rockchip,rk808" }, - { }, -}; -MODULE_DEVICE_TABLE(of, rk808_of_match); - static const struct i2c_device_id rk808_ids[] = { { "rk808" }, + { "rk818" }, { }, }; MODULE_DEVICE_TABLE(i2c, rk808_ids); @@ -272,4 +435,5 @@ module_i2c_driver(rk808_i2c_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Chris Zhong "); MODULE_AUTHOR("Zhang Qing "); -MODULE_DESCRIPTION("RK808 PMIC driver"); +MODULE_AUTHOR("Wadim Egorov "); +MODULE_DESCRIPTION("RK808/RK818 PMIC driver"); -- cgit v1.2.3-58-ga151