]>
Commit | Line | Data |
---|---|---|
25ca4ae4 BW |
1 | /* |
2 | * Copyright (C) 2017 Spreadtrum Communications Inc. | |
3 | * | |
4 | * This software is licensed under the terms of the GNU General Public | |
5 | * License version 2, as published by the Free Software Foundation, and | |
6 | * may be copied, distributed, and modified under those terms. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | */ | |
13 | ||
14 | #include <linux/interrupt.h> | |
15 | #include <linux/kernel.h> | |
16 | #include <linux/module.h> | |
17 | #include <linux/mfd/core.h> | |
18 | #include <linux/of_device.h> | |
19 | #include <linux/regmap.h> | |
20 | #include <linux/spi/spi.h> | |
21 | ||
22 | #define SPRD_PMIC_INT_MASK_STATUS 0x0 | |
23 | #define SPRD_PMIC_INT_RAW_STATUS 0x4 | |
24 | #define SPRD_PMIC_INT_EN 0x8 | |
25 | ||
26 | #define SPRD_SC2731_IRQ_BASE 0x140 | |
27 | #define SPRD_SC2731_IRQ_NUMS 16 | |
28 | ||
29 | struct sprd_pmic { | |
30 | struct regmap *regmap; | |
31 | struct device *dev; | |
32 | struct regmap_irq *irqs; | |
33 | struct regmap_irq_chip irq_chip; | |
34 | struct regmap_irq_chip_data *irq_data; | |
35 | int irq; | |
36 | }; | |
37 | ||
38 | struct sprd_pmic_data { | |
39 | u32 irq_base; | |
40 | u32 num_irqs; | |
41 | }; | |
42 | ||
43 | /* | |
44 | * Since different PMICs of SC27xx series can have different interrupt | |
45 | * base address and irq number, we should save irq number and irq base | |
46 | * in the device data structure. | |
47 | */ | |
48 | static const struct sprd_pmic_data sc2731_data = { | |
49 | .irq_base = SPRD_SC2731_IRQ_BASE, | |
50 | .num_irqs = SPRD_SC2731_IRQ_NUMS, | |
51 | }; | |
52 | ||
53 | static const struct mfd_cell sprd_pmic_devs[] = { | |
54 | { | |
55 | .name = "sc27xx-wdt", | |
56 | .of_compatible = "sprd,sc27xx-wdt", | |
57 | }, { | |
58 | .name = "sc27xx-rtc", | |
59 | .of_compatible = "sprd,sc27xx-rtc", | |
60 | }, { | |
61 | .name = "sc27xx-charger", | |
62 | .of_compatible = "sprd,sc27xx-charger", | |
63 | }, { | |
64 | .name = "sc27xx-chg-timer", | |
65 | .of_compatible = "sprd,sc27xx-chg-timer", | |
66 | }, { | |
67 | .name = "sc27xx-fast-chg", | |
68 | .of_compatible = "sprd,sc27xx-fast-chg", | |
69 | }, { | |
70 | .name = "sc27xx-chg-wdt", | |
71 | .of_compatible = "sprd,sc27xx-chg-wdt", | |
72 | }, { | |
73 | .name = "sc27xx-typec", | |
74 | .of_compatible = "sprd,sc27xx-typec", | |
75 | }, { | |
76 | .name = "sc27xx-flash", | |
77 | .of_compatible = "sprd,sc27xx-flash", | |
78 | }, { | |
79 | .name = "sc27xx-eic", | |
80 | .of_compatible = "sprd,sc27xx-eic", | |
81 | }, { | |
82 | .name = "sc27xx-efuse", | |
83 | .of_compatible = "sprd,sc27xx-efuse", | |
84 | }, { | |
85 | .name = "sc27xx-thermal", | |
86 | .of_compatible = "sprd,sc27xx-thermal", | |
87 | }, { | |
88 | .name = "sc27xx-adc", | |
89 | .of_compatible = "sprd,sc27xx-adc", | |
90 | }, { | |
91 | .name = "sc27xx-audio-codec", | |
92 | .of_compatible = "sprd,sc27xx-audio-codec", | |
93 | }, { | |
94 | .name = "sc27xx-regulator", | |
95 | .of_compatible = "sprd,sc27xx-regulator", | |
96 | }, { | |
97 | .name = "sc27xx-vibrator", | |
98 | .of_compatible = "sprd,sc27xx-vibrator", | |
99 | }, { | |
100 | .name = "sc27xx-keypad-led", | |
101 | .of_compatible = "sprd,sc27xx-keypad-led", | |
102 | }, { | |
103 | .name = "sc27xx-bltc", | |
104 | .of_compatible = "sprd,sc27xx-bltc", | |
105 | }, { | |
106 | .name = "sc27xx-fgu", | |
107 | .of_compatible = "sprd,sc27xx-fgu", | |
108 | }, { | |
109 | .name = "sc27xx-7sreset", | |
110 | .of_compatible = "sprd,sc27xx-7sreset", | |
111 | }, { | |
112 | .name = "sc27xx-poweroff", | |
113 | .of_compatible = "sprd,sc27xx-poweroff", | |
114 | }, | |
115 | }; | |
116 | ||
117 | static int sprd_pmic_spi_write(void *context, const void *data, size_t count) | |
118 | { | |
119 | struct device *dev = context; | |
120 | struct spi_device *spi = to_spi_device(dev); | |
121 | ||
122 | return spi_write(spi, data, count); | |
123 | } | |
124 | ||
125 | static int sprd_pmic_spi_read(void *context, | |
126 | const void *reg, size_t reg_size, | |
127 | void *val, size_t val_size) | |
128 | { | |
129 | struct device *dev = context; | |
130 | struct spi_device *spi = to_spi_device(dev); | |
131 | u32 rx_buf[2] = { 0 }; | |
132 | int ret; | |
133 | ||
134 | /* Now we only support one PMIC register to read every time. */ | |
135 | if (reg_size != sizeof(u32) || val_size != sizeof(u32)) | |
136 | return -EINVAL; | |
137 | ||
138 | /* Copy address to read from into first element of SPI buffer. */ | |
139 | memcpy(rx_buf, reg, sizeof(u32)); | |
140 | ret = spi_read(spi, rx_buf, 1); | |
141 | if (ret < 0) | |
142 | return ret; | |
143 | ||
144 | memcpy(val, rx_buf, val_size); | |
145 | return 0; | |
146 | } | |
147 | ||
148 | static struct regmap_bus sprd_pmic_regmap = { | |
149 | .write = sprd_pmic_spi_write, | |
150 | .read = sprd_pmic_spi_read, | |
151 | .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, | |
152 | .val_format_endian_default = REGMAP_ENDIAN_NATIVE, | |
153 | }; | |
154 | ||
155 | static const struct regmap_config sprd_pmic_config = { | |
156 | .reg_bits = 32, | |
157 | .val_bits = 32, | |
158 | .reg_stride = 4, | |
159 | .max_register = 0xffff, | |
160 | }; | |
161 | ||
162 | static int sprd_pmic_probe(struct spi_device *spi) | |
163 | { | |
164 | struct sprd_pmic *ddata; | |
165 | const struct sprd_pmic_data *pdata; | |
166 | int ret, i; | |
167 | ||
168 | pdata = of_device_get_match_data(&spi->dev); | |
169 | if (!pdata) { | |
170 | dev_err(&spi->dev, "No matching driver data found\n"); | |
171 | return -EINVAL; | |
172 | } | |
173 | ||
174 | ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL); | |
175 | if (!ddata) | |
176 | return -ENOMEM; | |
177 | ||
178 | ddata->regmap = devm_regmap_init(&spi->dev, &sprd_pmic_regmap, | |
179 | &spi->dev, &sprd_pmic_config); | |
180 | if (IS_ERR(ddata->regmap)) { | |
181 | ret = PTR_ERR(ddata->regmap); | |
182 | dev_err(&spi->dev, "Failed to allocate register map %d\n", ret); | |
183 | return ret; | |
184 | } | |
185 | ||
186 | spi_set_drvdata(spi, ddata); | |
187 | ddata->dev = &spi->dev; | |
188 | ddata->irq = spi->irq; | |
189 | ||
190 | ddata->irq_chip.name = dev_name(&spi->dev); | |
191 | ddata->irq_chip.status_base = | |
192 | pdata->irq_base + SPRD_PMIC_INT_MASK_STATUS; | |
193 | ddata->irq_chip.mask_base = pdata->irq_base + SPRD_PMIC_INT_EN; | |
194 | ddata->irq_chip.ack_base = 0; | |
195 | ddata->irq_chip.num_regs = 1; | |
196 | ddata->irq_chip.num_irqs = pdata->num_irqs; | |
197 | ddata->irq_chip.mask_invert = true; | |
198 | ||
199 | ddata->irqs = devm_kzalloc(&spi->dev, sizeof(struct regmap_irq) * | |
200 | pdata->num_irqs, GFP_KERNEL); | |
201 | if (!ddata->irqs) | |
202 | return -ENOMEM; | |
203 | ||
204 | ddata->irq_chip.irqs = ddata->irqs; | |
205 | for (i = 0; i < pdata->num_irqs; i++) { | |
206 | ddata->irqs[i].reg_offset = i / pdata->num_irqs; | |
207 | ddata->irqs[i].mask = BIT(i % pdata->num_irqs); | |
208 | } | |
209 | ||
210 | ret = devm_regmap_add_irq_chip(&spi->dev, ddata->regmap, ddata->irq, | |
211 | IRQF_ONESHOT | IRQF_NO_SUSPEND, 0, | |
212 | &ddata->irq_chip, &ddata->irq_data); | |
213 | if (ret) { | |
214 | dev_err(&spi->dev, "Failed to add PMIC irq chip %d\n", ret); | |
215 | return ret; | |
216 | } | |
217 | ||
218 | ret = devm_mfd_add_devices(&spi->dev, PLATFORM_DEVID_AUTO, | |
219 | sprd_pmic_devs, ARRAY_SIZE(sprd_pmic_devs), | |
220 | NULL, 0, | |
221 | regmap_irq_get_domain(ddata->irq_data)); | |
222 | if (ret) { | |
223 | dev_err(&spi->dev, "Failed to register device %d\n", ret); | |
224 | return ret; | |
225 | } | |
226 | ||
227 | return 0; | |
228 | } | |
229 | ||
230 | static const struct of_device_id sprd_pmic_match[] = { | |
231 | { .compatible = "sprd,sc2731", .data = &sc2731_data }, | |
232 | {}, | |
233 | }; | |
234 | MODULE_DEVICE_TABLE(of, sprd_pmic_match); | |
235 | ||
236 | static struct spi_driver sprd_pmic_driver = { | |
237 | .driver = { | |
238 | .name = "sc27xx-pmic", | |
239 | .bus = &spi_bus_type, | |
240 | .of_match_table = sprd_pmic_match, | |
241 | }, | |
242 | .probe = sprd_pmic_probe, | |
243 | }; | |
244 | ||
245 | static int __init sprd_pmic_init(void) | |
246 | { | |
247 | return spi_register_driver(&sprd_pmic_driver); | |
248 | } | |
249 | subsys_initcall(sprd_pmic_init); | |
250 | ||
251 | static void __exit sprd_pmic_exit(void) | |
252 | { | |
253 | spi_unregister_driver(&sprd_pmic_driver); | |
254 | } | |
255 | module_exit(sprd_pmic_exit); | |
256 | ||
257 | MODULE_LICENSE("GPL v2"); | |
258 | MODULE_DESCRIPTION("Spreadtrum SC27xx PMICs driver"); | |
259 | MODULE_AUTHOR("Baolin Wang <baolin.wang@spreadtrum.com>"); |