]>
Commit | Line | Data |
---|---|---|
e95ed23b NS |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * | |
4 | * This file is provided under a dual BSD/GPLv2 license. When using or | |
5 | * redistributing this file, you may do so under either license. | |
6 | * | |
7 | * GPL LICENSE SUMMARY | |
8 | * | |
9 | * Copyright (C) 2018 Advanced Micro Devices, Inc. All Rights Reserved. | |
10 | * | |
11 | * This program is free software; you can redistribute it and/or modify | |
12 | * it under the terms of version 2 of the GNU General Public License as | |
13 | * published by the Free Software Foundation. | |
14 | * | |
15 | * BSD LICENSE | |
16 | * | |
17 | * Copyright (C) 2018 Advanced Micro Devices, Inc. All Rights Reserved. | |
18 | * | |
19 | * Redistribution and use in source and binary forms, with or without | |
20 | * modification, are permitted provided that the following conditions | |
21 | * are met: | |
22 | * | |
23 | * * Redistributions of source code must retain the above copyright | |
24 | * notice, this list of conditions and the following disclaimer. | |
25 | * * Redistributions in binary form must reproduce the above copy | |
26 | * notice, this list of conditions and the following disclaimer in | |
27 | * the documentation and/or other materials provided with the | |
28 | * distribution. | |
29 | * * Neither the name of AMD Corporation nor the names of its | |
30 | * contributors may be used to endorse or promote products derived | |
31 | * from this software without specific prior written permission. | |
32 | * | |
33 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
34 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
35 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
36 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
37 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
38 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
39 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
40 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
41 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
42 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
43 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
44 | * | |
45 | ||
46 | * AMD I2C Platform Driver | |
47 | * Author: Nehal Bakulchandra Shah <Nehal-bakulchandra.shah@amd.com> | |
48 | */ | |
49 | ||
50 | #include <linux/kernel.h> | |
51 | #include <linux/module.h> | |
52 | #include <linux/types.h> | |
53 | #include <linux/slab.h> | |
54 | #include <linux/i2c.h> | |
55 | #include <linux/platform_device.h> | |
56 | #include <linux/acpi.h> | |
57 | #include <linux/delay.h> | |
58 | #include "i2c-amd-pci-mp2.h" | |
59 | #include <linux/dma-mapping.h> | |
60 | #define DRIVER_NAME "AMD-I2C-PLATDRV" | |
61 | ||
62 | struct amd_i2c_dev { | |
63 | struct platform_device *pdev; | |
64 | struct i2c_adapter adapter; | |
65 | struct amd_i2c_common i2c_common; | |
66 | struct completion msg_complete; | |
67 | struct i2c_msg *msg_buf; | |
68 | bool is_configured; | |
69 | u8 bus_id; | |
70 | u8 *buf; | |
71 | ||
72 | }; | |
73 | ||
8a9dee1f | 74 | static int i2c_amd_read_completion(struct i2c_event event, void *dev_ctx) |
e95ed23b NS |
75 | { |
76 | struct amd_i2c_dev *i2c_dev = (struct amd_i2c_dev *)dev_ctx; | |
77 | struct amd_i2c_common *commond = &i2c_dev->i2c_common; | |
8a9dee1f KHF |
78 | int i; |
79 | int buf_len; | |
e95ed23b | 80 | |
8a9dee1f KHF |
81 | if (event.base.r.status == i2c_readcomplete_event) { |
82 | if (event.base.r.length <= 32) { | |
e95ed23b NS |
83 | pr_devel(" in %s i2c_dev->msg_buf :%p\n", |
84 | __func__, i2c_dev->msg_buf); | |
85 | ||
86 | memcpy(i2c_dev->msg_buf->buf, | |
8a9dee1f | 87 | (unsigned char *)event.buf, event.base.r.length); |
e95ed23b | 88 | |
8a9dee1f KHF |
89 | buf_len = (event.base.r.length + 3) / 4; |
90 | for (i = 0; i < buf_len; i++) | |
e95ed23b NS |
91 | pr_devel("%s:%s readdata:%x\n", |
92 | DRIVER_NAME, __func__, event.buf[i]); | |
93 | ||
94 | } else { | |
95 | memcpy(i2c_dev->msg_buf->buf, | |
96 | (unsigned char *)commond->read_cfg.buf, | |
8a9dee1f | 97 | event.base.r.length); |
e95ed23b NS |
98 | pr_devel("%s:%s virt:%llx phy_addr:%llx\n", |
99 | DRIVER_NAME, __func__, | |
100 | (u64)commond->read_cfg.buf, | |
101 | (u64)commond->read_cfg.phy_addr); | |
102 | ||
8a9dee1f KHF |
103 | buf_len = (event.base.r.length + 3) / 4; |
104 | for (i = 0; i < buf_len; i++) | |
e95ed23b NS |
105 | pr_devel("%s:%s readdata:%x\n", |
106 | DRIVER_NAME, __func__, ((unsigned int *) | |
107 | commond->read_cfg.buf)[i]); | |
108 | } | |
109 | ||
110 | complete(&i2c_dev->msg_complete); | |
111 | } | |
112 | ||
113 | return 0; | |
114 | } | |
115 | ||
8a9dee1f | 116 | static int i2c_amd_write_completion(struct i2c_event event, void *dev_ctx) |
e95ed23b NS |
117 | { |
118 | struct amd_i2c_dev *i2c_dev = (struct amd_i2c_dev *)dev_ctx; | |
119 | ||
8a9dee1f | 120 | if (event.base.r.status == i2c_writecomplete_event) |
e95ed23b NS |
121 | complete(&i2c_dev->msg_complete); |
122 | ||
123 | return 0; | |
124 | } | |
125 | ||
8a9dee1f | 126 | static int i2c_amd_connect_completion(struct i2c_event event, void *dev_ctx) |
e95ed23b NS |
127 | { |
128 | struct amd_i2c_dev *i2c_dev = (struct amd_i2c_dev *)dev_ctx; | |
129 | ||
8a9dee1f | 130 | if (event.base.r.status == i2c_busenable_complete) |
e95ed23b NS |
131 | complete(&i2c_dev->msg_complete); |
132 | ||
133 | return 0; | |
134 | } | |
135 | ||
136 | static const struct amd_i2c_pci_ops data_handler = { | |
137 | .read_complete = i2c_amd_read_completion, | |
138 | .write_complete = i2c_amd_write_completion, | |
139 | .connect_complete = i2c_amd_connect_completion, | |
140 | }; | |
141 | ||
142 | static int i2c_amd_pci_configure(struct amd_i2c_dev *i2c_dev, int slaveaddr) | |
143 | { | |
144 | struct amd_i2c_common *i2c_common = &i2c_dev->i2c_common; | |
145 | int ret; | |
146 | ||
147 | amd_i2c_register_cb(i2c_common->pdev, &data_handler, (void *)i2c_dev); | |
148 | i2c_common->connect_cfg.bus_id = i2c_dev->bus_id; | |
149 | i2c_common->connect_cfg.dev_addr = slaveaddr; | |
150 | i2c_common->connect_cfg.i2c_speed = speed400k; | |
151 | ||
152 | ret = amd_mp2_connect(i2c_common->pdev, i2c_common->connect_cfg); | |
153 | if (ret) | |
154 | return -1; | |
155 | ||
156 | mdelay(100); | |
157 | ||
158 | i2c_common->write_cfg.bus_id = i2c_dev->bus_id; | |
159 | i2c_common->write_cfg.dev_addr = slaveaddr; | |
160 | i2c_common->write_cfg.i2c_speed = speed400k; | |
161 | ||
162 | i2c_common->read_cfg.bus_id = i2c_dev->bus_id; | |
163 | i2c_common->read_cfg.dev_addr = slaveaddr; | |
164 | i2c_common->read_cfg.i2c_speed = speed400k; | |
165 | ||
166 | return 0; | |
167 | } | |
168 | ||
169 | static int i2c_amd_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) | |
170 | { | |
171 | struct amd_i2c_dev *dev = i2c_get_adapdata(adap); | |
172 | struct amd_i2c_common *i2c_common = &dev->i2c_common; | |
173 | ||
8a9dee1f | 174 | int i; |
e95ed23b NS |
175 | unsigned long timeout; |
176 | struct i2c_msg *pmsg; | |
177 | unsigned char *dma_buf = NULL; | |
178 | ||
179 | dma_addr_t phys; | |
180 | ||
181 | reinit_completion(&dev->msg_complete); | |
182 | if (dev->is_configured == 0) { | |
183 | i2c_amd_pci_configure(dev, msgs->addr); | |
184 | timeout = wait_for_completion_timeout(&dev->msg_complete, 50); | |
185 | dev->is_configured = 1; | |
186 | } | |
187 | ||
188 | for (i = 0; i < num; i++) { | |
189 | pmsg = &msgs[i]; | |
190 | if (pmsg->flags & I2C_M_RD) { | |
191 | if (pmsg->len <= 32) { | |
192 | i2c_common->read_cfg.buf = dev->buf; | |
193 | i2c_common->read_cfg.length = pmsg->len; | |
194 | i2c_common->read_cfg.phy_addr = | |
195 | virt_to_phys(dev->buf); | |
196 | } else { | |
197 | dma_buf = (u8 *)dma_alloc_coherent(&i2c_common->pdev->dev, | |
198 | pmsg->len, &phys, GFP_KERNEL); | |
199 | ||
200 | if (!dma_buf) | |
201 | return -ENOMEM; | |
202 | ||
203 | i2c_common->read_cfg.buf = dma_buf; | |
204 | i2c_common->read_cfg.length = pmsg->len; | |
205 | i2c_common->read_cfg.phy_addr = phys; | |
206 | } | |
207 | dev->msg_buf = pmsg; | |
208 | amd_mp2_read(i2c_common->pdev, | |
209 | i2c_common->read_cfg); | |
210 | timeout = wait_for_completion_timeout | |
211 | (&dev->msg_complete, 50); | |
212 | if (pmsg->len > 32 && dma_buf) | |
213 | dma_free_coherent(&i2c_common->pdev->dev, | |
214 | pmsg->len, dma_buf, phys); | |
215 | ||
216 | } else { | |
217 | i2c_common->write_cfg.buf = (unsigned int *)pmsg->buf; | |
218 | i2c_common->write_cfg.length = pmsg->len; | |
219 | amd_mp2_write(i2c_common->pdev, | |
220 | i2c_common->write_cfg); | |
221 | ||
222 | timeout = wait_for_completion_timeout | |
223 | (&dev->msg_complete, 50); | |
224 | } | |
225 | } | |
226 | return num; | |
227 | } | |
228 | ||
229 | static u32 i2c_amd_func(struct i2c_adapter *a) | |
230 | { | |
231 | return I2C_FUNC_I2C; | |
232 | } | |
233 | ||
234 | static const struct i2c_algorithm i2c_amd_algorithm = { | |
235 | .master_xfer = i2c_amd_xfer, | |
236 | .functionality = i2c_amd_func, | |
237 | }; | |
238 | ||
239 | static int i2c_amd_probe(struct platform_device *pdev) | |
240 | { | |
241 | int ret; | |
242 | struct amd_i2c_dev *i2c_dev; | |
243 | struct device *dev = &pdev->dev; | |
244 | acpi_handle handle = ACPI_HANDLE(&pdev->dev); | |
245 | struct acpi_device *adev; | |
246 | const char *uid = NULL; | |
247 | ||
248 | i2c_dev = devm_kzalloc(dev, sizeof(*i2c_dev), GFP_KERNEL); | |
249 | if (!i2c_dev) | |
250 | return -ENOMEM; | |
251 | ||
252 | i2c_dev->pdev = pdev; | |
253 | ||
254 | if (!acpi_bus_get_device(handle, &adev)) { | |
255 | pr_err(" i2c0 pdev->id=%s\n", adev->pnp.unique_id); | |
256 | uid = adev->pnp.unique_id; | |
257 | } | |
258 | ||
259 | if (strcmp(uid, "0") == 0) { | |
260 | pr_err(" bus id is 0\n"); | |
261 | i2c_dev->bus_id = 0; | |
262 | } | |
263 | ||
264 | pr_devel(" i2c1 pdev->id=%s\n", uid); | |
265 | if (strcmp(uid, "1") == 0) { | |
266 | pr_err(" bus id is 1\n"); | |
267 | i2c_dev->bus_id = 1; | |
268 | } | |
269 | /* setup i2c adapter description */ | |
270 | i2c_dev->adapter.owner = THIS_MODULE; | |
271 | i2c_dev->adapter.algo = &i2c_amd_algorithm; | |
272 | i2c_dev->adapter.dev.parent = dev; | |
273 | i2c_dev->adapter.algo_data = i2c_dev; | |
274 | ACPI_COMPANION_SET(&i2c_dev->adapter.dev, ACPI_COMPANION(&pdev->dev)); | |
275 | i2c_dev->adapter.dev.of_node = dev->of_node; | |
276 | snprintf(i2c_dev->adapter.name, sizeof(i2c_dev->adapter.name), "%s-%s", | |
277 | "i2c_dev-i2c", dev_name(pdev->dev.parent)); | |
278 | ||
279 | i2c_dev->i2c_common.pdev = pci_get_device(PCI_VENDOR_ID_AMD, | |
280 | PCI_DEVICE_ID_AMD_MP2, NULL); | |
281 | ||
282 | if (!i2c_dev->i2c_common.pdev) { | |
283 | pr_err("%s Could not find pdev in i2c\n", __func__); | |
284 | return -EINVAL; | |
285 | } | |
286 | i2c_dev->buf = kzalloc(32, GFP_KERNEL); | |
287 | ||
288 | if (!i2c_dev->buf) | |
289 | return -ENOMEM; | |
290 | ||
291 | platform_set_drvdata(pdev, i2c_dev); | |
292 | ||
293 | i2c_set_adapdata(&i2c_dev->adapter, i2c_dev); | |
294 | ||
295 | init_completion(&i2c_dev->msg_complete); | |
296 | /* and finally attach to i2c layer */ | |
297 | ret = i2c_add_adapter(&i2c_dev->adapter); | |
298 | ||
299 | if (ret < 0) | |
300 | pr_err(" i2c add adpater failed =%d", ret); | |
301 | ||
302 | return ret; | |
303 | } | |
304 | ||
305 | static int i2c_amd_remove(struct platform_device *pdev) | |
306 | { | |
307 | struct amd_i2c_dev *i2c_dev = platform_get_drvdata(pdev); | |
308 | ||
309 | kfree(i2c_dev->buf); | |
310 | i2c_del_adapter(&i2c_dev->adapter); | |
311 | ||
312 | return 0; | |
313 | } | |
314 | ||
315 | static const struct acpi_device_id i2c_amd_acpi_match[] = { | |
316 | { "AMDI0011" }, | |
317 | { }, | |
318 | }; | |
319 | ||
320 | static struct platform_driver amd_i2c_plat_driver = { | |
321 | .probe = i2c_amd_probe, | |
322 | .remove = i2c_amd_remove, | |
323 | .driver = { | |
324 | .name = "i2c_amd_platdrv", | |
325 | .acpi_match_table = ACPI_PTR | |
326 | (i2c_amd_acpi_match), | |
327 | }, | |
328 | }; | |
329 | ||
330 | module_platform_driver(amd_i2c_plat_driver); | |
331 | ||
332 | MODULE_AUTHOR("Nehal Shah <nehal-bakulchandra.shah@amd.com>"); | |
333 | MODULE_DESCRIPTION("AMD I2C Platform Driver"); | |
334 | MODULE_LICENSE("Dual BSD/GPL"); | |
335 |