Skip to content

Commit

Permalink
mfd: rk808: Add RK818 support
Browse files Browse the repository at this point in the history
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 <w.egorov@phytec.de>
Tested-by: Andy Yan <andy.yan@rock-chips.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
  • Loading branch information
Wadim Egorov authored and Lee Jones committed Aug 31, 2016
1 parent 694d0d0 commit 2eedcbf
Show file tree
Hide file tree
Showing 3 changed files with 342 additions and 42 deletions.
4 changes: 2 additions & 2 deletions drivers/mfd/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
226 changes: 195 additions & 31 deletions drivers/mfd/rk808.c
Original file line number Diff line number Diff line change
@@ -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 <zyw@rock-chips.com>
* Author: Zhang Qing <zhangqing@rock-chips.com>
*
* Copyright (C) 2016 PHYTEC Messtechnik GmbH
*
* Author: Wadim Egorov <w.egorov@phytec.de>
*
* 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.
Expand All @@ -21,6 +25,7 @@
#include <linux/mfd/rk808.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regmap.h>

struct rk808_reg_data {
Expand Down Expand Up @@ -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,
Expand All @@ -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 },
Expand All @@ -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] = {
Expand Down Expand Up @@ -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,
Expand All @@ -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)
{
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -272,4 +435,5 @@ module_i2c_driver(rk808_i2c_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
MODULE_AUTHOR("Zhang Qing <zhangqing@rock-chips.com>");
MODULE_DESCRIPTION("RK808 PMIC driver");
MODULE_AUTHOR("Wadim Egorov <w.egorov@phytec.de>");
MODULE_DESCRIPTION("RK808/RK818 PMIC driver");
Loading

0 comments on commit 2eedcbf

Please sign in to comment.