Root/
1 | /* |
2 | * AS3711 PMIC MFC driver |
3 | * |
4 | * Copyright (C) 2012 Renesas Electronics Corporation |
5 | * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de> |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the version 2 of the GNU General Public License as |
9 | * published by the Free Software Foundation |
10 | */ |
11 | |
12 | #include <linux/device.h> |
13 | #include <linux/err.h> |
14 | #include <linux/i2c.h> |
15 | #include <linux/init.h> |
16 | #include <linux/kernel.h> |
17 | #include <linux/mfd/as3711.h> |
18 | #include <linux/mfd/core.h> |
19 | #include <linux/module.h> |
20 | #include <linux/regmap.h> |
21 | #include <linux/slab.h> |
22 | |
23 | enum { |
24 | AS3711_REGULATOR, |
25 | AS3711_BACKLIGHT, |
26 | }; |
27 | |
28 | /* |
29 | * Ok to have it static: it is only used during probing and multiple I2C devices |
30 | * cannot be probed simultaneously. Just make sure to avoid stale data. |
31 | */ |
32 | static struct mfd_cell as3711_subdevs[] = { |
33 | [AS3711_REGULATOR] = {.name = "as3711-regulator",}, |
34 | [AS3711_BACKLIGHT] = {.name = "as3711-backlight",}, |
35 | }; |
36 | |
37 | static bool as3711_volatile_reg(struct device *dev, unsigned int reg) |
38 | { |
39 | switch (reg) { |
40 | case AS3711_GPIO_SIGNAL_IN: |
41 | case AS3711_INTERRUPT_STATUS_1: |
42 | case AS3711_INTERRUPT_STATUS_2: |
43 | case AS3711_INTERRUPT_STATUS_3: |
44 | case AS3711_CHARGER_STATUS_1: |
45 | case AS3711_CHARGER_STATUS_2: |
46 | case AS3711_REG_STATUS: |
47 | return true; |
48 | } |
49 | return false; |
50 | } |
51 | |
52 | static bool as3711_precious_reg(struct device *dev, unsigned int reg) |
53 | { |
54 | switch (reg) { |
55 | case AS3711_INTERRUPT_STATUS_1: |
56 | case AS3711_INTERRUPT_STATUS_2: |
57 | case AS3711_INTERRUPT_STATUS_3: |
58 | return true; |
59 | } |
60 | return false; |
61 | } |
62 | |
63 | static bool as3711_readable_reg(struct device *dev, unsigned int reg) |
64 | { |
65 | switch (reg) { |
66 | case AS3711_SD_1_VOLTAGE: |
67 | case AS3711_SD_2_VOLTAGE: |
68 | case AS3711_SD_3_VOLTAGE: |
69 | case AS3711_SD_4_VOLTAGE: |
70 | case AS3711_LDO_1_VOLTAGE: |
71 | case AS3711_LDO_2_VOLTAGE: |
72 | case AS3711_LDO_3_VOLTAGE: |
73 | case AS3711_LDO_4_VOLTAGE: |
74 | case AS3711_LDO_5_VOLTAGE: |
75 | case AS3711_LDO_6_VOLTAGE: |
76 | case AS3711_LDO_7_VOLTAGE: |
77 | case AS3711_LDO_8_VOLTAGE: |
78 | case AS3711_SD_CONTROL: |
79 | case AS3711_GPIO_SIGNAL_OUT: |
80 | case AS3711_GPIO_SIGNAL_IN: |
81 | case AS3711_SD_CONTROL_1: |
82 | case AS3711_SD_CONTROL_2: |
83 | case AS3711_CURR_CONTROL: |
84 | case AS3711_CURR1_VALUE: |
85 | case AS3711_CURR2_VALUE: |
86 | case AS3711_CURR3_VALUE: |
87 | case AS3711_STEPUP_CONTROL_1: |
88 | case AS3711_STEPUP_CONTROL_2: |
89 | case AS3711_STEPUP_CONTROL_4: |
90 | case AS3711_STEPUP_CONTROL_5: |
91 | case AS3711_REG_STATUS: |
92 | case AS3711_INTERRUPT_STATUS_1: |
93 | case AS3711_INTERRUPT_STATUS_2: |
94 | case AS3711_INTERRUPT_STATUS_3: |
95 | case AS3711_CHARGER_STATUS_1: |
96 | case AS3711_CHARGER_STATUS_2: |
97 | case AS3711_ASIC_ID_1: |
98 | case AS3711_ASIC_ID_2: |
99 | return true; |
100 | } |
101 | return false; |
102 | } |
103 | |
104 | static const struct regmap_config as3711_regmap_config = { |
105 | .reg_bits = 8, |
106 | .val_bits = 8, |
107 | .volatile_reg = as3711_volatile_reg, |
108 | .readable_reg = as3711_readable_reg, |
109 | .precious_reg = as3711_precious_reg, |
110 | .max_register = AS3711_MAX_REGS, |
111 | .num_reg_defaults_raw = AS3711_MAX_REGS, |
112 | .cache_type = REGCACHE_RBTREE, |
113 | }; |
114 | |
115 | static int as3711_i2c_probe(struct i2c_client *client, |
116 | const struct i2c_device_id *id) |
117 | { |
118 | struct as3711 *as3711; |
119 | struct as3711_platform_data *pdata = client->dev.platform_data; |
120 | unsigned int id1, id2; |
121 | int ret; |
122 | |
123 | if (!pdata) |
124 | dev_dbg(&client->dev, "Platform data not found\n"); |
125 | |
126 | as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL); |
127 | if (!as3711) { |
128 | dev_err(&client->dev, "Memory allocation failed\n"); |
129 | return -ENOMEM; |
130 | } |
131 | |
132 | as3711->dev = &client->dev; |
133 | i2c_set_clientdata(client, as3711); |
134 | |
135 | if (client->irq) |
136 | dev_notice(&client->dev, "IRQ not supported yet\n"); |
137 | |
138 | as3711->regmap = devm_regmap_init_i2c(client, &as3711_regmap_config); |
139 | if (IS_ERR(as3711->regmap)) { |
140 | ret = PTR_ERR(as3711->regmap); |
141 | dev_err(&client->dev, "regmap initialization failed: %d\n", ret); |
142 | return ret; |
143 | } |
144 | |
145 | ret = regmap_read(as3711->regmap, AS3711_ASIC_ID_1, &id1); |
146 | if (!ret) |
147 | ret = regmap_read(as3711->regmap, AS3711_ASIC_ID_2, &id2); |
148 | if (ret < 0) { |
149 | dev_err(&client->dev, "regmap_read() failed: %d\n", ret); |
150 | return ret; |
151 | } |
152 | if (id1 != 0x8b) |
153 | return -ENODEV; |
154 | dev_info(as3711->dev, "AS3711 detected: %x:%x\n", id1, id2); |
155 | |
156 | /* We can reuse as3711_subdevs[], it will be copied in mfd_add_devices() */ |
157 | if (pdata) { |
158 | as3711_subdevs[AS3711_REGULATOR].platform_data = &pdata->regulator; |
159 | as3711_subdevs[AS3711_REGULATOR].pdata_size = sizeof(pdata->regulator); |
160 | as3711_subdevs[AS3711_BACKLIGHT].platform_data = &pdata->backlight; |
161 | as3711_subdevs[AS3711_BACKLIGHT].pdata_size = sizeof(pdata->backlight); |
162 | } else { |
163 | as3711_subdevs[AS3711_REGULATOR].platform_data = NULL; |
164 | as3711_subdevs[AS3711_REGULATOR].pdata_size = 0; |
165 | as3711_subdevs[AS3711_BACKLIGHT].platform_data = NULL; |
166 | as3711_subdevs[AS3711_BACKLIGHT].pdata_size = 0; |
167 | } |
168 | |
169 | ret = mfd_add_devices(as3711->dev, -1, as3711_subdevs, |
170 | ARRAY_SIZE(as3711_subdevs), NULL, 0, NULL); |
171 | if (ret < 0) |
172 | dev_err(&client->dev, "add mfd devices failed: %d\n", ret); |
173 | |
174 | return ret; |
175 | } |
176 | |
177 | static int as3711_i2c_remove(struct i2c_client *client) |
178 | { |
179 | struct as3711 *as3711 = i2c_get_clientdata(client); |
180 | |
181 | mfd_remove_devices(as3711->dev); |
182 | return 0; |
183 | } |
184 | |
185 | static const struct i2c_device_id as3711_i2c_id[] = { |
186 | {.name = "as3711", .driver_data = 0}, |
187 | {} |
188 | }; |
189 | |
190 | MODULE_DEVICE_TABLE(i2c, as3711_i2c_id); |
191 | |
192 | static struct i2c_driver as3711_i2c_driver = { |
193 | .driver = { |
194 | .name = "as3711", |
195 | .owner = THIS_MODULE, |
196 | }, |
197 | .probe = as3711_i2c_probe, |
198 | .remove = as3711_i2c_remove, |
199 | .id_table = as3711_i2c_id, |
200 | }; |
201 | |
202 | static int __init as3711_i2c_init(void) |
203 | { |
204 | return i2c_add_driver(&as3711_i2c_driver); |
205 | } |
206 | /* Initialise early */ |
207 | subsys_initcall(as3711_i2c_init); |
208 | |
209 | static void __exit as3711_i2c_exit(void) |
210 | { |
211 | i2c_del_driver(&as3711_i2c_driver); |
212 | } |
213 | module_exit(as3711_i2c_exit); |
214 | |
215 | MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); |
216 | MODULE_DESCRIPTION("AS3711 PMIC driver"); |
217 | MODULE_LICENSE("GPL v2"); |
218 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9