Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.0-rc7 275 lines 6.7 kB view raw
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_VB_MON_REG, MASK_ALL, VB_LO_ACT | 93 VB_LO_SEL_3500MV }, 94}; 95 96static const struct regmap_irq rk808_irqs[] = { 97 /* INT_STS */ 98 [RK808_IRQ_VOUT_LO] = { 99 .mask = RK808_IRQ_VOUT_LO_MSK, 100 .reg_offset = 0, 101 }, 102 [RK808_IRQ_VB_LO] = { 103 .mask = RK808_IRQ_VB_LO_MSK, 104 .reg_offset = 0, 105 }, 106 [RK808_IRQ_PWRON] = { 107 .mask = RK808_IRQ_PWRON_MSK, 108 .reg_offset = 0, 109 }, 110 [RK808_IRQ_PWRON_LP] = { 111 .mask = RK808_IRQ_PWRON_LP_MSK, 112 .reg_offset = 0, 113 }, 114 [RK808_IRQ_HOTDIE] = { 115 .mask = RK808_IRQ_HOTDIE_MSK, 116 .reg_offset = 0, 117 }, 118 [RK808_IRQ_RTC_ALARM] = { 119 .mask = RK808_IRQ_RTC_ALARM_MSK, 120 .reg_offset = 0, 121 }, 122 [RK808_IRQ_RTC_PERIOD] = { 123 .mask = RK808_IRQ_RTC_PERIOD_MSK, 124 .reg_offset = 0, 125 }, 126 127 /* INT_STS2 */ 128 [RK808_IRQ_PLUG_IN_INT] = { 129 .mask = RK808_IRQ_PLUG_IN_INT_MSK, 130 .reg_offset = 1, 131 }, 132 [RK808_IRQ_PLUG_OUT_INT] = { 133 .mask = RK808_IRQ_PLUG_OUT_INT_MSK, 134 .reg_offset = 1, 135 }, 136}; 137 138static struct regmap_irq_chip rk808_irq_chip = { 139 .name = "rk808", 140 .irqs = rk808_irqs, 141 .num_irqs = ARRAY_SIZE(rk808_irqs), 142 .num_regs = 2, 143 .irq_reg_stride = 2, 144 .status_base = RK808_INT_STS_REG1, 145 .mask_base = RK808_INT_STS_MSK_REG1, 146 .ack_base = RK808_INT_STS_REG1, 147 .init_ack_masked = true, 148}; 149 150static struct i2c_client *rk808_i2c_client; 151static void rk808_device_shutdown(void) 152{ 153 int ret; 154 struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client); 155 156 if (!rk808) { 157 dev_warn(&rk808_i2c_client->dev, 158 "have no rk808, so do nothing here\n"); 159 return; 160 } 161 162 ret = regmap_update_bits(rk808->regmap, 163 RK808_DEVCTRL_REG, 164 DEV_OFF_RST, DEV_OFF_RST); 165 if (ret) 166 dev_err(&rk808_i2c_client->dev, "power off error!\n"); 167} 168 169static int rk808_probe(struct i2c_client *client, 170 const struct i2c_device_id *id) 171{ 172 struct device_node *np = client->dev.of_node; 173 struct rk808 *rk808; 174 int pm_off = 0; 175 int ret; 176 int i; 177 178 if (!client->irq) { 179 dev_err(&client->dev, "No interrupt support, no core IRQ\n"); 180 return -EINVAL; 181 } 182 183 rk808 = devm_kzalloc(&client->dev, sizeof(*rk808), GFP_KERNEL); 184 if (!rk808) 185 return -ENOMEM; 186 187 rk808->regmap = devm_regmap_init_i2c(client, &rk808_regmap_config); 188 if (IS_ERR(rk808->regmap)) { 189 dev_err(&client->dev, "regmap initialization failed\n"); 190 return PTR_ERR(rk808->regmap); 191 } 192 193 for (i = 0; i < ARRAY_SIZE(pre_init_reg); i++) { 194 ret = regmap_update_bits(rk808->regmap, pre_init_reg[i].addr, 195 pre_init_reg[i].mask, 196 pre_init_reg[i].value); 197 if (ret) { 198 dev_err(&client->dev, 199 "0x%x write err\n", pre_init_reg[i].addr); 200 return ret; 201 } 202 } 203 204 ret = regmap_add_irq_chip(rk808->regmap, client->irq, 205 IRQF_ONESHOT, -1, 206 &rk808_irq_chip, &rk808->irq_data); 207 if (ret) { 208 dev_err(&client->dev, "Failed to add irq_chip %d\n", ret); 209 return ret; 210 } 211 212 rk808->i2c = client; 213 i2c_set_clientdata(client, rk808); 214 215 ret = mfd_add_devices(&client->dev, -1, 216 rk808s, ARRAY_SIZE(rk808s), 217 NULL, 0, regmap_irq_get_domain(rk808->irq_data)); 218 if (ret) { 219 dev_err(&client->dev, "failed to add MFD devices %d\n", ret); 220 goto err_irq; 221 } 222 223 pm_off = of_property_read_bool(np, 224 "rockchip,system-power-controller"); 225 if (pm_off && !pm_power_off) { 226 rk808_i2c_client = client; 227 pm_power_off = rk808_device_shutdown; 228 } 229 230 return 0; 231 232err_irq: 233 regmap_del_irq_chip(client->irq, rk808->irq_data); 234 return ret; 235} 236 237static int rk808_remove(struct i2c_client *client) 238{ 239 struct rk808 *rk808 = i2c_get_clientdata(client); 240 241 regmap_del_irq_chip(client->irq, rk808->irq_data); 242 mfd_remove_devices(&client->dev); 243 pm_power_off = NULL; 244 245 return 0; 246} 247 248static struct of_device_id rk808_of_match[] = { 249 { .compatible = "rockchip,rk808" }, 250 { }, 251}; 252MODULE_DEVICE_TABLE(of, rk808_of_match); 253 254static const struct i2c_device_id rk808_ids[] = { 255 { "rk808" }, 256 { }, 257}; 258MODULE_DEVICE_TABLE(i2c, rk808_ids); 259 260static struct i2c_driver rk808_i2c_driver = { 261 .driver = { 262 .name = "rk808", 263 .of_match_table = rk808_of_match, 264 }, 265 .probe = rk808_probe, 266 .remove = rk808_remove, 267 .id_table = rk808_ids, 268}; 269 270module_i2c_driver(rk808_i2c_driver); 271 272MODULE_LICENSE("GPL"); 273MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>"); 274MODULE_AUTHOR("Zhang Qing <zhangqing@rock-chips.com>"); 275MODULE_DESCRIPTION("RK808 PMIC driver");