1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Marvell NFC-over-I2C driver: I2C interface related functions |
4 | * |
5 | * Copyright (C) 2015, Marvell International Ltd. |
6 | */ |
7 | |
8 | #include <linux/module.h> |
9 | #include <linux/interrupt.h> |
10 | #include <linux/i2c.h> |
11 | #include <linux/nfc.h> |
12 | #include <linux/delay.h> |
13 | #include <linux/of_irq.h> |
14 | #include <net/nfc/nci.h> |
15 | #include <net/nfc/nci_core.h> |
16 | #include "nfcmrvl.h" |
17 | |
18 | struct nfcmrvl_i2c_drv_data { |
19 | unsigned long flags; |
20 | struct device *dev; |
21 | struct i2c_client *i2c; |
22 | struct nfcmrvl_private *priv; |
23 | }; |
24 | |
25 | static int nfcmrvl_i2c_read(struct nfcmrvl_i2c_drv_data *drv_data, |
26 | struct sk_buff **skb) |
27 | { |
28 | int ret; |
29 | struct nci_ctrl_hdr nci_hdr; |
30 | |
31 | /* Read NCI header to know the payload size */ |
32 | ret = i2c_master_recv(client: drv_data->i2c, buf: (u8 *)&nci_hdr, NCI_CTRL_HDR_SIZE); |
33 | if (ret != NCI_CTRL_HDR_SIZE) { |
34 | nfc_err(&drv_data->i2c->dev, "cannot read NCI header\n" ); |
35 | return -EBADMSG; |
36 | } |
37 | |
38 | *skb = nci_skb_alloc(ndev: drv_data->priv->ndev, |
39 | len: nci_hdr.plen + NCI_CTRL_HDR_SIZE, GFP_KERNEL); |
40 | if (!*skb) |
41 | return -ENOMEM; |
42 | |
43 | /* Copy NCI header into the SKB */ |
44 | skb_put_data(skb: *skb, data: &nci_hdr, NCI_CTRL_HDR_SIZE); |
45 | |
46 | if (nci_hdr.plen) { |
47 | /* Read the NCI payload */ |
48 | ret = i2c_master_recv(client: drv_data->i2c, |
49 | buf: skb_put(skb: *skb, len: nci_hdr.plen), |
50 | count: nci_hdr.plen); |
51 | |
52 | if (ret != nci_hdr.plen) { |
53 | nfc_err(&drv_data->i2c->dev, |
54 | "Invalid frame payload length: %u (expected %u)\n" , |
55 | ret, nci_hdr.plen); |
56 | kfree_skb(skb: *skb); |
57 | return -EBADMSG; |
58 | } |
59 | } |
60 | |
61 | return 0; |
62 | } |
63 | |
64 | static irqreturn_t nfcmrvl_i2c_int_irq_thread_fn(int irq, void *drv_data_ptr) |
65 | { |
66 | struct nfcmrvl_i2c_drv_data *drv_data = drv_data_ptr; |
67 | struct sk_buff *skb = NULL; |
68 | int ret; |
69 | |
70 | if (!drv_data->priv) |
71 | return IRQ_HANDLED; |
72 | |
73 | if (test_bit(NFCMRVL_PHY_ERROR, &drv_data->priv->flags)) |
74 | return IRQ_HANDLED; |
75 | |
76 | ret = nfcmrvl_i2c_read(drv_data, skb: &skb); |
77 | |
78 | switch (ret) { |
79 | case -EREMOTEIO: |
80 | set_bit(NFCMRVL_PHY_ERROR, addr: &drv_data->priv->flags); |
81 | break; |
82 | case -ENOMEM: |
83 | case -EBADMSG: |
84 | nfc_err(&drv_data->i2c->dev, "read failed %d\n" , ret); |
85 | break; |
86 | default: |
87 | if (nfcmrvl_nci_recv_frame(priv: drv_data->priv, skb) < 0) |
88 | nfc_err(&drv_data->i2c->dev, "corrupted RX packet\n" ); |
89 | break; |
90 | } |
91 | return IRQ_HANDLED; |
92 | } |
93 | |
94 | static int nfcmrvl_i2c_nci_open(struct nfcmrvl_private *priv) |
95 | { |
96 | struct nfcmrvl_i2c_drv_data *drv_data = priv->drv_data; |
97 | |
98 | if (!drv_data) |
99 | return -ENODEV; |
100 | |
101 | return 0; |
102 | } |
103 | |
104 | static int nfcmrvl_i2c_nci_close(struct nfcmrvl_private *priv) |
105 | { |
106 | return 0; |
107 | } |
108 | |
109 | static int nfcmrvl_i2c_nci_send(struct nfcmrvl_private *priv, |
110 | struct sk_buff *skb) |
111 | { |
112 | struct nfcmrvl_i2c_drv_data *drv_data = priv->drv_data; |
113 | int ret; |
114 | |
115 | if (test_bit(NFCMRVL_PHY_ERROR, &priv->flags)) { |
116 | kfree_skb(skb); |
117 | return -EREMOTEIO; |
118 | } |
119 | |
120 | ret = i2c_master_send(client: drv_data->i2c, buf: skb->data, count: skb->len); |
121 | |
122 | /* Retry if chip was in standby */ |
123 | if (ret == -EREMOTEIO) { |
124 | nfc_info(drv_data->dev, "chip may sleep, retry\n" ); |
125 | usleep_range(min: 6000, max: 10000); |
126 | ret = i2c_master_send(client: drv_data->i2c, buf: skb->data, count: skb->len); |
127 | } |
128 | |
129 | if (ret >= 0) { |
130 | if (ret != skb->len) { |
131 | nfc_err(drv_data->dev, |
132 | "Invalid length sent: %u (expected %u)\n" , |
133 | ret, skb->len); |
134 | ret = -EREMOTEIO; |
135 | } else |
136 | ret = 0; |
137 | } |
138 | |
139 | if (ret) { |
140 | kfree_skb(skb); |
141 | return ret; |
142 | } |
143 | |
144 | consume_skb(skb); |
145 | return 0; |
146 | } |
147 | |
148 | static void nfcmrvl_i2c_nci_update_config(struct nfcmrvl_private *priv, |
149 | const void *param) |
150 | { |
151 | } |
152 | |
153 | static const struct nfcmrvl_if_ops i2c_ops = { |
154 | .nci_open = nfcmrvl_i2c_nci_open, |
155 | .nci_close = nfcmrvl_i2c_nci_close, |
156 | .nci_send = nfcmrvl_i2c_nci_send, |
157 | .nci_update_config = nfcmrvl_i2c_nci_update_config, |
158 | }; |
159 | |
160 | static int nfcmrvl_i2c_parse_dt(struct device_node *node, |
161 | struct nfcmrvl_platform_data *pdata) |
162 | { |
163 | int ret; |
164 | |
165 | ret = nfcmrvl_parse_dt(node, pdata); |
166 | if (ret < 0) { |
167 | pr_err("Failed to get generic entries\n" ); |
168 | return ret; |
169 | } |
170 | |
171 | if (of_property_read_bool(np: node, propname: "i2c-int-falling" )) |
172 | pdata->irq_polarity = IRQF_TRIGGER_FALLING; |
173 | else |
174 | pdata->irq_polarity = IRQF_TRIGGER_RISING; |
175 | |
176 | ret = irq_of_parse_and_map(node, index: 0); |
177 | if (!ret) { |
178 | pr_err("Unable to get irq\n" ); |
179 | return -EINVAL; |
180 | } |
181 | pdata->irq = ret; |
182 | |
183 | return 0; |
184 | } |
185 | |
186 | static int nfcmrvl_i2c_probe(struct i2c_client *client) |
187 | { |
188 | const struct nfcmrvl_platform_data *pdata; |
189 | struct nfcmrvl_i2c_drv_data *drv_data; |
190 | struct nfcmrvl_platform_data config; |
191 | int ret; |
192 | |
193 | if (!i2c_check_functionality(adap: client->adapter, I2C_FUNC_I2C)) { |
194 | nfc_err(&client->dev, "Need I2C_FUNC_I2C\n" ); |
195 | return -ENODEV; |
196 | } |
197 | |
198 | drv_data = devm_kzalloc(dev: &client->dev, size: sizeof(*drv_data), GFP_KERNEL); |
199 | if (!drv_data) |
200 | return -ENOMEM; |
201 | |
202 | drv_data->i2c = client; |
203 | drv_data->dev = &client->dev; |
204 | drv_data->priv = NULL; |
205 | |
206 | i2c_set_clientdata(client, data: drv_data); |
207 | |
208 | pdata = client->dev.platform_data; |
209 | |
210 | if (!pdata && client->dev.of_node) |
211 | if (nfcmrvl_i2c_parse_dt(node: client->dev.of_node, pdata: &config) == 0) |
212 | pdata = &config; |
213 | |
214 | if (!pdata) |
215 | return -EINVAL; |
216 | |
217 | /* Request the read IRQ */ |
218 | ret = devm_request_threaded_irq(dev: &drv_data->i2c->dev, irq: pdata->irq, |
219 | NULL, thread_fn: nfcmrvl_i2c_int_irq_thread_fn, |
220 | irqflags: pdata->irq_polarity | IRQF_ONESHOT, |
221 | devname: "nfcmrvl_i2c_int" , dev_id: drv_data); |
222 | if (ret < 0) { |
223 | nfc_err(&drv_data->i2c->dev, |
224 | "Unable to register IRQ handler\n" ); |
225 | return ret; |
226 | } |
227 | |
228 | drv_data->priv = nfcmrvl_nci_register_dev(phy: NFCMRVL_PHY_I2C, |
229 | drv_data, ops: &i2c_ops, |
230 | dev: &drv_data->i2c->dev, pdata); |
231 | |
232 | if (IS_ERR(ptr: drv_data->priv)) |
233 | return PTR_ERR(ptr: drv_data->priv); |
234 | |
235 | drv_data->priv->support_fw_dnld = true; |
236 | |
237 | return 0; |
238 | } |
239 | |
240 | static void nfcmrvl_i2c_remove(struct i2c_client *client) |
241 | { |
242 | struct nfcmrvl_i2c_drv_data *drv_data = i2c_get_clientdata(client); |
243 | |
244 | nfcmrvl_nci_unregister_dev(priv: drv_data->priv); |
245 | } |
246 | |
247 | |
248 | static const struct of_device_id of_nfcmrvl_i2c_match[] __maybe_unused = { |
249 | { .compatible = "marvell,nfc-i2c" , }, |
250 | {}, |
251 | }; |
252 | MODULE_DEVICE_TABLE(of, of_nfcmrvl_i2c_match); |
253 | |
254 | static const struct i2c_device_id nfcmrvl_i2c_id_table[] = { |
255 | { "nfcmrvl_i2c" , 0 }, |
256 | {} |
257 | }; |
258 | MODULE_DEVICE_TABLE(i2c, nfcmrvl_i2c_id_table); |
259 | |
260 | static struct i2c_driver nfcmrvl_i2c_driver = { |
261 | .probe = nfcmrvl_i2c_probe, |
262 | .id_table = nfcmrvl_i2c_id_table, |
263 | .remove = nfcmrvl_i2c_remove, |
264 | .driver = { |
265 | .name = "nfcmrvl_i2c" , |
266 | .of_match_table = of_match_ptr(of_nfcmrvl_i2c_match), |
267 | }, |
268 | }; |
269 | |
270 | module_i2c_driver(nfcmrvl_i2c_driver); |
271 | |
272 | MODULE_AUTHOR("Marvell International Ltd." ); |
273 | MODULE_DESCRIPTION("Marvell NFC-over-I2C driver" ); |
274 | MODULE_LICENSE("GPL v2" ); |
275 | |