1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2022 Richtek Technology Corp. |
4 | * |
5 | * Author: ChiYuan Huang <cy_huang@richtek.com> |
6 | */ |
7 | |
8 | #include <linux/bits.h> |
9 | #include <linux/bitfield.h> |
10 | #include <linux/i2c.h> |
11 | #include <linux/interrupt.h> |
12 | #include <linux/kernel.h> |
13 | #include <linux/mfd/core.h> |
14 | #include <linux/module.h> |
15 | #include <linux/regmap.h> |
16 | |
17 | #include "mt6370.h" |
18 | |
19 | #define MT6370_REG_DEV_INFO 0x100 |
20 | #define MT6370_REG_CHG_IRQ1 0x1C0 |
21 | #define MT6370_REG_CHG_MASK1 0x1E0 |
22 | #define MT6370_REG_MAXADDR 0x1FF |
23 | |
24 | #define MT6370_VENID_MASK GENMASK(7, 4) |
25 | |
26 | #define MT6370_NUM_IRQREGS 16 |
27 | #define MT6370_USBC_I2CADDR 0x4E |
28 | #define MT6370_MAX_ADDRLEN 2 |
29 | |
30 | #define MT6370_VENID_RT5081 0x8 |
31 | #define MT6370_VENID_RT5081A 0xA |
32 | #define MT6370_VENID_MT6370 0xE |
33 | #define MT6370_VENID_MT6371 0xF |
34 | #define MT6370_VENID_MT6372P 0x9 |
35 | #define MT6370_VENID_MT6372CP 0xB |
36 | |
37 | static const struct regmap_irq mt6370_irqs[] = { |
38 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHGON, 8), |
39 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_TREG, 8), |
40 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_AICR, 8), |
41 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_MIVR, 8), |
42 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_PWR_RDY, 8), |
43 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_FL_CHG_VINOVP, 8), |
44 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_VSYSUV, 8), |
45 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_VSYSOV, 8), |
46 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_VBATOV, 8), |
47 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_VINOVPCHG, 8), |
48 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_TS_BAT_COLD, 8), |
49 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_TS_BAT_COOL, 8), |
50 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_TS_BAT_WARM, 8), |
51 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_TS_BAT_HOT, 8), |
52 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_TS_STATC, 8), |
53 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_FAULT, 8), |
54 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_STATC, 8), |
55 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_TMR, 8), |
56 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_BATABS, 8), |
57 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_ADPBAD, 8), |
58 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_RVP, 8), |
59 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_TSHUTDOWN, 8), |
60 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_IINMEAS, 8), |
61 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_ICCMEAS, 8), |
62 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHGDET_DONE, 8), |
63 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_WDTMR, 8), |
64 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_SSFINISH, 8), |
65 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_RECHG, 8), |
66 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_TERM, 8), |
67 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_IEOC, 8), |
68 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_ADC_DONE, 8), |
69 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_PUMPX_DONE, 8), |
70 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_BST_BATUV, 8), |
71 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_BST_MIDOV, 8), |
72 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_BST_OLP, 8), |
73 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_ATTACH, 8), |
74 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_DETACH, 8), |
75 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_HVDCP_STPDONE, 8), |
76 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_HVDCP_VBUSDET_DONE, 8), |
77 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_HVDCP_DET, 8), |
78 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHGDET, 8), |
79 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_DCDT, 8), |
80 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHG_VGOK, 8), |
81 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHG_WDTMR, 8), |
82 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHG_UC, 8), |
83 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHG_OC, 8), |
84 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHG_OV, 8), |
85 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_OVPCTRL_SWON, 8), |
86 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_OVPCTRL_UVP_D, 8), |
87 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_OVPCTRL_UVP, 8), |
88 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_OVPCTRL_OVP_D, 8), |
89 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_OVPCTRL_OVP, 8), |
90 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED_STRBPIN, 8), |
91 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED_TORPIN, 8), |
92 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED_TX, 8), |
93 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED_LVF, 8), |
94 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED2_SHORT, 8), |
95 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED1_SHORT, 8), |
96 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED2_STRB, 8), |
97 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED1_STRB, 8), |
98 | REGMAP_IRQ_REG_LINE(mT6370_IRQ_FLED2_STRB_TO, 8), |
99 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED1_STRB_TO, 8), |
100 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED2_TOR, 8), |
101 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED1_TOR, 8), |
102 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_OTP, 8), |
103 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_VDDA_OVP, 8), |
104 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_VDDA_UV, 8), |
105 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_LDO_OC, 8), |
106 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_BLED_OCP, 8), |
107 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_BLED_OVP, 8), |
108 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_DSV_VNEG_OCP, 8), |
109 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_DSV_VPOS_OCP, 8), |
110 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_DSV_BST_OCP, 8), |
111 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_DSV_VNEG_SCP, 8), |
112 | REGMAP_IRQ_REG_LINE(MT6370_IRQ_DSV_VPOS_SCP, 8), |
113 | }; |
114 | |
115 | static const struct regmap_irq_chip mt6370_irq_chip = { |
116 | .name = "mt6370-irqs" , |
117 | .status_base = MT6370_REG_CHG_IRQ1, |
118 | .mask_base = MT6370_REG_CHG_MASK1, |
119 | .num_regs = MT6370_NUM_IRQREGS, |
120 | .irqs = mt6370_irqs, |
121 | .num_irqs = ARRAY_SIZE(mt6370_irqs), |
122 | }; |
123 | |
124 | static const struct resource mt6370_regulator_irqs[] = { |
125 | DEFINE_RES_IRQ_NAMED(MT6370_IRQ_DSV_VPOS_SCP, "db_vpos_scp" ), |
126 | DEFINE_RES_IRQ_NAMED(MT6370_IRQ_DSV_VNEG_SCP, "db_vneg_scp" ), |
127 | DEFINE_RES_IRQ_NAMED(MT6370_IRQ_DSV_BST_OCP, "db_vbst_ocp" ), |
128 | DEFINE_RES_IRQ_NAMED(MT6370_IRQ_DSV_VPOS_OCP, "db_vpos_ocp" ), |
129 | DEFINE_RES_IRQ_NAMED(MT6370_IRQ_DSV_VNEG_OCP, "db_vneg_ocp" ), |
130 | DEFINE_RES_IRQ_NAMED(MT6370_IRQ_LDO_OC, "ldo_oc" ), |
131 | }; |
132 | |
133 | static const struct mfd_cell mt6370_devices[] = { |
134 | MFD_CELL_OF("mt6370-adc" , |
135 | NULL, NULL, 0, 0, "mediatek,mt6370-adc" ), |
136 | MFD_CELL_OF("mt6370-charger" , |
137 | NULL, NULL, 0, 0, "mediatek,mt6370-charger" ), |
138 | MFD_CELL_OF("mt6370-flashlight" , |
139 | NULL, NULL, 0, 0, "mediatek,mt6370-flashlight" ), |
140 | MFD_CELL_OF("mt6370-indicator" , |
141 | NULL, NULL, 0, 0, "mediatek,mt6370-indicator" ), |
142 | MFD_CELL_OF("mt6370-tcpc" , |
143 | NULL, NULL, 0, 0, "mediatek,mt6370-tcpc" ), |
144 | MFD_CELL_RES("mt6370-regulator" , mt6370_regulator_irqs), |
145 | }; |
146 | |
147 | static const struct mfd_cell mt6370_exclusive_devices[] = { |
148 | MFD_CELL_OF("mt6370-backlight" , |
149 | NULL, NULL, 0, 0, "mediatek,mt6370-backlight" ), |
150 | }; |
151 | |
152 | static const struct mfd_cell mt6372_exclusive_devices[] = { |
153 | MFD_CELL_OF("mt6370-backlight" , |
154 | NULL, NULL, 0, 0, "mediatek,mt6372-backlight" ), |
155 | }; |
156 | |
157 | static int mt6370_check_vendor_info(struct device *dev, struct regmap *rmap, |
158 | int *vid) |
159 | { |
160 | unsigned int devinfo; |
161 | int ret; |
162 | |
163 | ret = regmap_read(map: rmap, MT6370_REG_DEV_INFO, val: &devinfo); |
164 | if (ret) |
165 | return ret; |
166 | |
167 | *vid = FIELD_GET(MT6370_VENID_MASK, devinfo); |
168 | switch (*vid) { |
169 | case MT6370_VENID_RT5081: |
170 | case MT6370_VENID_RT5081A: |
171 | case MT6370_VENID_MT6370: |
172 | case MT6370_VENID_MT6371: |
173 | case MT6370_VENID_MT6372P: |
174 | case MT6370_VENID_MT6372CP: |
175 | return 0; |
176 | default: |
177 | dev_err(dev, "Unknown Vendor ID 0x%02x\n" , devinfo); |
178 | return -ENODEV; |
179 | } |
180 | } |
181 | |
182 | static int mt6370_regmap_read(void *context, const void *reg_buf, |
183 | size_t reg_size, void *val_buf, size_t val_size) |
184 | { |
185 | struct mt6370_info *info = context; |
186 | const u8 *u8_buf = reg_buf; |
187 | u8 bank_idx, bank_addr; |
188 | int ret; |
189 | |
190 | bank_idx = u8_buf[0]; |
191 | bank_addr = u8_buf[1]; |
192 | |
193 | ret = i2c_smbus_read_i2c_block_data(client: info->i2c[bank_idx], command: bank_addr, |
194 | length: val_size, values: val_buf); |
195 | if (ret < 0) |
196 | return ret; |
197 | |
198 | if (ret != val_size) |
199 | return -EIO; |
200 | |
201 | return 0; |
202 | } |
203 | |
204 | static int mt6370_regmap_write(void *context, const void *data, size_t count) |
205 | { |
206 | struct mt6370_info *info = context; |
207 | const u8 *u8_buf = data; |
208 | u8 bank_idx, bank_addr; |
209 | int len = count - MT6370_MAX_ADDRLEN; |
210 | |
211 | bank_idx = u8_buf[0]; |
212 | bank_addr = u8_buf[1]; |
213 | |
214 | return i2c_smbus_write_i2c_block_data(client: info->i2c[bank_idx], command: bank_addr, |
215 | length: len, values: data + MT6370_MAX_ADDRLEN); |
216 | } |
217 | |
218 | static const struct regmap_bus mt6370_regmap_bus = { |
219 | .read = mt6370_regmap_read, |
220 | .write = mt6370_regmap_write, |
221 | }; |
222 | |
223 | static const struct regmap_config mt6370_regmap_config = { |
224 | .reg_bits = 16, |
225 | .val_bits = 8, |
226 | .reg_format_endian = REGMAP_ENDIAN_BIG, |
227 | .max_register = MT6370_REG_MAXADDR, |
228 | }; |
229 | |
230 | static int mt6370_probe(struct i2c_client *i2c) |
231 | { |
232 | struct mt6370_info *info; |
233 | struct i2c_client *usbc_i2c; |
234 | struct regmap *regmap; |
235 | struct device *dev = &i2c->dev; |
236 | int ret, vid; |
237 | |
238 | info = devm_kzalloc(dev, size: sizeof(*info), GFP_KERNEL); |
239 | if (!info) |
240 | return -ENOMEM; |
241 | |
242 | usbc_i2c = devm_i2c_new_dummy_device(dev, adap: i2c->adapter, |
243 | MT6370_USBC_I2CADDR); |
244 | if (IS_ERR(ptr: usbc_i2c)) |
245 | return dev_err_probe(dev, err: PTR_ERR(ptr: usbc_i2c), |
246 | fmt: "Failed to register USBC I2C client\n" ); |
247 | |
248 | /* Assign I2C client for PMU and TypeC */ |
249 | info->i2c[MT6370_PMU_I2C] = i2c; |
250 | info->i2c[MT6370_USBC_I2C] = usbc_i2c; |
251 | |
252 | regmap = devm_regmap_init(dev, &mt6370_regmap_bus, |
253 | info, &mt6370_regmap_config); |
254 | if (IS_ERR(ptr: regmap)) |
255 | return dev_err_probe(dev, err: PTR_ERR(ptr: regmap), |
256 | fmt: "Failed to init regmap\n" ); |
257 | |
258 | ret = mt6370_check_vendor_info(dev, rmap: regmap, vid: &vid); |
259 | if (ret) |
260 | return dev_err_probe(dev, err: ret, fmt: "Failed to check vendor info\n" ); |
261 | |
262 | ret = devm_regmap_add_irq_chip(dev, map: regmap, irq: i2c->irq, |
263 | IRQF_ONESHOT, irq_base: -1, chip: &mt6370_irq_chip, |
264 | data: &info->irq_data); |
265 | if (ret) |
266 | return dev_err_probe(dev, err: ret, fmt: "Failed to add irq chip\n" ); |
267 | |
268 | switch (vid) { |
269 | case MT6370_VENID_MT6372P: |
270 | case MT6370_VENID_MT6372CP: |
271 | ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, |
272 | cells: mt6372_exclusive_devices, |
273 | ARRAY_SIZE(mt6372_exclusive_devices), |
274 | NULL, irq_base: 0, |
275 | irq_domain: regmap_irq_get_domain(data: info->irq_data)); |
276 | break; |
277 | default: |
278 | ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, |
279 | cells: mt6370_exclusive_devices, |
280 | ARRAY_SIZE(mt6370_exclusive_devices), |
281 | NULL, irq_base: 0, |
282 | irq_domain: regmap_irq_get_domain(data: info->irq_data)); |
283 | break; |
284 | } |
285 | |
286 | if (ret) |
287 | return dev_err_probe(dev, err: ret, fmt: "Failed to add the exclusive devices\n" ); |
288 | |
289 | return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, |
290 | cells: mt6370_devices, ARRAY_SIZE(mt6370_devices), |
291 | NULL, irq_base: 0, |
292 | irq_domain: regmap_irq_get_domain(data: info->irq_data)); |
293 | } |
294 | |
295 | static const struct of_device_id mt6370_match_table[] = { |
296 | { .compatible = "mediatek,mt6370" }, |
297 | {} |
298 | }; |
299 | MODULE_DEVICE_TABLE(of, mt6370_match_table); |
300 | |
301 | static struct i2c_driver mt6370_driver = { |
302 | .driver = { |
303 | .name = "mt6370" , |
304 | .of_match_table = mt6370_match_table, |
305 | }, |
306 | .probe = mt6370_probe, |
307 | }; |
308 | module_i2c_driver(mt6370_driver); |
309 | |
310 | MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>" ); |
311 | MODULE_DESCRIPTION("MediaTek MT6370 SubPMIC Driver" ); |
312 | MODULE_LICENSE("GPL v2" ); |
313 | |