diff options
author | Dirk Brandewie <dirk.brandewie@gmail.com> | 2011-10-06 14:26:35 -0400 |
---|---|---|
committer | Ben Dooks <ben-linux@fluff.org> | 2011-10-29 06:03:52 -0400 |
commit | fe20ff5c7e9ca7f5369aacc7d7ca3efeda3b90fe (patch) | |
tree | 67100c5dd0fe8a212063eada32ac4ee9eca48983 /drivers/i2c/busses/i2c-designware-pcidrv.c | |
parent | f3fa9f3da5621154323775ff0efdba99dcebcee4 (diff) |
i2c-designware: Add support for Designware core behind PCI devices.
Signed-off-by: Dirk Brandewie <dirk.brandewie@gmail.com>
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Diffstat (limited to 'drivers/i2c/busses/i2c-designware-pcidrv.c')
-rw-r--r-- | drivers/i2c/busses/i2c-designware-pcidrv.c | 316 |
1 files changed, 316 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..83307ef3b41f --- /dev/null +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c | |||
@@ -0,0 +1,316 @@ | |||
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 "i2c-designware-core.h" | ||
42 | |||
43 | #define DRIVER_NAME "i2c-designware-pci" | ||
44 | |||
45 | enum dw_pci_ctl_id_t { | ||
46 | moorestown_0, | ||
47 | moorestown_1, | ||
48 | moorestown_2, | ||
49 | |||
50 | medfield_0, | ||
51 | medfield_1, | ||
52 | medfield_2, | ||
53 | medfield_3, | ||
54 | medfield_4, | ||
55 | medfield_5, | ||
56 | }; | ||
57 | |||
58 | struct dw_pci_controller { | ||
59 | u32 bus_num; | ||
60 | u32 bus_cfg; | ||
61 | u32 tx_fifo_depth; | ||
62 | u32 rx_fifo_depth; | ||
63 | u32 clk_khz; | ||
64 | }; | ||
65 | |||
66 | #define INTEL_MID_STD_CFG (DW_IC_CON_MASTER | \ | ||
67 | DW_IC_CON_SLAVE_DISABLE | \ | ||
68 | DW_IC_CON_RESTART_EN) | ||
69 | |||
70 | static struct dw_pci_controller dw_pci_controllers[] = { | ||
71 | [moorestown_0] = { | ||
72 | .bus_num = 0, | ||
73 | .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, | ||
74 | .tx_fifo_depth = 32, | ||
75 | .rx_fifo_depth = 32, | ||
76 | .clk_khz = 25000, | ||
77 | }, | ||
78 | [moorestown_1] = { | ||
79 | .bus_num = 1, | ||
80 | .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, | ||
81 | .tx_fifo_depth = 32, | ||
82 | .rx_fifo_depth = 32, | ||
83 | .clk_khz = 25000, | ||
84 | }, | ||
85 | [moorestown_2] = { | ||
86 | .bus_num = 2, | ||
87 | .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, | ||
88 | .tx_fifo_depth = 32, | ||
89 | .rx_fifo_depth = 32, | ||
90 | .clk_khz = 25000, | ||
91 | }, | ||
92 | [medfield_0] = { | ||
93 | .bus_num = 0, | ||
94 | .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, | ||
95 | .tx_fifo_depth = 32, | ||
96 | .rx_fifo_depth = 32, | ||
97 | .clk_khz = 25000, | ||
98 | }, | ||
99 | [medfield_1] = { | ||
100 | .bus_num = 1, | ||
101 | .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, | ||
102 | .tx_fifo_depth = 32, | ||
103 | .rx_fifo_depth = 32, | ||
104 | .clk_khz = 25000, | ||
105 | }, | ||
106 | [medfield_2] = { | ||
107 | .bus_num = 2, | ||
108 | .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, | ||
109 | .tx_fifo_depth = 32, | ||
110 | .rx_fifo_depth = 32, | ||
111 | .clk_khz = 25000, | ||
112 | }, | ||
113 | [medfield_3] = { | ||
114 | .bus_num = 3, | ||
115 | .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_STD, | ||
116 | .tx_fifo_depth = 32, | ||
117 | .rx_fifo_depth = 32, | ||
118 | .clk_khz = 25000, | ||
119 | }, | ||
120 | [medfield_4] = { | ||
121 | .bus_num = 4, | ||
122 | .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, | ||
123 | .tx_fifo_depth = 32, | ||
124 | .rx_fifo_depth = 32, | ||
125 | .clk_khz = 25000, | ||
126 | }, | ||
127 | [medfield_5] = { | ||
128 | .bus_num = 5, | ||
129 | .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST, | ||
130 | .tx_fifo_depth = 32, | ||
131 | .rx_fifo_depth = 32, | ||
132 | .clk_khz = 25000, | ||
133 | }, | ||
134 | }; | ||
135 | static struct i2c_algorithm i2c_dw_algo = { | ||
136 | .master_xfer = i2c_dw_xfer, | ||
137 | .functionality = i2c_dw_func, | ||
138 | }; | ||
139 | |||
140 | static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev) | ||
141 | { | ||
142 | return dev->controller->clk_khz; | ||
143 | } | ||
144 | |||
145 | static int __devinit i2c_dw_pci_probe(struct pci_dev *pdev, | ||
146 | const struct pci_device_id *id) | ||
147 | { | ||
148 | struct dw_i2c_dev *dev; | ||
149 | struct i2c_adapter *adap; | ||
150 | unsigned long start, len; | ||
151 | void __iomem *base; | ||
152 | int r; | ||
153 | struct dw_pci_controller *controller; | ||
154 | |||
155 | if (id->driver_data >= ARRAY_SIZE(dw_pci_controllers)) { | ||
156 | printk(KERN_ERR "dw_i2c_pci_probe: invalid driver data %ld\n", | ||
157 | id->driver_data); | ||
158 | return -EINVAL; | ||
159 | } | ||
160 | |||
161 | controller = &dw_pci_controllers[id->driver_data]; | ||
162 | |||
163 | r = pci_enable_device(pdev); | ||
164 | if (r) { | ||
165 | dev_err(&pdev->dev, "Failed to enable I2C PCI device (%d)\n", | ||
166 | r); | ||
167 | goto exit; | ||
168 | } | ||
169 | |||
170 | /* Determine the address of the I2C area */ | ||
171 | start = pci_resource_start(pdev, 0); | ||
172 | len = pci_resource_len(pdev, 0); | ||
173 | if (!start || len == 0) { | ||
174 | dev_err(&pdev->dev, "base address not set\n"); | ||
175 | r = -ENODEV; | ||
176 | goto exit; | ||
177 | } | ||
178 | |||
179 | r = pci_request_region(pdev, 0, DRIVER_NAME); | ||
180 | if (r) { | ||
181 | dev_err(&pdev->dev, "failed to request I2C region " | ||
182 | "0x%lx-0x%lx\n", start, | ||
183 | (unsigned long)pci_resource_end(pdev, 0)); | ||
184 | goto exit; | ||
185 | } | ||
186 | |||
187 | base = ioremap_nocache(start, len); | ||
188 | if (!base) { | ||
189 | dev_err(&pdev->dev, "I/O memory remapping failed\n"); | ||
190 | r = -ENOMEM; | ||
191 | goto err_release_region; | ||
192 | } | ||
193 | |||
194 | |||
195 | dev = kzalloc(sizeof(struct dw_i2c_dev), GFP_KERNEL); | ||
196 | if (!dev) { | ||
197 | r = -ENOMEM; | ||
198 | goto err_release_region; | ||
199 | } | ||
200 | |||
201 | init_completion(&dev->cmd_complete); | ||
202 | mutex_init(&dev->lock); | ||
203 | dev->clk = NULL; | ||
204 | dev->controller = controller; | ||
205 | dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz; | ||
206 | dev->base = base; | ||
207 | dev->dev = get_device(&pdev->dev); | ||
208 | dev->functionality = | ||
209 | I2C_FUNC_I2C | | ||
210 | I2C_FUNC_SMBUS_BYTE | | ||
211 | I2C_FUNC_SMBUS_BYTE_DATA | | ||
212 | I2C_FUNC_SMBUS_WORD_DATA | | ||
213 | I2C_FUNC_SMBUS_I2C_BLOCK; | ||
214 | dev->master_cfg = controller->bus_cfg; | ||
215 | |||
216 | pci_set_drvdata(pdev, dev); | ||
217 | |||
218 | dev->tx_fifo_depth = controller->tx_fifo_depth; | ||
219 | dev->rx_fifo_depth = controller->rx_fifo_depth; | ||
220 | r = i2c_dw_init(dev); | ||
221 | if (r) | ||
222 | goto err_iounmap; | ||
223 | |||
224 | adap = &dev->adapter; | ||
225 | i2c_set_adapdata(adap, dev); | ||
226 | adap->owner = THIS_MODULE; | ||
227 | adap->class = 0; | ||
228 | adap->algo = &i2c_dw_algo; | ||
229 | adap->dev.parent = &pdev->dev; | ||
230 | adap->nr = controller->bus_num; | ||
231 | snprintf(adap->name, sizeof(adap->name), "i2c-designware-pci-%d", | ||
232 | adap->nr); | ||
233 | |||
234 | r = request_irq(pdev->irq, i2c_dw_isr, IRQF_SHARED, adap->name, dev); | ||
235 | if (r) { | ||
236 | dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq); | ||
237 | goto err_iounmap; | ||
238 | } | ||
239 | |||
240 | i2c_dw_disable_int(dev); | ||
241 | i2c_dw_clear_int(dev); | ||
242 | r = i2c_add_numbered_adapter(adap); | ||
243 | if (r) { | ||
244 | dev_err(&pdev->dev, "failure adding adapter\n"); | ||
245 | goto err_free_irq; | ||
246 | } | ||
247 | |||
248 | return 0; | ||
249 | |||
250 | err_free_irq: | ||
251 | free_irq(pdev->irq, dev); | ||
252 | err_iounmap: | ||
253 | iounmap(dev->base); | ||
254 | pci_set_drvdata(pdev, NULL); | ||
255 | put_device(&pdev->dev); | ||
256 | kfree(dev); | ||
257 | err_release_region: | ||
258 | pci_release_region(pdev, 0); | ||
259 | exit: | ||
260 | return r; | ||
261 | } | ||
262 | |||
263 | static void __devexit i2c_dw_pci_remove(struct pci_dev *pdev) | ||
264 | { | ||
265 | struct dw_i2c_dev *dev = pci_get_drvdata(pdev); | ||
266 | |||
267 | pci_set_drvdata(pdev, NULL); | ||
268 | i2c_del_adapter(&dev->adapter); | ||
269 | put_device(&pdev->dev); | ||
270 | |||
271 | free_irq(dev->irq, dev); | ||
272 | kfree(dev); | ||
273 | pci_release_region(pdev, 0); | ||
274 | } | ||
275 | |||
276 | /* work with hotplug and coldplug */ | ||
277 | MODULE_ALIAS("i2c_designware-pci"); | ||
278 | |||
279 | DEFINE_PCI_DEVICE_TABLE(i2_designware_pci_ids) = { | ||
280 | /* Moorestown */ | ||
281 | { PCI_VDEVICE(INTEL, 0x0802), moorestown_0 }, | ||
282 | { PCI_VDEVICE(INTEL, 0x0803), moorestown_1 }, | ||
283 | { PCI_VDEVICE(INTEL, 0x0804), moorestown_2 }, | ||
284 | /* Medfield */ | ||
285 | { PCI_VDEVICE(INTEL, 0x0817), medfield_3,}, | ||
286 | { PCI_VDEVICE(INTEL, 0x0818), medfield_4 }, | ||
287 | { PCI_VDEVICE(INTEL, 0x0819), medfield_5 }, | ||
288 | { PCI_VDEVICE(INTEL, 0x082C), medfield_0 }, | ||
289 | { PCI_VDEVICE(INTEL, 0x082D), medfield_1 }, | ||
290 | { PCI_VDEVICE(INTEL, 0x082E), medfield_2 }, | ||
291 | { 0,} | ||
292 | }; | ||
293 | MODULE_DEVICE_TABLE(pci, i2_designware_pci_ids); | ||
294 | |||
295 | static struct pci_driver dw_i2c_driver = { | ||
296 | .name = DRIVER_NAME, | ||
297 | .id_table = i2_designware_pci_ids, | ||
298 | .probe = i2c_dw_pci_probe, | ||
299 | .remove = __devexit_p(i2c_dw_pci_remove), | ||
300 | }; | ||
301 | |||
302 | static int __init dw_i2c_init_driver(void) | ||
303 | { | ||
304 | return pci_register_driver(&dw_i2c_driver); | ||
305 | } | ||
306 | module_init(dw_i2c_init_driver); | ||
307 | |||
308 | static void __exit dw_i2c_exit_driver(void) | ||
309 | { | ||
310 | pci_unregister_driver(&dw_i2c_driver); | ||
311 | } | ||
312 | module_exit(dw_i2c_exit_driver); | ||
313 | |||
314 | MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); | ||
315 | MODULE_DESCRIPTION("Synopsys DesignWare PCI I2C bus adapter"); | ||
316 | MODULE_LICENSE("GPL"); | ||