Commit | Line | Data |
---|---|---|
c942fddf | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
a06347c0 RD |
2 | /* ------------------------------------------------------------------------- |
3 | * Copyright (C) 2014-2016, Intel Corporation | |
4 | * | |
a06347c0 RD |
5 | * ------------------------------------------------------------------------- |
6 | */ | |
7 | ||
8 | #include <linux/module.h> | |
9 | #include <linux/acpi.h> | |
10 | #include <linux/i2c.h> | |
11 | #include <linux/interrupt.h> | |
12 | #include <linux/nfc.h> | |
13 | #include <linux/delay.h> | |
14 | #include <linux/gpio/consumer.h> | |
15 | #include <net/nfc/nfc.h> | |
16 | #include <net/nfc/nci_core.h> | |
17 | ||
18 | #include "fdp.h" | |
19 | ||
20 | #define FDP_I2C_DRIVER_NAME "fdp_nci_i2c" | |
21 | ||
a06347c0 RD |
22 | #define FDP_DP_CLOCK_TYPE_NAME "clock-type" |
23 | #define FDP_DP_CLOCK_FREQ_NAME "clock-freq" | |
24 | #define FDP_DP_FW_VSC_CFG_NAME "fw-vsc-cfg" | |
25 | ||
26 | #define FDP_FRAME_HEADROOM 2 | |
27 | #define FDP_FRAME_TAILROOM 1 | |
28 | ||
29 | #define FDP_NCI_I2C_MIN_PAYLOAD 5 | |
30 | #define FDP_NCI_I2C_MAX_PAYLOAD 261 | |
31 | ||
32 | #define FDP_POWER_OFF 0 | |
33 | #define FDP_POWER_ON 1 | |
34 | ||
35 | #define fdp_nci_i2c_dump_skb(dev, prefix, skb) \ | |
36 | print_hex_dump(KERN_DEBUG, prefix": ", DUMP_PREFIX_OFFSET, \ | |
37 | 16, 1, (skb)->data, (skb)->len, 0) | |
38 | ||
39 | static void fdp_nci_i2c_reset(struct fdp_i2c_phy *phy) | |
40 | { | |
41 | /* Reset RST/WakeUP for at least 100 micro-second */ | |
42 | gpiod_set_value_cansleep(phy->power_gpio, FDP_POWER_OFF); | |
43 | usleep_range(1000, 4000); | |
44 | gpiod_set_value_cansleep(phy->power_gpio, FDP_POWER_ON); | |
45 | usleep_range(10000, 14000); | |
46 | } | |
47 | ||
48 | static int fdp_nci_i2c_enable(void *phy_id) | |
49 | { | |
50 | struct fdp_i2c_phy *phy = phy_id; | |
51 | ||
52 | dev_dbg(&phy->i2c_dev->dev, "%s\n", __func__); | |
53 | fdp_nci_i2c_reset(phy); | |
54 | ||
55 | return 0; | |
56 | } | |
57 | ||
58 | static void fdp_nci_i2c_disable(void *phy_id) | |
59 | { | |
60 | struct fdp_i2c_phy *phy = phy_id; | |
61 | ||
62 | dev_dbg(&phy->i2c_dev->dev, "%s\n", __func__); | |
63 | fdp_nci_i2c_reset(phy); | |
64 | } | |
65 | ||
66 | static void fdp_nci_i2c_add_len_lrc(struct sk_buff *skb) | |
67 | { | |
68 | u8 lrc = 0; | |
69 | u16 len, i; | |
70 | ||
71 | /* Add length header */ | |
72 | len = skb->len; | |
d58ff351 JB |
73 | *(u8 *)skb_push(skb, 1) = len & 0xff; |
74 | *(u8 *)skb_push(skb, 1) = len >> 8; | |
a06347c0 RD |
75 | |
76 | /* Compute and add lrc */ | |
77 | for (i = 0; i < len + 2; i++) | |
78 | lrc ^= skb->data[i]; | |
79 | ||
634fef61 | 80 | skb_put_u8(skb, lrc); |
a06347c0 RD |
81 | } |
82 | ||
83 | static void fdp_nci_i2c_remove_len_lrc(struct sk_buff *skb) | |
84 | { | |
85 | skb_pull(skb, FDP_FRAME_HEADROOM); | |
86 | skb_trim(skb, skb->len - FDP_FRAME_TAILROOM); | |
87 | } | |
88 | ||
89 | static int fdp_nci_i2c_write(void *phy_id, struct sk_buff *skb) | |
90 | { | |
91 | struct fdp_i2c_phy *phy = phy_id; | |
92 | struct i2c_client *client = phy->i2c_dev; | |
93 | int r; | |
94 | ||
95 | if (phy->hard_fault != 0) | |
96 | return phy->hard_fault; | |
97 | ||
98 | fdp_nci_i2c_add_len_lrc(skb); | |
99 | fdp_nci_i2c_dump_skb(&client->dev, "fdp_wr", skb); | |
100 | ||
101 | r = i2c_master_send(client, skb->data, skb->len); | |
102 | if (r == -EREMOTEIO) { /* Retry, chip was in standby */ | |
103 | usleep_range(1000, 4000); | |
104 | r = i2c_master_send(client, skb->data, skb->len); | |
105 | } | |
106 | ||
107 | if (r < 0 || r != skb->len) | |
108 | dev_dbg(&client->dev, "%s: error err=%d len=%d\n", | |
109 | __func__, r, skb->len); | |
110 | ||
111 | if (r >= 0) { | |
112 | if (r != skb->len) { | |
113 | phy->hard_fault = r; | |
114 | r = -EREMOTEIO; | |
115 | } else { | |
116 | r = 0; | |
117 | } | |
118 | } | |
119 | ||
120 | fdp_nci_i2c_remove_len_lrc(skb); | |
121 | ||
122 | return r; | |
123 | } | |
124 | ||
125 | static struct nfc_phy_ops i2c_phy_ops = { | |
126 | .write = fdp_nci_i2c_write, | |
127 | .enable = fdp_nci_i2c_enable, | |
128 | .disable = fdp_nci_i2c_disable, | |
129 | }; | |
130 | ||
131 | static int fdp_nci_i2c_read(struct fdp_i2c_phy *phy, struct sk_buff **skb) | |
132 | { | |
133 | int r, len; | |
134 | u8 tmp[FDP_NCI_I2C_MAX_PAYLOAD], lrc, k; | |
135 | u16 i; | |
136 | struct i2c_client *client = phy->i2c_dev; | |
137 | ||
138 | *skb = NULL; | |
139 | ||
140 | /* Read the length packet and the data packet */ | |
141 | for (k = 0; k < 2; k++) { | |
142 | ||
143 | len = phy->next_read_size; | |
144 | ||
145 | r = i2c_master_recv(client, tmp, len); | |
146 | if (r != len) { | |
147 | dev_dbg(&client->dev, "%s: i2c recv err: %d\n", | |
148 | __func__, r); | |
149 | goto flush; | |
150 | } | |
151 | ||
152 | /* Check packet integruty */ | |
153 | for (lrc = i = 0; i < r; i++) | |
154 | lrc ^= tmp[i]; | |
155 | ||
156 | /* | |
157 | * LRC check failed. This may due to transmission error or | |
158 | * desynchronization between driver and FDP. Drop the paquet | |
159 | * and force resynchronization | |
160 | */ | |
161 | if (lrc) { | |
162 | dev_dbg(&client->dev, "%s: corrupted packet\n", | |
163 | __func__); | |
164 | phy->next_read_size = 5; | |
165 | goto flush; | |
166 | } | |
167 | ||
168 | /* Packet that contains a length */ | |
169 | if (tmp[0] == 0 && tmp[1] == 0) { | |
170 | phy->next_read_size = (tmp[2] << 8) + tmp[3] + 3; | |
171 | } else { | |
172 | phy->next_read_size = FDP_NCI_I2C_MIN_PAYLOAD; | |
173 | ||
174 | *skb = alloc_skb(len, GFP_KERNEL); | |
175 | if (*skb == NULL) { | |
176 | r = -ENOMEM; | |
177 | goto flush; | |
178 | } | |
179 | ||
59ae1d12 | 180 | skb_put_data(*skb, tmp, len); |
a06347c0 RD |
181 | fdp_nci_i2c_dump_skb(&client->dev, "fdp_rd", *skb); |
182 | ||
183 | fdp_nci_i2c_remove_len_lrc(*skb); | |
184 | } | |
185 | } | |
186 | ||
187 | return 0; | |
188 | ||
189 | flush: | |
190 | /* Flush the remaining data */ | |
191 | if (i2c_master_recv(client, tmp, sizeof(tmp)) < 0) | |
192 | r = -EREMOTEIO; | |
193 | ||
194 | return r; | |
195 | } | |
196 | ||
197 | static irqreturn_t fdp_nci_i2c_irq_thread_fn(int irq, void *phy_id) | |
198 | { | |
199 | struct fdp_i2c_phy *phy = phy_id; | |
200 | struct i2c_client *client; | |
201 | struct sk_buff *skb; | |
202 | int r; | |
203 | ||
a06347c0 RD |
204 | if (!phy || irq != phy->i2c_dev->irq) { |
205 | WARN_ON_ONCE(1); | |
206 | return IRQ_NONE; | |
207 | } | |
208 | ||
b6355fb3 SM |
209 | client = phy->i2c_dev; |
210 | dev_dbg(&client->dev, "%s\n", __func__); | |
211 | ||
a06347c0 RD |
212 | r = fdp_nci_i2c_read(phy, &skb); |
213 | ||
214 | if (r == -EREMOTEIO) | |
215 | return IRQ_HANDLED; | |
216 | else if (r == -ENOMEM || r == -EBADMSG) | |
217 | return IRQ_HANDLED; | |
218 | ||
219 | if (skb != NULL) | |
220 | fdp_nci_recv_frame(phy->ndev, skb); | |
221 | ||
222 | return IRQ_HANDLED; | |
223 | } | |
224 | ||
225 | static void fdp_nci_i2c_read_device_properties(struct device *dev, | |
226 | u8 *clock_type, u32 *clock_freq, | |
227 | u8 **fw_vsc_cfg) | |
228 | { | |
229 | int r; | |
230 | u8 len; | |
231 | ||
232 | r = device_property_read_u8(dev, FDP_DP_CLOCK_TYPE_NAME, clock_type); | |
233 | if (r) { | |
234 | dev_dbg(dev, "Using default clock type"); | |
235 | *clock_type = 0; | |
236 | } | |
237 | ||
238 | r = device_property_read_u32(dev, FDP_DP_CLOCK_FREQ_NAME, clock_freq); | |
239 | if (r) { | |
240 | dev_dbg(dev, "Using default clock frequency\n"); | |
241 | *clock_freq = 26000; | |
242 | } | |
243 | ||
244 | if (device_property_present(dev, FDP_DP_FW_VSC_CFG_NAME)) { | |
245 | r = device_property_read_u8(dev, FDP_DP_FW_VSC_CFG_NAME, | |
246 | &len); | |
247 | ||
248 | if (r || len <= 0) | |
249 | goto vsc_read_err; | |
250 | ||
251 | /* Add 1 to the length to inclue the length byte itself */ | |
252 | len++; | |
253 | ||
3c4211ba KC |
254 | *fw_vsc_cfg = devm_kmalloc_array(dev, |
255 | len, sizeof(**fw_vsc_cfg), | |
a06347c0 RD |
256 | GFP_KERNEL); |
257 | ||
258 | r = device_property_read_u8_array(dev, FDP_DP_FW_VSC_CFG_NAME, | |
259 | *fw_vsc_cfg, len); | |
260 | ||
261 | if (r) { | |
517ce4e9 | 262 | devm_kfree(dev, *fw_vsc_cfg); |
a06347c0 RD |
263 | goto vsc_read_err; |
264 | } | |
265 | } else { | |
266 | vsc_read_err: | |
267 | dev_dbg(dev, "FW vendor specific commands not present\n"); | |
268 | *fw_vsc_cfg = NULL; | |
269 | } | |
270 | ||
271 | dev_dbg(dev, "Clock type: %d, clock frequency: %d, VSC: %s", | |
272 | *clock_type, *clock_freq, *fw_vsc_cfg != NULL ? "yes" : "no"); | |
273 | } | |
274 | ||
a5d41094 AS |
275 | static const struct acpi_gpio_params power_gpios = { 0, 0, false }; |
276 | ||
277 | static const struct acpi_gpio_mapping acpi_fdp_gpios[] = { | |
278 | { "power-gpios", &power_gpios, 1 }, | |
279 | {}, | |
280 | }; | |
281 | ||
8597c092 | 282 | static int fdp_nci_i2c_probe(struct i2c_client *client) |
a06347c0 RD |
283 | { |
284 | struct fdp_i2c_phy *phy; | |
285 | struct device *dev = &client->dev; | |
286 | u8 *fw_vsc_cfg; | |
287 | u8 clock_type; | |
288 | u32 clock_freq; | |
289 | int r = 0; | |
290 | ||
291 | dev_dbg(dev, "%s\n", __func__); | |
292 | ||
293 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | |
294 | nfc_err(dev, "No I2C_FUNC_I2C support\n"); | |
295 | return -ENODEV; | |
296 | } | |
297 | ||
0b0a264d CR |
298 | /* Checking if we have an irq */ |
299 | if (client->irq <= 0) { | |
300 | nfc_err(dev, "IRQ not present\n"); | |
301 | return -ENODEV; | |
302 | } | |
303 | ||
7b9fcda9 | 304 | phy = devm_kzalloc(dev, sizeof(struct fdp_i2c_phy), GFP_KERNEL); |
a06347c0 RD |
305 | if (!phy) |
306 | return -ENOMEM; | |
307 | ||
308 | phy->i2c_dev = client; | |
309 | phy->next_read_size = FDP_NCI_I2C_MIN_PAYLOAD; | |
310 | i2c_set_clientdata(client, phy); | |
311 | ||
7b9fcda9 AS |
312 | r = devm_request_threaded_irq(dev, client->irq, |
313 | NULL, fdp_nci_i2c_irq_thread_fn, | |
314 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, | |
315 | FDP_I2C_DRIVER_NAME, phy); | |
a06347c0 RD |
316 | |
317 | if (r < 0) { | |
318 | nfc_err(&client->dev, "Unable to register IRQ handler\n"); | |
319 | return r; | |
320 | } | |
321 | ||
a5d41094 AS |
322 | r = devm_acpi_dev_add_driver_gpios(dev, acpi_fdp_gpios); |
323 | if (r) | |
324 | dev_dbg(dev, "Unable to add GPIO mapping table\n"); | |
a06347c0 | 325 | |
a5d41094 AS |
326 | /* Requesting the power gpio */ |
327 | phy->power_gpio = devm_gpiod_get(dev, "power", GPIOD_OUT_LOW); | |
a06347c0 RD |
328 | if (IS_ERR(phy->power_gpio)) { |
329 | nfc_err(dev, "Power GPIO request failed\n"); | |
330 | return PTR_ERR(phy->power_gpio); | |
331 | } | |
332 | ||
333 | /* read device properties to get the clock and production settings */ | |
334 | fdp_nci_i2c_read_device_properties(dev, &clock_type, &clock_freq, | |
335 | &fw_vsc_cfg); | |
336 | ||
337 | /* Call the NFC specific probe function */ | |
338 | r = fdp_nci_probe(phy, &i2c_phy_ops, &phy->ndev, | |
339 | FDP_FRAME_HEADROOM, FDP_FRAME_TAILROOM, | |
340 | clock_type, clock_freq, fw_vsc_cfg); | |
341 | if (r < 0) { | |
342 | nfc_err(dev, "NCI probing error\n"); | |
343 | return r; | |
344 | } | |
345 | ||
346 | dev_dbg(dev, "I2C driver loaded\n"); | |
347 | return 0; | |
348 | } | |
349 | ||
350 | static int fdp_nci_i2c_remove(struct i2c_client *client) | |
351 | { | |
352 | struct fdp_i2c_phy *phy = i2c_get_clientdata(client); | |
353 | ||
354 | dev_dbg(&client->dev, "%s\n", __func__); | |
355 | ||
356 | fdp_nci_remove(phy->ndev); | |
357 | fdp_nci_i2c_disable(phy); | |
358 | ||
359 | return 0; | |
360 | } | |
361 | ||
a06347c0 RD |
362 | static const struct acpi_device_id fdp_nci_i2c_acpi_match[] = { |
363 | {"INT339A", 0}, | |
364 | {} | |
365 | }; | |
366 | MODULE_DEVICE_TABLE(acpi, fdp_nci_i2c_acpi_match); | |
367 | ||
368 | static struct i2c_driver fdp_nci_i2c_driver = { | |
369 | .driver = { | |
370 | .name = FDP_I2C_DRIVER_NAME, | |
371 | .acpi_match_table = ACPI_PTR(fdp_nci_i2c_acpi_match), | |
372 | }, | |
8597c092 | 373 | .probe_new = fdp_nci_i2c_probe, |
a06347c0 RD |
374 | .remove = fdp_nci_i2c_remove, |
375 | }; | |
376 | module_i2c_driver(fdp_nci_i2c_driver); | |
377 | ||
378 | MODULE_LICENSE("GPL"); | |
379 | MODULE_DESCRIPTION("I2C driver for Intel Fields Peak NFC controller"); | |
380 | MODULE_AUTHOR("Robert Dolca <robert.dolca@intel.com>"); |