Commit | Line | Data |
---|---|---|
2373f6b9 DB |
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 | * | |
10 | * ---------------------------------------------------------------------------- | |
11 | * | |
12 | * This program is free software; you can redistribute it and/or modify | |
13 | * it under the terms of the GNU General Public License as published by | |
14 | * the Free Software Foundation; either version 2 of the License, or | |
15 | * (at your option) any later version. | |
16 | * | |
17 | * This program is distributed in the hope that it will be useful, | |
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | * GNU General Public License for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU General Public License | |
23 | * along with this program; if not, write to the Free Software | |
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
25 | * ---------------------------------------------------------------------------- | |
26 | * | |
27 | */ | |
28 | #include <linux/kernel.h> | |
29 | #include <linux/module.h> | |
30 | #include <linux/delay.h> | |
31 | #include <linux/i2c.h> | |
32 | #include <linux/clk.h> | |
33 | #include <linux/errno.h> | |
34 | #include <linux/sched.h> | |
35 | #include <linux/err.h> | |
36 | #include <linux/interrupt.h> | |
af71100c | 37 | #include <linux/of_i2c.h> |
2373f6b9 | 38 | #include <linux/platform_device.h> |
3bf3b289 | 39 | #include <linux/pm.h> |
7272194e | 40 | #include <linux/pm_runtime.h> |
2373f6b9 DB |
41 | #include <linux/io.h> |
42 | #include <linux/slab.h> | |
b61b1415 | 43 | #include <linux/acpi.h> |
2373f6b9 DB |
44 | #include "i2c-designware-core.h" |
45 | ||
46 | static struct i2c_algorithm i2c_dw_algo = { | |
47 | .master_xfer = i2c_dw_xfer, | |
48 | .functionality = i2c_dw_func, | |
49 | }; | |
1d31b58f DB |
50 | static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev) |
51 | { | |
52 | return clk_get_rate(dev->clk)/1000; | |
53 | } | |
2373f6b9 | 54 | |
b61b1415 MW |
55 | #ifdef CONFIG_ACPI |
56 | static int dw_i2c_acpi_configure(struct platform_device *pdev) | |
57 | { | |
58 | struct dw_i2c_dev *dev = platform_get_drvdata(pdev); | |
59 | struct acpi_device *adev; | |
60 | int busno, ret; | |
61 | ||
62 | if (!ACPI_HANDLE(&pdev->dev)) | |
63 | return -ENODEV; | |
64 | ||
65 | ret = acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev); | |
66 | if (ret) | |
67 | return -ENODEV; | |
68 | ||
69 | dev->adapter.nr = -1; | |
70 | if (adev->pnp.unique_id && !kstrtoint(adev->pnp.unique_id, 0, &busno)) | |
71 | dev->adapter.nr = busno; | |
72 | ||
73 | dev->tx_fifo_depth = 32; | |
74 | dev->rx_fifo_depth = 32; | |
75 | return 0; | |
76 | } | |
77 | ||
78 | static const struct acpi_device_id dw_i2c_acpi_match[] = { | |
79 | { "INT33C2", 0 }, | |
80 | { "INT33C3", 0 }, | |
81 | { } | |
82 | }; | |
83 | MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match); | |
84 | #else | |
85 | static inline int dw_i2c_acpi_configure(struct platform_device *pdev) | |
86 | { | |
87 | return -ENODEV; | |
88 | } | |
89 | #endif | |
90 | ||
0b255e92 | 91 | static int dw_i2c_probe(struct platform_device *pdev) |
2373f6b9 DB |
92 | { |
93 | struct dw_i2c_dev *dev; | |
94 | struct i2c_adapter *adap; | |
95 | struct resource *mem, *ioarea; | |
96 | int irq, r; | |
97 | ||
98 | /* NOTE: driver uses the static register mapping */ | |
99 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
100 | if (!mem) { | |
101 | dev_err(&pdev->dev, "no mem resource?\n"); | |
102 | return -EINVAL; | |
103 | } | |
104 | ||
105 | irq = platform_get_irq(pdev, 0); | |
106 | if (irq < 0) { | |
107 | dev_err(&pdev->dev, "no irq resource?\n"); | |
108 | return irq; /* -ENXIO */ | |
109 | } | |
110 | ||
111 | ioarea = request_mem_region(mem->start, resource_size(mem), | |
112 | pdev->name); | |
113 | if (!ioarea) { | |
114 | dev_err(&pdev->dev, "I2C region already claimed\n"); | |
115 | return -EBUSY; | |
116 | } | |
117 | ||
118 | dev = kzalloc(sizeof(struct dw_i2c_dev), GFP_KERNEL); | |
119 | if (!dev) { | |
120 | r = -ENOMEM; | |
121 | goto err_release_region; | |
122 | } | |
123 | ||
124 | init_completion(&dev->cmd_complete); | |
125 | mutex_init(&dev->lock); | |
126 | dev->dev = get_device(&pdev->dev); | |
127 | dev->irq = irq; | |
128 | platform_set_drvdata(pdev, dev); | |
129 | ||
130 | dev->clk = clk_get(&pdev->dev, NULL); | |
1d31b58f DB |
131 | dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz; |
132 | ||
2373f6b9 DB |
133 | if (IS_ERR(dev->clk)) { |
134 | r = -ENODEV; | |
135 | goto err_free_mem; | |
136 | } | |
e1fac69f | 137 | clk_prepare_enable(dev->clk); |
2373f6b9 | 138 | |
2fa8326b DB |
139 | dev->functionality = |
140 | I2C_FUNC_I2C | | |
141 | I2C_FUNC_10BIT_ADDR | | |
142 | I2C_FUNC_SMBUS_BYTE | | |
143 | I2C_FUNC_SMBUS_BYTE_DATA | | |
144 | I2C_FUNC_SMBUS_WORD_DATA | | |
145 | I2C_FUNC_SMBUS_I2C_BLOCK; | |
e18563fc DB |
146 | dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | |
147 | DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST; | |
2fa8326b | 148 | |
2373f6b9 DB |
149 | dev->base = ioremap(mem->start, resource_size(mem)); |
150 | if (dev->base == NULL) { | |
151 | dev_err(&pdev->dev, "failure mapping io resources\n"); | |
152 | r = -EBUSY; | |
153 | goto err_unuse_clocks; | |
154 | } | |
b61b1415 MW |
155 | |
156 | /* Try first if we can configure the device from ACPI */ | |
157 | r = dw_i2c_acpi_configure(pdev); | |
158 | if (r) { | |
f3fa9f3d | 159 | u32 param1 = i2c_dw_read_comp_param(dev); |
2373f6b9 DB |
160 | |
161 | dev->tx_fifo_depth = ((param1 >> 16) & 0xff) + 1; | |
162 | dev->rx_fifo_depth = ((param1 >> 8) & 0xff) + 1; | |
b61b1415 | 163 | dev->adapter.nr = pdev->id; |
2373f6b9 DB |
164 | } |
165 | r = i2c_dw_init(dev); | |
166 | if (r) | |
167 | goto err_iounmap; | |
168 | ||
f3fa9f3d | 169 | i2c_dw_disable_int(dev); |
b61b1415 | 170 | r = request_irq(dev->irq, i2c_dw_isr, IRQF_SHARED, pdev->name, dev); |
2373f6b9 DB |
171 | if (r) { |
172 | dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq); | |
173 | goto err_iounmap; | |
174 | } | |
175 | ||
176 | adap = &dev->adapter; | |
177 | i2c_set_adapdata(adap, dev); | |
178 | adap->owner = THIS_MODULE; | |
179 | adap->class = I2C_CLASS_HWMON; | |
180 | strlcpy(adap->name, "Synopsys DesignWare I2C adapter", | |
181 | sizeof(adap->name)); | |
182 | adap->algo = &i2c_dw_algo; | |
183 | adap->dev.parent = &pdev->dev; | |
af71100c | 184 | adap->dev.of_node = pdev->dev.of_node; |
b61b1415 | 185 | ACPI_HANDLE_SET(&adap->dev, ACPI_HANDLE(&pdev->dev)); |
2373f6b9 | 186 | |
2373f6b9 DB |
187 | r = i2c_add_numbered_adapter(adap); |
188 | if (r) { | |
189 | dev_err(&pdev->dev, "failure adding adapter\n"); | |
190 | goto err_free_irq; | |
191 | } | |
af71100c | 192 | of_i2c_register_devices(adap); |
b61b1415 | 193 | acpi_i2c_register_devices(adap); |
2373f6b9 | 194 | |
7272194e MW |
195 | pm_runtime_set_active(&pdev->dev); |
196 | pm_runtime_enable(&pdev->dev); | |
197 | pm_runtime_put(&pdev->dev); | |
198 | ||
2373f6b9 DB |
199 | return 0; |
200 | ||
201 | err_free_irq: | |
202 | free_irq(dev->irq, dev); | |
203 | err_iounmap: | |
204 | iounmap(dev->base); | |
205 | err_unuse_clocks: | |
e1fac69f | 206 | clk_disable_unprepare(dev->clk); |
2373f6b9 DB |
207 | clk_put(dev->clk); |
208 | dev->clk = NULL; | |
209 | err_free_mem: | |
2373f6b9 DB |
210 | put_device(&pdev->dev); |
211 | kfree(dev); | |
212 | err_release_region: | |
213 | release_mem_region(mem->start, resource_size(mem)); | |
214 | ||
215 | return r; | |
216 | } | |
217 | ||
0b255e92 | 218 | static int dw_i2c_remove(struct platform_device *pdev) |
2373f6b9 DB |
219 | { |
220 | struct dw_i2c_dev *dev = platform_get_drvdata(pdev); | |
221 | struct resource *mem; | |
222 | ||
7272194e MW |
223 | pm_runtime_get_sync(&pdev->dev); |
224 | ||
2373f6b9 DB |
225 | i2c_del_adapter(&dev->adapter); |
226 | put_device(&pdev->dev); | |
227 | ||
e1fac69f | 228 | clk_disable_unprepare(dev->clk); |
2373f6b9 DB |
229 | clk_put(dev->clk); |
230 | dev->clk = NULL; | |
231 | ||
f3fa9f3d | 232 | i2c_dw_disable(dev); |
2373f6b9 DB |
233 | free_irq(dev->irq, dev); |
234 | kfree(dev); | |
235 | ||
7272194e MW |
236 | pm_runtime_put(&pdev->dev); |
237 | pm_runtime_disable(&pdev->dev); | |
238 | ||
2373f6b9 DB |
239 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
240 | release_mem_region(mem->start, resource_size(mem)); | |
241 | return 0; | |
242 | } | |
243 | ||
af71100c RH |
244 | #ifdef CONFIG_OF |
245 | static const struct of_device_id dw_i2c_of_match[] = { | |
246 | { .compatible = "snps,designware-i2c", }, | |
247 | {}, | |
248 | }; | |
249 | MODULE_DEVICE_TABLE(of, dw_i2c_of_match); | |
250 | #endif | |
251 | ||
3bf3b289 DS |
252 | #ifdef CONFIG_PM |
253 | static int dw_i2c_suspend(struct device *dev) | |
254 | { | |
255 | struct platform_device *pdev = to_platform_device(dev); | |
256 | struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev); | |
257 | ||
e1fac69f | 258 | clk_disable_unprepare(i_dev->clk); |
3bf3b289 DS |
259 | |
260 | return 0; | |
261 | } | |
262 | ||
263 | static int dw_i2c_resume(struct device *dev) | |
264 | { | |
265 | struct platform_device *pdev = to_platform_device(dev); | |
266 | struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev); | |
267 | ||
e1fac69f | 268 | clk_prepare_enable(i_dev->clk); |
3bf3b289 DS |
269 | i2c_dw_init(i_dev); |
270 | ||
271 | return 0; | |
272 | } | |
273 | #endif | |
274 | ||
275 | static SIMPLE_DEV_PM_OPS(dw_i2c_dev_pm_ops, dw_i2c_suspend, dw_i2c_resume); | |
276 | ||
2373f6b9 DB |
277 | /* work with hotplug and coldplug */ |
278 | MODULE_ALIAS("platform:i2c_designware"); | |
279 | ||
280 | static struct platform_driver dw_i2c_driver = { | |
0b255e92 | 281 | .remove = dw_i2c_remove, |
2373f6b9 DB |
282 | .driver = { |
283 | .name = "i2c_designware", | |
284 | .owner = THIS_MODULE, | |
af71100c | 285 | .of_match_table = of_match_ptr(dw_i2c_of_match), |
b61b1415 | 286 | .acpi_match_table = ACPI_PTR(dw_i2c_acpi_match), |
3bf3b289 | 287 | .pm = &dw_i2c_dev_pm_ops, |
2373f6b9 DB |
288 | }, |
289 | }; | |
290 | ||
291 | static int __init dw_i2c_init_driver(void) | |
292 | { | |
293 | return platform_driver_probe(&dw_i2c_driver, dw_i2c_probe); | |
294 | } | |
10452280 | 295 | subsys_initcall(dw_i2c_init_driver); |
2373f6b9 DB |
296 | |
297 | static void __exit dw_i2c_exit_driver(void) | |
298 | { | |
299 | platform_driver_unregister(&dw_i2c_driver); | |
300 | } | |
301 | module_exit(dw_i2c_exit_driver); | |
302 | ||
303 | MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); | |
304 | MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter"); | |
305 | MODULE_LICENSE("GPL"); |