1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * ROHM BD9571MWV-M and BD9574MVF-M core driver |
4 | * |
5 | * Copyright (C) 2017 Marek Vasut <marek.vasut+renesas@gmail.com> |
6 | * Copyright (C) 2020 Renesas Electronics Corporation |
7 | * |
8 | * Based on the TPS65086 driver |
9 | */ |
10 | |
11 | #include <linux/i2c.h> |
12 | #include <linux/interrupt.h> |
13 | #include <linux/mfd/core.h> |
14 | #include <linux/mfd/rohm-generic.h> |
15 | #include <linux/module.h> |
16 | |
17 | #include <linux/mfd/bd9571mwv.h> |
18 | |
19 | static const struct mfd_cell bd9571mwv_cells[] = { |
20 | { .name = "bd9571mwv-regulator" , }, |
21 | { .name = "bd9571mwv-gpio" , }, |
22 | }; |
23 | |
24 | static const struct regmap_range bd9571mwv_readable_yes_ranges[] = { |
25 | regmap_reg_range(BD9571MWV_VENDOR_CODE, BD9571MWV_PRODUCT_REVISION), |
26 | regmap_reg_range(BD9571MWV_BKUP_MODE_CNT, BD9571MWV_BKUP_MODE_CNT), |
27 | regmap_reg_range(BD9571MWV_AVS_SET_MONI, BD9571MWV_AVS_DVFS_VID(3)), |
28 | regmap_reg_range(BD9571MWV_VD18_VID, BD9571MWV_VD33_VID), |
29 | regmap_reg_range(BD9571MWV_DVFS_VINIT, BD9571MWV_DVFS_VINIT), |
30 | regmap_reg_range(BD9571MWV_DVFS_SETVMAX, BD9571MWV_DVFS_MONIVDAC), |
31 | regmap_reg_range(BD9571MWV_GPIO_IN, BD9571MWV_GPIO_IN), |
32 | regmap_reg_range(BD9571MWV_GPIO_INT, BD9571MWV_GPIO_INTMASK), |
33 | regmap_reg_range(BD9571MWV_INT_INTREQ, BD9571MWV_INT_INTMASK), |
34 | }; |
35 | |
36 | static const struct regmap_access_table bd9571mwv_readable_table = { |
37 | .yes_ranges = bd9571mwv_readable_yes_ranges, |
38 | .n_yes_ranges = ARRAY_SIZE(bd9571mwv_readable_yes_ranges), |
39 | }; |
40 | |
41 | static const struct regmap_range bd9571mwv_writable_yes_ranges[] = { |
42 | regmap_reg_range(BD9571MWV_BKUP_MODE_CNT, BD9571MWV_BKUP_MODE_CNT), |
43 | regmap_reg_range(BD9571MWV_AVS_VD09_VID(0), BD9571MWV_AVS_VD09_VID(3)), |
44 | regmap_reg_range(BD9571MWV_DVFS_SETVID, BD9571MWV_DVFS_SETVID), |
45 | regmap_reg_range(BD9571MWV_GPIO_DIR, BD9571MWV_GPIO_OUT), |
46 | regmap_reg_range(BD9571MWV_GPIO_INT_SET, BD9571MWV_GPIO_INTMASK), |
47 | regmap_reg_range(BD9571MWV_INT_INTREQ, BD9571MWV_INT_INTMASK), |
48 | }; |
49 | |
50 | static const struct regmap_access_table bd9571mwv_writable_table = { |
51 | .yes_ranges = bd9571mwv_writable_yes_ranges, |
52 | .n_yes_ranges = ARRAY_SIZE(bd9571mwv_writable_yes_ranges), |
53 | }; |
54 | |
55 | static const struct regmap_range bd9571mwv_volatile_yes_ranges[] = { |
56 | regmap_reg_range(BD9571MWV_DVFS_MONIVDAC, BD9571MWV_DVFS_MONIVDAC), |
57 | regmap_reg_range(BD9571MWV_GPIO_IN, BD9571MWV_GPIO_IN), |
58 | regmap_reg_range(BD9571MWV_GPIO_INT, BD9571MWV_GPIO_INT), |
59 | regmap_reg_range(BD9571MWV_INT_INTREQ, BD9571MWV_INT_INTREQ), |
60 | }; |
61 | |
62 | static const struct regmap_access_table bd9571mwv_volatile_table = { |
63 | .yes_ranges = bd9571mwv_volatile_yes_ranges, |
64 | .n_yes_ranges = ARRAY_SIZE(bd9571mwv_volatile_yes_ranges), |
65 | }; |
66 | |
67 | static const struct regmap_config bd9571mwv_regmap_config = { |
68 | .reg_bits = 8, |
69 | .val_bits = 8, |
70 | .cache_type = REGCACHE_MAPLE, |
71 | .rd_table = &bd9571mwv_readable_table, |
72 | .wr_table = &bd9571mwv_writable_table, |
73 | .volatile_table = &bd9571mwv_volatile_table, |
74 | .max_register = 0xff, |
75 | }; |
76 | |
77 | static const struct regmap_irq bd9571mwv_irqs[] = { |
78 | REGMAP_IRQ_REG(BD9571MWV_IRQ_MD1, 0, |
79 | BD9571MWV_INT_INTREQ_MD1_INT), |
80 | REGMAP_IRQ_REG(BD9571MWV_IRQ_MD2_E1, 0, |
81 | BD9571MWV_INT_INTREQ_MD2_E1_INT), |
82 | REGMAP_IRQ_REG(BD9571MWV_IRQ_MD2_E2, 0, |
83 | BD9571MWV_INT_INTREQ_MD2_E2_INT), |
84 | REGMAP_IRQ_REG(BD9571MWV_IRQ_PROT_ERR, 0, |
85 | BD9571MWV_INT_INTREQ_PROT_ERR_INT), |
86 | REGMAP_IRQ_REG(BD9571MWV_IRQ_GP, 0, |
87 | BD9571MWV_INT_INTREQ_GP_INT), |
88 | REGMAP_IRQ_REG(BD9571MWV_IRQ_128H_OF, 0, |
89 | BD9571MWV_INT_INTREQ_128H_OF_INT), |
90 | REGMAP_IRQ_REG(BD9571MWV_IRQ_WDT_OF, 0, |
91 | BD9571MWV_INT_INTREQ_WDT_OF_INT), |
92 | REGMAP_IRQ_REG(BD9571MWV_IRQ_BKUP_TRG, 0, |
93 | BD9571MWV_INT_INTREQ_BKUP_TRG_INT), |
94 | }; |
95 | |
96 | static struct regmap_irq_chip bd9571mwv_irq_chip = { |
97 | .name = "bd9571mwv" , |
98 | .status_base = BD9571MWV_INT_INTREQ, |
99 | .mask_base = BD9571MWV_INT_INTMASK, |
100 | .ack_base = BD9571MWV_INT_INTREQ, |
101 | .init_ack_masked = true, |
102 | .num_regs = 1, |
103 | .irqs = bd9571mwv_irqs, |
104 | .num_irqs = ARRAY_SIZE(bd9571mwv_irqs), |
105 | }; |
106 | |
107 | static const struct mfd_cell bd9574mwf_cells[] = { |
108 | { .name = "bd9574mwf-regulator" , }, |
109 | { .name = "bd9574mwf-gpio" , }, |
110 | }; |
111 | |
112 | static const struct regmap_range bd9574mwf_readable_yes_ranges[] = { |
113 | regmap_reg_range(BD9571MWV_VENDOR_CODE, BD9571MWV_PRODUCT_REVISION), |
114 | regmap_reg_range(BD9571MWV_BKUP_MODE_CNT, BD9571MWV_BKUP_MODE_CNT), |
115 | regmap_reg_range(BD9571MWV_DVFS_VINIT, BD9571MWV_DVFS_SETVMAX), |
116 | regmap_reg_range(BD9571MWV_DVFS_SETVID, BD9571MWV_DVFS_MONIVDAC), |
117 | regmap_reg_range(BD9571MWV_GPIO_IN, BD9571MWV_GPIO_IN), |
118 | regmap_reg_range(BD9571MWV_GPIO_INT, BD9571MWV_GPIO_INTMASK), |
119 | regmap_reg_range(BD9571MWV_INT_INTREQ, BD9571MWV_INT_INTMASK), |
120 | }; |
121 | |
122 | static const struct regmap_access_table bd9574mwf_readable_table = { |
123 | .yes_ranges = bd9574mwf_readable_yes_ranges, |
124 | .n_yes_ranges = ARRAY_SIZE(bd9574mwf_readable_yes_ranges), |
125 | }; |
126 | |
127 | static const struct regmap_range bd9574mwf_writable_yes_ranges[] = { |
128 | regmap_reg_range(BD9571MWV_BKUP_MODE_CNT, BD9571MWV_BKUP_MODE_CNT), |
129 | regmap_reg_range(BD9571MWV_DVFS_SETVID, BD9571MWV_DVFS_SETVID), |
130 | regmap_reg_range(BD9571MWV_GPIO_DIR, BD9571MWV_GPIO_OUT), |
131 | regmap_reg_range(BD9571MWV_GPIO_INT_SET, BD9571MWV_GPIO_INTMASK), |
132 | regmap_reg_range(BD9571MWV_INT_INTREQ, BD9571MWV_INT_INTMASK), |
133 | }; |
134 | |
135 | static const struct regmap_access_table bd9574mwf_writable_table = { |
136 | .yes_ranges = bd9574mwf_writable_yes_ranges, |
137 | .n_yes_ranges = ARRAY_SIZE(bd9574mwf_writable_yes_ranges), |
138 | }; |
139 | |
140 | static const struct regmap_range bd9574mwf_volatile_yes_ranges[] = { |
141 | regmap_reg_range(BD9571MWV_DVFS_MONIVDAC, BD9571MWV_DVFS_MONIVDAC), |
142 | regmap_reg_range(BD9571MWV_GPIO_IN, BD9571MWV_GPIO_IN), |
143 | regmap_reg_range(BD9571MWV_GPIO_INT, BD9571MWV_GPIO_INT), |
144 | regmap_reg_range(BD9571MWV_INT_INTREQ, BD9571MWV_INT_INTREQ), |
145 | }; |
146 | |
147 | static const struct regmap_access_table bd9574mwf_volatile_table = { |
148 | .yes_ranges = bd9574mwf_volatile_yes_ranges, |
149 | .n_yes_ranges = ARRAY_SIZE(bd9574mwf_volatile_yes_ranges), |
150 | }; |
151 | |
152 | static const struct regmap_config bd9574mwf_regmap_config = { |
153 | .reg_bits = 8, |
154 | .val_bits = 8, |
155 | .cache_type = REGCACHE_MAPLE, |
156 | .rd_table = &bd9574mwf_readable_table, |
157 | .wr_table = &bd9574mwf_writable_table, |
158 | .volatile_table = &bd9574mwf_volatile_table, |
159 | .max_register = 0xff, |
160 | }; |
161 | |
162 | static struct regmap_irq_chip bd9574mwf_irq_chip = { |
163 | .name = "bd9574mwf" , |
164 | .status_base = BD9571MWV_INT_INTREQ, |
165 | .mask_base = BD9571MWV_INT_INTMASK, |
166 | .ack_base = BD9571MWV_INT_INTREQ, |
167 | .init_ack_masked = true, |
168 | .num_regs = 1, |
169 | .irqs = bd9571mwv_irqs, |
170 | .num_irqs = ARRAY_SIZE(bd9571mwv_irqs), |
171 | }; |
172 | |
173 | static int bd957x_identify(struct device *dev, struct regmap *regmap) |
174 | { |
175 | unsigned int value; |
176 | int ret; |
177 | |
178 | ret = regmap_read(map: regmap, BD9571MWV_VENDOR_CODE, val: &value); |
179 | if (ret) { |
180 | dev_err(dev, "Failed to read vendor code register (ret=%i)\n" , |
181 | ret); |
182 | return ret; |
183 | } |
184 | |
185 | if (value != BD9571MWV_VENDOR_CODE_VAL) { |
186 | dev_err(dev, "Invalid vendor code ID %02x (expected %02x)\n" , |
187 | value, BD9571MWV_VENDOR_CODE_VAL); |
188 | return -EINVAL; |
189 | } |
190 | |
191 | ret = regmap_read(map: regmap, BD9571MWV_PRODUCT_CODE, val: &value); |
192 | if (ret) { |
193 | dev_err(dev, "Failed to read product code register (ret=%i)\n" , |
194 | ret); |
195 | return ret; |
196 | } |
197 | ret = regmap_read(map: regmap, BD9571MWV_PRODUCT_REVISION, val: &value); |
198 | if (ret) { |
199 | dev_err(dev, "Failed to read revision register (ret=%i)\n" , |
200 | ret); |
201 | return ret; |
202 | } |
203 | |
204 | return 0; |
205 | } |
206 | |
207 | static int bd9571mwv_probe(struct i2c_client *client) |
208 | { |
209 | const struct regmap_config *regmap_config; |
210 | const struct regmap_irq_chip *irq_chip; |
211 | const struct mfd_cell *cells; |
212 | struct device *dev = &client->dev; |
213 | struct regmap *regmap; |
214 | struct regmap_irq_chip_data *irq_data; |
215 | int ret, num_cells, irq = client->irq; |
216 | |
217 | /* Read the PMIC product code */ |
218 | ret = i2c_smbus_read_byte_data(client, BD9571MWV_PRODUCT_CODE); |
219 | if (ret < 0) { |
220 | dev_err(dev, "Failed to read product code\n" ); |
221 | return ret; |
222 | } |
223 | |
224 | switch (ret) { |
225 | case BD9571MWV_PRODUCT_CODE_BD9571MWV: |
226 | regmap_config = &bd9571mwv_regmap_config; |
227 | irq_chip = &bd9571mwv_irq_chip; |
228 | cells = bd9571mwv_cells; |
229 | num_cells = ARRAY_SIZE(bd9571mwv_cells); |
230 | break; |
231 | case BD9571MWV_PRODUCT_CODE_BD9574MWF: |
232 | regmap_config = &bd9574mwf_regmap_config; |
233 | irq_chip = &bd9574mwf_irq_chip; |
234 | cells = bd9574mwf_cells; |
235 | num_cells = ARRAY_SIZE(bd9574mwf_cells); |
236 | break; |
237 | default: |
238 | dev_err(dev, "Unsupported device 0x%x\n" , ret); |
239 | return -ENODEV; |
240 | } |
241 | |
242 | regmap = devm_regmap_init_i2c(client, regmap_config); |
243 | if (IS_ERR(ptr: regmap)) { |
244 | dev_err(dev, "Failed to initialize register map\n" ); |
245 | return PTR_ERR(ptr: regmap); |
246 | } |
247 | |
248 | ret = bd957x_identify(dev, regmap); |
249 | if (ret) |
250 | return ret; |
251 | |
252 | ret = devm_regmap_add_irq_chip(dev, map: regmap, irq, IRQF_ONESHOT, irq_base: 0, |
253 | chip: irq_chip, data: &irq_data); |
254 | if (ret) { |
255 | dev_err(dev, "Failed to register IRQ chip\n" ); |
256 | return ret; |
257 | } |
258 | |
259 | return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, cells, n_devs: num_cells, |
260 | NULL, irq_base: 0, irq_domain: regmap_irq_get_domain(data: irq_data)); |
261 | } |
262 | |
263 | static const struct of_device_id bd9571mwv_of_match_table[] = { |
264 | { .compatible = "rohm,bd9571mwv" , }, |
265 | { .compatible = "rohm,bd9574mwf" , }, |
266 | { /* sentinel */ } |
267 | }; |
268 | MODULE_DEVICE_TABLE(of, bd9571mwv_of_match_table); |
269 | |
270 | static const struct i2c_device_id bd9571mwv_id_table[] = { |
271 | { "bd9571mwv" , 0 }, |
272 | { /* sentinel */ } |
273 | }; |
274 | MODULE_DEVICE_TABLE(i2c, bd9571mwv_id_table); |
275 | |
276 | static struct i2c_driver bd9571mwv_driver = { |
277 | .driver = { |
278 | .name = "bd9571mwv" , |
279 | .of_match_table = bd9571mwv_of_match_table, |
280 | }, |
281 | .probe = bd9571mwv_probe, |
282 | .id_table = bd9571mwv_id_table, |
283 | }; |
284 | module_i2c_driver(bd9571mwv_driver); |
285 | |
286 | MODULE_AUTHOR("Marek Vasut <marek.vasut+renesas@gmail.com>" ); |
287 | MODULE_DESCRIPTION("BD9571MWV PMIC Driver" ); |
288 | MODULE_LICENSE("GPL v2" ); |
289 | |