1/* 2 * MFD core driver for Rockchip RK808 3 * 4 * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd 5 * 6 * Author: Chris Zhong <zyw@rock-chips.com> 7 * Author: Zhang Qing <zhangqing@rock-chips.com> 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms and conditions of the GNU General Public License, 11 * version 2, as published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 16 * more details. 17 */ 18 19#include <linux/i2c.h> 20#include <linux/interrupt.h> 21#include <linux/mfd/rk808.h> 22#include <linux/mfd/core.h> 23#include <linux/module.h> 24#include <linux/regmap.h> 25 26struct rk808_reg_data { 27 int addr; 28 int mask; 29 int value; 30}; 31 32static bool rk808_is_volatile_reg(struct device *dev, unsigned int reg) 33{ 34 /* 35 * Notes: 36 * - Technically the ROUND_30s bit makes RTC_CTRL_REG volatile, but 37 * we don't use that feature. It's better to cache. 38 * - It's unlikely we care that RK808_DEVCTRL_REG is volatile since 39 * bits are cleared in case when we shutoff anyway, but better safe. 40 */ 41 42 switch (reg) { 43 case RK808_SECONDS_REG ... RK808_WEEKS_REG: 44 case RK808_RTC_STATUS_REG: 45 case RK808_VB_MON_REG: 46 case RK808_THERMAL_REG: 47 case RK808_DCDC_UV_STS_REG: 48 case RK808_LDO_UV_STS_REG: 49 case RK808_DCDC_PG_REG: 50 case RK808_LDO_PG_REG: 51 case RK808_DEVCTRL_REG: 52 case RK808_INT_STS_REG1: 53 case RK808_INT_STS_REG2: 54 return true; 55 } 56 57 return false; 58} 59 60static const struct regmap_config rk808_regmap_config = { 61 .reg_bits = 8, 62 .val_bits = 8, 63 .max_register = RK808_IO_POL_REG, 64 .cache_type = REGCACHE_RBTREE, 65 .volatile_reg = rk808_is_volatile_reg, 66}; 67 68static struct resource rtc_resources[] = { 69 { 70 .start = RK808_IRQ_RTC_ALARM, 71 .end = RK808_IRQ_RTC_ALARM, 72 .flags = IORESOURCE_IRQ, 73 } 74}; 75 76static const struct mfd_cell rk808s[] = { 77 { .name = "rk808-clkout", }, 78 { .name = "rk808-regulator", }, 79 { 80 .name = "rk808-rtc", 81 .num_resources = ARRAY_SIZE(rtc_resources), 82 .resources = &rtc_resources[0], 83 }, 84}; 85 86static const struct rk808_reg_data pre_init_reg[] = { 87 { RK808_BUCK3_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_150MA }, 88 { RK808_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_200MA }, 89 { RK808_BOOST_CONFIG_REG, BOOST_ILMIN_MASK, BOOST_ILMIN_100MA }, 90 { RK808_BUCK1_CONFIG_REG, BUCK1_RATE_MASK, BUCK_ILMIN_200MA }, 91 { RK808_BUCK2_CONFIG_REG, BUCK2_RATE_MASK, BUCK_ILMIN_200MA }, 92 { RK808_DCDC_UV_ACT_REG, BUCK_UV_ACT_MASK, BUCK_UV_ACT_DISABLE}, 93 { RK808_VB_MON_REG, MASK_ALL, VB_LO_ACT | 94 VB_LO_SEL_3500MV }, 95}; 96 97static const struct regmap_irq rk808_irqs[] = { 98 /* INT_STS */ 99 [RK808_IRQ_VOUT_LO] = { 100 .mask = RK808_IRQ_VOUT_LO_MSK, 101 .reg_offset = 0, 102 }, 103 [RK808_IRQ_VB_LO] = { 104 .mask = RK808_IRQ_VB_LO_MSK, 105 .reg_offset = 0, 106 }, 107 [RK808_IRQ_PWRON] = { 108 .mask = RK808_IRQ_PWRON_MSK, 109 .reg_offset = 0, 110 }, 111 [RK808_IRQ_PWRON_LP] = { 112 .mask = RK808_IRQ_PWRON_LP_MSK, 113 .reg_offset = 0, 114 }, 115 [RK808_IRQ_HOTDIE] = { 116 .mask = RK808_IRQ_HOTDIE_MSK, 117 .reg_offset = 0, 118 }, 119 [RK808_IRQ_RTC_ALARM] = { 120 .mask = RK808_IRQ_RTC_ALARM_MSK, 121 .reg_offset = 0, 122 }, 123 [RK808_IRQ_RTC_PERIOD] = { 124 .mask = RK808_IRQ_RTC_PERIOD_MSK, 125 .reg_offset = 0, 126 }, 127 128 /* INT_STS2 */ 129 [RK808_IRQ_PLUG_IN_INT] = { 130 .mask = RK808_IRQ_PLUG_IN_INT_MSK, 131 .reg_offset = 1, 132 }, 133 [RK808_IRQ_PLUG_OUT_INT] = { 134 .mask = RK808_IRQ_PLUG_OUT_INT_MSK, 135 .reg_offset = 1, 136 }, 137}; 138 139static struct regmap_irq_chip rk808_irq_chip = { 140 .name = "rk808", 141 .irqs = rk808_irqs, 142 .num_irqs = ARRAY_SIZE(rk808_irqs), 143 .num_regs = 2, 144 .irq_reg_stride = 2, 145 .status_base = RK808_INT_STS_REG1, 146 .mask_base = RK808_INT_STS_MSK_REG1, 147 .ack_base = RK808_INT_STS_REG1, 148 .init_ack_masked = true, 149}; 150 151static struct i2c_client *rk808_i2c_client; 152static void rk808_device_shutdown(void) 153{ 154 int ret; 155 struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client); 156 157 if (!rk808) { 158 dev_warn(&rk808_i2c_client->dev, 159 "have no rk808, so do nothing here\n"); 160 return; 161 } 162 163 ret = regmap_update_bits(rk808->regmap, 164 RK808_DEVCTRL_REG, 165 DEV_OFF_RST, DEV_OFF_RST); 166 if (ret) 167 dev_err(&rk808_i2c_client->dev, "power off error!\n"); 168} 169 170static int rk808_probe(struct i2c_client *client, 171 const struct i2c_device_id *id) 172{ 173 struct device_node *np = client->dev.of_node; 174 struct rk808 *rk808; 175 int pm_off = 0; 176 int ret; 177 int i; 178 179 if (!client->irq) { 180 dev_err(&client->dev, "No interrupt support, no core IRQ\n"); 181 return -EINVAL; 182 } 183 184 rk808 = devm_kzalloc(&client->dev, sizeof(*rk808), GFP_KERNEL); 185 if (!rk808) 186 return -ENOMEM; 187 188 rk808->regmap = devm_regmap_init_i2c(client, &rk808_regmap_config); 189 if (IS_ERR(rk808->regmap)) { 190 dev_err(&client->dev, "regmap initialization failed\n"); 191 return PTR_ERR(rk808->regmap); 192 } 193 194 for (i = 0; i < ARRAY_SIZE(pre_init_reg); i++) { 195 ret = regmap_update_bits(rk808->regmap, pre_init_reg[i].addr, 196 pre_init_reg[i].mask, 197 pre_init_reg[i].value); 198 if (ret) { 199 dev_err(&client->dev, 200 "0x%x write err\n", pre_init_reg[i].addr); 201 return ret; 202 } 203 } 204 205 ret = regmap_add_irq_chip(rk808->regmap, client->irq, 206 IRQF_ONESHOT, -1, 207 &rk808_irq_chip, &rk808->irq_data); 208 if (ret) { 209 dev_err(&client->dev, "Failed to add irq_chip %d\n", ret); 210 return ret; 211 } 212 213 rk808->i2c = client; 214 i2c_set_clientdata(client, rk808); 215 216 ret = mfd_add_devices(&client->dev, -1, 217 rk808s, ARRAY_SIZE(rk808s), 218 NULL, 0, regmap_irq_get_domain(rk808->irq_data)); 219 if (ret) { 220 dev_err(&client->dev, "failed to add MFD devices %d\n", ret); 221 goto err_irq; 222 } 223 224 pm_off = of_property_read_bool(np, 225 "rockchip,system-power-controller"); 226 if (pm_off && !pm_power_off) { 227 rk808_i2c_client = client; 228 pm_power_off = rk808_device_shutdown; 229 } 230 231 return 0; 232 233err_irq: 234 regmap_del_irq_chip(client->irq, rk808->irq_data); 235 return ret; 236} 237 238static int rk808_remove(struct i2c_client *client) 239{ 240 struct rk808 *rk808 = i2c_get_clientdata(client); 241 242 regmap_del_irq_chip(client->irq, rk808->irq_data); 243 mfd_remove_devices(&client->dev); 244 pm_power_off = NULL; 245 246 return 0; 247} 248 249static const struct of_device_id rk808_of_match[] = { 250 { .compatible = "rockchip,rk808" }, 251 { }, 252}; 253MODULE_DEVICE_TABLE(of, rk808_of_match); 254 255static const struct i2c_device_id rk808_ids[] = { 256 { "rk808" }, 257 { }, 258}; 259MODULE_DEVICE_TABLE(i2c, rk808_ids); 260 261static struct i2c_driver rk808_i2c_driver = { 262 .driver = { 263 .name = "rk808", 264 .of_match_table = rk808_of_match, 265 }, 266 .probe = rk808_probe, 267 .remove = rk808_remove, 268 .id_table = rk808_ids, 269}; 270 271module_i2c_driver(rk808_i2c_driver); 272 273MODULE_LICENSE("GPL"); 274MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>"); 275MODULE_AUTHOR("Zhang Qing <zhangqing@rock-chips.com>"); 276MODULE_DESCRIPTION("RK808 PMIC driver"); 277