diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-designware-pcidrv.c')
-rw-r--r-- | drivers/i2c/busses/i2c-designware-pcidrv.c | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c new file mode 100644 index 000000000000..9e89e7313d62 --- /dev/null +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c | |||
@@ -0,0 +1,392 @@ | |||
1 | /* | ||
2 | * Synopsys DesignWare I2C adapter driver (master only). | ||
3 | * | ||
4 | * Based on the TI DAVINCI I2C adapter driver. | ||
5 | * | ||
6 | * Copyright (C) 2006 Texas Instruments. | ||
7 | * Copyright (C) 2007 MontaVista Software Inc. | ||
8 | * Copyright (C) 2009 Provigent Ltd. | ||
9 | * Copyright (C) 2011 Intel corporation. | ||
10 | * | ||
11 | * ---------------------------------------------------------------------------- | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License as published by | ||
15 | * the Free Software Foundation; either version 2 of the License, or | ||
16 | * (at your option) any later version. | ||
17 | * | ||
18 | * This program is distributed in the hope that it will be useful, | ||
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
21 | * GNU General Public License for more details. | ||
22 | * | ||
23 | * You should have received a copy of the GNU General Public License | ||
24 | * along with this program; if not, write to the Free Software | ||
25 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
26 | * ---------------------------------------------------------------------------- | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <linux/kernel.h> | ||
31 | #include <linux/module.h> | ||
32 | #include <linux/delay.h> | ||
33 | #include <linux/i2c.h> | ||
34 | #include <linux/errno.h> | ||
35 | #include <linux/sched.h> | ||
36 | #include <linux/err.h> | ||
37 | #include <linux/interrupt.h> | ||
38 | #include <linux/io.h> | ||
39 | #include <linux/slab.h> | ||
40 | #include <linux/pci.h> | ||
41 | #include <linux/pm_runtime.h> | ||
42 | #include "i2c-designware-core.h" | ||
43 | |||
44 | #define DRIVER_NAME "i2c-designware-pci" | ||
45 | |||
46 | enum dw_pci_ctl_id_t { | ||
47 | moorestown_0, | ||
48 | moorestown_1, | ||
49 | moorestown_2, | ||
50 | |||
51 | medfield_0, | ||
52 | medfield_1, | ||
53 | medfield_2, | ||
54 | medfield_3, | ||
55 | medfield_4, | ||
56 | medfield_5, | ||
57 | }; | ||
58 | |||
59 | struct dw_pci_controller { | ||
60 | u32 bus_num; | ||
61 | u32 bus_cfg; | ||
62 | u32 tx_fifo_depth; | ||
63 | u32 rx_fifo_depth; | ||
64 | u32 clk_khz; | ||
65 | }; | ||
66 | |||
67 | #define INTEL_MID_STD_CFG (DW_IC_CON_MASTER | \ | ||
68 | DW_IC_CON_SLAVE_DISABLE | \ | ||
69 | DW_IC_CON_RESTART_EN) | ||
70 | |||
71 | static struct dw_pci_controller dw_pci_controllers[] = { | ||
72 | [moorestown_0] = { | ||
73 | .bus_num = 0, | ||
74 | .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, | ||
75 | .tx_fifo_depth = 32, | ||
76 | .rx_fifo_depth = 32, | ||
77 | .clk_khz = 25000, | ||
78 | }, | ||
79 | [moorestown_1] = { | ||
80 | .bus_num = 1, | ||
81 | .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, | ||
82 | .tx_fifo_depth = 32, | ||
83 | .rx_fifo_depth = 32, | ||
84 | .clk_khz = 25000, | ||
85 | }, | ||
86 | [moorestown_2] = { | ||
87 | .bus_num = 2, | ||
88 | .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, | ||
89 | .tx_fifo_depth = 32, | ||
90 | .rx_fifo_depth = 32, | ||
91 | .clk_khz = 25000, | ||
92 | }, | ||
93 | [medfield_0] = { | ||
94 | .bus_num = 0, | ||
95 | .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, | ||
96 | .tx_fifo_depth = 32, | ||
97 | .rx_fifo_depth = 32, | ||
98 | .clk_khz = 25000, | ||
99 | }, | ||
100 | [medfield_1] = { | ||
101 | .bus_num = 1, | ||
102 | .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, | ||
103 | .tx_fifo_depth = 32, | ||
104 | .rx_fifo_depth = 32, | ||
105 | .clk_khz = 25000, | ||
106 | }, | ||
107 | [medfield_2] = { | ||
108 | .bus_num = 2, | ||
109 | .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, | ||
110 | .tx_fifo_depth = 32, | ||
111 | .rx_fifo_depth = 32, | ||
112 | .clk_khz = 25000, | ||
113 | }, | ||
114 | [medfield_3] = { | ||
115 | .bus_num = 3, | ||
116 | .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_STD, | ||
117 | .tx_fifo_depth = 32, | ||
118 | .rx_fifo_depth = 32, | ||
119 | .clk_khz = 25000, | ||
120 | }, | ||
121 | [medfield_4] = { | ||
122 | .bus_num = 4, | ||
123 | .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, | ||
124 | .tx_fifo_depth = 32, | ||
125 | .rx_fifo_depth = 32, | ||
126 | .clk_khz = 25000, | ||
127 | }, | ||
128 | [medfield_5] = { | ||
129 | .bus_num = 5, | ||
130 | .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, | ||
131 | .tx_fifo_depth = 32, | ||
132 | .rx_fifo_depth = 32, | ||
133 | .clk_khz = 25000, | ||
134 | }, | ||
135 | }; | ||
136 | static struct i2c_algorithm i2c_dw_algo = { | ||
137 | .master_xfer = i2c_dw_xfer, | ||
138 | .functionality = i2c_dw_func, | ||
139 | }; | ||
140 | |||
141 | static int i2c_dw_pci_suspend(struct device *dev) | ||
142 | { | ||
143 | struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); | ||
144 | struct dw_i2c_dev *i2c = pci_get_drvdata(pdev); | ||
145 | int err; | ||
146 | |||
147 | |||
148 | i2c_dw_disable(i2c); | ||
149 | |||
150 | err = pci_save_state(pdev); | ||
151 | if (err) { | ||
152 | dev_err(&pdev->dev, "pci_save_state failed\n"); | ||
153 | return err; | ||
154 | } | ||
155 | |||
156 | err = pci_set_power_state(pdev, PCI_D3hot); | ||
157 | if (err) { | ||
158 | dev_err(&pdev->dev, "pci_set_power_state failed\n"); | ||
159 | return err; | ||
160 | } | ||
161 | |||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | static int i2c_dw_pci_resume(struct device *dev) | ||
166 | { | ||
167 | struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); | ||
168 | struct dw_i2c_dev *i2c = pci_get_drvdata(pdev); | ||
169 | int err; | ||
170 | u32 enabled; | ||
171 | |||
172 | enabled = i2c_dw_is_enabled(i2c); | ||
173 | if (enabled) | ||
174 | return 0; | ||
175 | |||
176 | err = pci_set_power_state(pdev, PCI_D0); | ||
177 | if (err) { | ||
178 | dev_err(&pdev->dev, "pci_set_power_state() failed\n"); | ||
179 | return err; | ||
180 | } | ||
181 | |||
182 | pci_restore_state(pdev); | ||
183 | |||
184 | i2c_dw_init(i2c); | ||
185 | i2c_dw_enable(i2c); | ||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | static int i2c_dw_pci_runtime_idle(struct device *dev) | ||
190 | { | ||
191 | int err = pm_schedule_suspend(dev, 500); | ||
192 | dev_dbg(dev, "runtime_idle called\n"); | ||
193 | |||
194 | if (err != 0) | ||
195 | return 0; | ||
196 | return -EBUSY; | ||
197 | } | ||
198 | |||
199 | static const struct dev_pm_ops i2c_dw_pm_ops = { | ||
200 | .resume = i2c_dw_pci_resume, | ||
201 | .suspend = i2c_dw_pci_suspend, | ||
202 | SET_RUNTIME_PM_OPS(i2c_dw_pci_suspend, i2c_dw_pci_resume, | ||
203 | i2c_dw_pci_runtime_idle) | ||
204 | }; | ||
205 | |||
206 | static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev) | ||
207 | { | ||
208 | return dev->controller->clk_khz; | ||
209 | } | ||
210 | |||
211 | static int __devinit i2c_dw_pci_probe(struct pci_dev *pdev, | ||
212 | const struct pci_device_id *id) | ||
213 | { | ||
214 | struct dw_i2c_dev *dev; | ||
215 | struct i2c_adapter *adap; | ||
216 | unsigned long start, len; | ||
217 | void __iomem *base; | ||
218 | int r; | ||
219 | struct dw_pci_controller *controller; | ||
220 | |||
221 | if (id->driver_data >= ARRAY_SIZE(dw_pci_controllers)) { | ||
222 | printk(KERN_ERR "dw_i2c_pci_probe: invalid driver data %ld\n", | ||
223 | id->driver_data); | ||
224 | return -EINVAL; | ||
225 | } | ||
226 | |||
227 | controller = &dw_pci_controllers[id->driver_data]; | ||
228 | |||
229 | r = pci_enable_device(pdev); | ||
230 | if (r) { | ||
231 | dev_err(&pdev->dev, "Failed to enable I2C PCI device (%d)\n", | ||
232 | r); | ||
233 | goto exit; | ||
234 | } | ||
235 | |||
236 | /* Determine the address of the I2C area */ | ||
237 | start = pci_resource_start(pdev, 0); | ||
238 | len = pci_resource_len(pdev, 0); | ||
239 | if (!start || len == 0) { | ||
240 | dev_err(&pdev->dev, "base address not set\n"); | ||
241 | r = -ENODEV; | ||
242 | goto exit; | ||
243 | } | ||
244 | |||
245 | r = pci_request_region(pdev, 0, DRIVER_NAME); | ||
246 | if (r) { | ||
247 | dev_err(&pdev->dev, "failed to request I2C region " | ||
248 | "0x%lx-0x%lx\n", start, | ||
249 | (unsigned long)pci_resource_end(pdev, 0)); | ||
250 | goto exit; | ||
251 | } | ||
252 | |||
253 | base = ioremap_nocache(start, len); | ||
254 | if (!base) { | ||
255 | dev_err(&pdev->dev, "I/O memory remapping failed\n"); | ||
256 | r = -ENOMEM; | ||
257 | goto err_release_region; | ||
258 | } | ||
259 | |||
260 | |||
261 | dev = kzalloc(sizeof(struct dw_i2c_dev), GFP_KERNEL); | ||
262 | if (!dev) { | ||
263 | r = -ENOMEM; | ||
264 | goto err_release_region; | ||
265 | } | ||
266 | |||
267 | init_completion(&dev->cmd_complete); | ||
268 | mutex_init(&dev->lock); | ||
269 | dev->clk = NULL; | ||
270 | dev->controller = controller; | ||
271 | dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz; | ||
272 | dev->base = base; | ||
273 | dev->dev = get_device(&pdev->dev); | ||
274 | dev->functionality = | ||
275 | I2C_FUNC_I2C | | ||
276 | I2C_FUNC_SMBUS_BYTE | | ||
277 | I2C_FUNC_SMBUS_BYTE_DATA | | ||
278 | I2C_FUNC_SMBUS_WORD_DATA | | ||
279 | I2C_FUNC_SMBUS_I2C_BLOCK; | ||
280 | dev->master_cfg = controller->bus_cfg; | ||
281 | |||
282 | pci_set_drvdata(pdev, dev); | ||
283 | |||
284 | dev->tx_fifo_depth = controller->tx_fifo_depth; | ||
285 | dev->rx_fifo_depth = controller->rx_fifo_depth; | ||
286 | r = i2c_dw_init(dev); | ||
287 | if (r) | ||
288 | goto err_iounmap; | ||
289 | |||
290 | adap = &dev->adapter; | ||
291 | i2c_set_adapdata(adap, dev); | ||
292 | adap->owner = THIS_MODULE; | ||
293 | adap->class = 0; | ||
294 | adap->algo = &i2c_dw_algo; | ||
295 | adap->dev.parent = &pdev->dev; | ||
296 | adap->nr = controller->bus_num; | ||
297 | snprintf(adap->name, sizeof(adap->name), "i2c-designware-pci-%d", | ||
298 | adap->nr); | ||
299 | |||
300 | r = request_irq(pdev->irq, i2c_dw_isr, IRQF_SHARED, adap->name, dev); | ||
301 | if (r) { | ||
302 | dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq); | ||
303 | goto err_iounmap; | ||
304 | } | ||
305 | |||
306 | i2c_dw_disable_int(dev); | ||
307 | i2c_dw_clear_int(dev); | ||
308 | r = i2c_add_numbered_adapter(adap); | ||
309 | if (r) { | ||
310 | dev_err(&pdev->dev, "failure adding adapter\n"); | ||
311 | goto err_free_irq; | ||
312 | } | ||
313 | |||
314 | pm_runtime_put_noidle(&pdev->dev); | ||
315 | pm_runtime_allow(&pdev->dev); | ||
316 | |||
317 | return 0; | ||
318 | |||
319 | err_free_irq: | ||
320 | free_irq(pdev->irq, dev); | ||
321 | err_iounmap: | ||
322 | iounmap(dev->base); | ||
323 | pci_set_drvdata(pdev, NULL); | ||
324 | put_device(&pdev->dev); | ||
325 | kfree(dev); | ||
326 | err_release_region: | ||
327 | pci_release_region(pdev, 0); | ||
328 | exit: | ||
329 | return r; | ||
330 | } | ||
331 | |||
332 | static void __devexit i2c_dw_pci_remove(struct pci_dev *pdev) | ||
333 | { | ||
334 | struct dw_i2c_dev *dev = pci_get_drvdata(pdev); | ||
335 | |||
336 | i2c_dw_disable(dev); | ||
337 | pm_runtime_forbid(&pdev->dev); | ||
338 | pm_runtime_get_noresume(&pdev->dev); | ||
339 | |||
340 | pci_set_drvdata(pdev, NULL); | ||
341 | i2c_del_adapter(&dev->adapter); | ||
342 | put_device(&pdev->dev); | ||
343 | |||
344 | free_irq(dev->irq, dev); | ||
345 | kfree(dev); | ||
346 | pci_release_region(pdev, 0); | ||
347 | } | ||
348 | |||
349 | /* work with hotplug and coldplug */ | ||
350 | MODULE_ALIAS("i2c_designware-pci"); | ||
351 | |||
352 | DEFINE_PCI_DEVICE_TABLE(i2_designware_pci_ids) = { | ||
353 | /* Moorestown */ | ||
354 | { PCI_VDEVICE(INTEL, 0x0802), moorestown_0 }, | ||
355 | { PCI_VDEVICE(INTEL, 0x0803), moorestown_1 }, | ||
356 | { PCI_VDEVICE(INTEL, 0x0804), moorestown_2 }, | ||
357 | /* Medfield */ | ||
358 | { PCI_VDEVICE(INTEL, 0x0817), medfield_3,}, | ||
359 | { PCI_VDEVICE(INTEL, 0x0818), medfield_4 }, | ||
360 | { PCI_VDEVICE(INTEL, 0x0819), medfield_5 }, | ||
361 | { PCI_VDEVICE(INTEL, 0x082C), medfield_0 }, | ||
362 | { PCI_VDEVICE(INTEL, 0x082D), medfield_1 }, | ||
363 | { PCI_VDEVICE(INTEL, 0x082E), medfield_2 }, | ||
364 | { 0,} | ||
365 | }; | ||
366 | MODULE_DEVICE_TABLE(pci, i2_designware_pci_ids); | ||
367 | |||
368 | static struct pci_driver dw_i2c_driver = { | ||
369 | .name = DRIVER_NAME, | ||
370 | .id_table = i2_designware_pci_ids, | ||
371 | .probe = i2c_dw_pci_probe, | ||
372 | .remove = __devexit_p(i2c_dw_pci_remove), | ||
373 | .driver = { | ||
374 | .pm = &i2c_dw_pm_ops, | ||
375 | }, | ||
376 | }; | ||
377 | |||
378 | static int __init dw_i2c_init_driver(void) | ||
379 | { | ||
380 | return pci_register_driver(&dw_i2c_driver); | ||
381 | } | ||
382 | module_init(dw_i2c_init_driver); | ||
383 | |||
384 | static void __exit dw_i2c_exit_driver(void) | ||
385 | { | ||
386 | pci_unregister_driver(&dw_i2c_driver); | ||
387 | } | ||
388 | module_exit(dw_i2c_exit_driver); | ||
389 | |||
390 | MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); | ||
391 | MODULE_DESCRIPTION("Synopsys DesignWare PCI I2C bus adapter"); | ||
392 | MODULE_LICENSE("GPL"); | ||