aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBaruch Siach <baruch@tkos.co.il>2015-03-19 07:16:46 -0400
committerWolfram Sang <wsa@the-dreams.de>2015-03-22 05:53:51 -0400
commit4a7a08226dd590a139e5f7835fe93f90b3beee90 (patch)
tree76038552f8a9fea3a9801ce20edf7b973a13bbf8
parent03bde7c31a360f814ca42101d60563b1b803dca1 (diff)
i2c: add support for the Digicolor I2C controller
The CX92755 is an SoC in the Conexant Digicolor series. The devicetree binding document describes the I2C controller on the CX92755 SoC, that is also shared by some other SoCs in the Digicolor series. The driver adds support. Signed-off-by: Baruch Siach <baruch@tkos.co.il> [wsa: fixed spaces around operators] Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
-rw-r--r--Documentation/devicetree/bindings/i2c/i2c-digicolor.txt25
-rw-r--r--drivers/i2c/busses/Kconfig9
-rw-r--r--drivers/i2c/busses/Makefile1
-rw-r--r--drivers/i2c/busses/i2c-digicolor.c385
4 files changed, 420 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/i2c/i2c-digicolor.txt b/Documentation/devicetree/bindings/i2c/i2c-digicolor.txt
new file mode 100644
index 000000000000..457a098d4f7e
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-digicolor.txt
@@ -0,0 +1,25 @@
1Conexant Digicolor I2C controller
2
3Required properties:
4 - compatible: must be "cnxt,cx92755-i2c"
5 - reg: physical address and length of the device registers
6 - interrupts: a single interrupt specifier
7 - clocks: clock for the device
8 - #address-cells: should be <1>
9 - #size-cells: should be <0>
10
11Optional properties:
12- clock-frequency: the desired I2C bus clock frequency in Hz; in
13 absence of this property the default value is used (100 kHz).
14
15Example:
16
17 i2c: i2c@f0000120 {
18 compatible = "cnxt,cx92755-i2c";
19 reg = <0xf0000120 0x10>;
20 interrupts = <28>;
21 clocks = <&main_clk>;
22 clock-frequency = <100000>;
23 #address-cells = <1>;
24 #size-cells = <0>;
25 };
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 22da9c2ffa22..db09881614b7 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -485,6 +485,15 @@ config I2C_DESIGNWARE_BAYTRAIL
485 the platform firmware controlling it. You should say Y if running on 485 the platform firmware controlling it. You should say Y if running on
486 a BayTrail system using the AXP288. 486 a BayTrail system using the AXP288.
487 487
488config I2C_DIGICOLOR
489 tristate "Conexant Digicolor I2C driver"
490 depends on ARCH_DIGICOLOR
491 help
492 Support for Conexant Digicolor SoCs (CX92755) I2C controller driver.
493
494 This driver can also be built as a module. If so, the module
495 will be called i2c-digicolor.
496
488config I2C_EFM32 497config I2C_EFM32
489 tristate "EFM32 I2C controller" 498 tristate "EFM32 I2C controller"
490 depends on ARCH_EFM32 || COMPILE_TEST 499 depends on ARCH_EFM32 || COMPILE_TEST
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 3638feb6677e..4413f09996cb 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -45,6 +45,7 @@ i2c-designware-platform-objs := i2c-designware-platdrv.o
45i2c-designware-platform-$(CONFIG_I2C_DESIGNWARE_BAYTRAIL) += i2c-designware-baytrail.o 45i2c-designware-platform-$(CONFIG_I2C_DESIGNWARE_BAYTRAIL) += i2c-designware-baytrail.o
46obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o 46obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o
47i2c-designware-pci-objs := i2c-designware-pcidrv.o 47i2c-designware-pci-objs := i2c-designware-pcidrv.o
48obj-$(CONFIG_I2C_DIGICOLOR) += i2c-digicolor.o
48obj-$(CONFIG_I2C_EFM32) += i2c-efm32.o 49obj-$(CONFIG_I2C_EFM32) += i2c-efm32.o
49obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o 50obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o
50obj-$(CONFIG_I2C_EXYNOS5) += i2c-exynos5.o 51obj-$(CONFIG_I2C_EXYNOS5) += i2c-exynos5.o
diff --git a/drivers/i2c/busses/i2c-digicolor.c b/drivers/i2c/busses/i2c-digicolor.c
new file mode 100644
index 000000000000..03f1e5549896
--- /dev/null
+++ b/drivers/i2c/busses/i2c-digicolor.c
@@ -0,0 +1,385 @@
1/*
2 * I2C bus driver for Conexant Digicolor SoCs
3 *
4 * Author: Baruch Siach <baruch@tkos.co.il>
5 *
6 * Copyright (C) 2015 Paradox Innovation Ltd.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/clk.h>
14#include <linux/completion.h>
15#include <linux/i2c.h>
16#include <linux/interrupt.h>
17#include <linux/io.h>
18#include <linux/clk.h>
19#include <linux/delay.h>
20#include <linux/kernel.h>
21#include <linux/module.h>
22#include <linux/of.h>
23#include <linux/platform_device.h>
24
25#define DEFAULT_FREQ 100000
26#define TIMEOUT_MS 100
27
28#define II_CONTROL 0x0
29#define II_CONTROL_LOCAL_RESET BIT(0)
30
31#define II_CLOCKTIME 0x1
32
33#define II_COMMAND 0x2
34#define II_CMD_START 1
35#define II_CMD_RESTART 2
36#define II_CMD_SEND_ACK 3
37#define II_CMD_GET_ACK 6
38#define II_CMD_GET_NOACK 7
39#define II_CMD_STOP 10
40#define II_COMMAND_GO BIT(7)
41#define II_COMMAND_COMPLETION_STATUS(r) (((r) >> 5) & 3)
42#define II_CMD_STATUS_NORMAL 0
43#define II_CMD_STATUS_ACK_GOOD 1
44#define II_CMD_STATUS_ACK_BAD 2
45#define II_CMD_STATUS_ABORT 3
46
47#define II_DATA 0x3
48#define II_INTFLAG_CLEAR 0x8
49#define II_INTENABLE 0xa
50
51struct dc_i2c {
52 struct i2c_adapter adap;
53 struct device *dev;
54 void __iomem *regs;
55 struct clk *clk;
56 unsigned int frequency;
57
58 struct i2c_msg *msg;
59 unsigned int msgbuf_ptr;
60 int last;
61 spinlock_t lock;
62 struct completion done;
63 int state;
64 int error;
65};
66
67enum {
68 STATE_IDLE,
69 STATE_START,
70 STATE_ADDR,
71 STATE_WRITE,
72 STATE_READ,
73 STATE_STOP,
74};
75
76static void dc_i2c_cmd(struct dc_i2c *i2c, u8 cmd)
77{
78 writeb_relaxed(cmd | II_COMMAND_GO, i2c->regs + II_COMMAND);
79}
80
81static u8 dc_i2c_addr_cmd(struct i2c_msg *msg)
82{
83 u8 addr = (msg->addr & 0x7f) << 1;
84
85 if (msg->flags & I2C_M_RD)
86 addr |= 1;
87
88 return addr;
89}
90
91static void dc_i2c_data(struct dc_i2c *i2c, u8 data)
92{
93 writeb_relaxed(data, i2c->regs + II_DATA);
94}
95
96static void dc_i2c_write_byte(struct dc_i2c *i2c, u8 byte)
97{
98 dc_i2c_data(i2c, byte);
99 dc_i2c_cmd(i2c, II_CMD_SEND_ACK);
100}
101
102static void dc_i2c_write_buf(struct dc_i2c *i2c)
103{
104 dc_i2c_write_byte(i2c, i2c->msg->buf[i2c->msgbuf_ptr++]);
105}
106
107static void dc_i2c_next_read(struct dc_i2c *i2c)
108{
109 bool last = (i2c->msgbuf_ptr + 1 == i2c->msg->len);
110
111 dc_i2c_cmd(i2c, last ? II_CMD_GET_NOACK : II_CMD_GET_ACK);
112}
113
114static void dc_i2c_stop(struct dc_i2c *i2c)
115{
116 i2c->state = STATE_STOP;
117 if (i2c->last)
118 dc_i2c_cmd(i2c, II_CMD_STOP);
119 else
120 complete(&i2c->done);
121}
122
123static u8 dc_i2c_read_byte(struct dc_i2c *i2c)
124{
125 return readb_relaxed(i2c->regs + II_DATA);
126}
127
128static void dc_i2c_read_buf(struct dc_i2c *i2c)
129{
130 i2c->msg->buf[i2c->msgbuf_ptr++] = dc_i2c_read_byte(i2c);
131 dc_i2c_next_read(i2c);
132}
133
134static void dc_i2c_set_irq(struct dc_i2c *i2c, int enable)
135{
136 if (enable)
137 writeb_relaxed(1, i2c->regs + II_INTFLAG_CLEAR);
138 writeb_relaxed(!!enable, i2c->regs + II_INTENABLE);
139}
140
141static int dc_i2c_cmd_status(struct dc_i2c *i2c)
142{
143 u8 cmd = readb_relaxed(i2c->regs + II_COMMAND);
144
145 return II_COMMAND_COMPLETION_STATUS(cmd);
146}
147
148static void dc_i2c_start_msg(struct dc_i2c *i2c, int first)
149{
150 struct i2c_msg *msg = i2c->msg;
151
152 if (!(msg->flags & I2C_M_NOSTART)) {
153 i2c->state = STATE_START;
154 dc_i2c_cmd(i2c, first ? II_CMD_START : II_CMD_RESTART);
155 } else if (msg->flags & I2C_M_RD) {
156 i2c->state = STATE_READ;
157 dc_i2c_next_read(i2c);
158 } else {
159 i2c->state = STATE_WRITE;
160 dc_i2c_write_buf(i2c);
161 }
162}
163
164static irqreturn_t dc_i2c_irq(int irq, void *dev_id)
165{
166 struct dc_i2c *i2c = dev_id;
167 int cmd_status = dc_i2c_cmd_status(i2c);
168 unsigned long flags;
169 u8 addr_cmd;
170
171 writeb_relaxed(1, i2c->regs + II_INTFLAG_CLEAR);
172
173 spin_lock_irqsave(&i2c->lock, flags);
174
175 if (cmd_status == II_CMD_STATUS_ACK_BAD
176 || cmd_status == II_CMD_STATUS_ABORT) {
177 i2c->error = -EIO;
178 complete(&i2c->done);
179 goto out;
180 }
181
182 switch (i2c->state) {
183 case STATE_START:
184 addr_cmd = dc_i2c_addr_cmd(i2c->msg);
185 dc_i2c_write_byte(i2c, addr_cmd);
186 i2c->state = STATE_ADDR;
187 break;
188 case STATE_ADDR:
189 if (i2c->msg->flags & I2C_M_RD) {
190 dc_i2c_next_read(i2c);
191 i2c->state = STATE_READ;
192 break;
193 }
194 i2c->state = STATE_WRITE;
195 /* fall through */
196 case STATE_WRITE:
197 if (i2c->msgbuf_ptr < i2c->msg->len)
198 dc_i2c_write_buf(i2c);
199 else
200 dc_i2c_stop(i2c);
201 break;
202 case STATE_READ:
203 if (i2c->msgbuf_ptr < i2c->msg->len)
204 dc_i2c_read_buf(i2c);
205 else
206 dc_i2c_stop(i2c);
207 break;
208 case STATE_STOP:
209 i2c->state = STATE_IDLE;
210 complete(&i2c->done);
211 break;
212 }
213
214out:
215 spin_unlock_irqrestore(&i2c->lock, flags);
216 return IRQ_HANDLED;
217}
218
219static int dc_i2c_xfer_msg(struct dc_i2c *i2c, struct i2c_msg *msg, int first,
220 int last)
221{
222 unsigned long timeout = msecs_to_jiffies(TIMEOUT_MS);
223 unsigned long flags;
224
225 spin_lock_irqsave(&i2c->lock, flags);
226 i2c->msg = msg;
227 i2c->msgbuf_ptr = 0;
228 i2c->last = last;
229 i2c->error = 0;
230
231 reinit_completion(&i2c->done);
232 dc_i2c_set_irq(i2c, 1);
233 dc_i2c_start_msg(i2c, first);
234 spin_unlock_irqrestore(&i2c->lock, flags);
235
236 timeout = wait_for_completion_timeout(&i2c->done, timeout);
237 dc_i2c_set_irq(i2c, 0);
238 if (timeout == 0) {
239 i2c->state = STATE_IDLE;
240 return -ETIMEDOUT;
241 }
242
243 if (i2c->error)
244 return i2c->error;
245
246 return 0;
247}
248
249static int dc_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
250{
251 struct dc_i2c *i2c = adap->algo_data;
252 int i, ret;
253
254 for (i = 0; i < num; i++) {
255 ret = dc_i2c_xfer_msg(i2c, &msgs[i], i == 0, i == num - 1);
256 if (ret)
257 return ret;
258 }
259
260 return num;
261}
262
263static int dc_i2c_init_hw(struct dc_i2c *i2c)
264{
265 unsigned long clk_rate = clk_get_rate(i2c->clk);
266 unsigned int clocktime;
267
268 writeb_relaxed(II_CONTROL_LOCAL_RESET, i2c->regs + II_CONTROL);
269 udelay(100);
270 writeb_relaxed(0, i2c->regs + II_CONTROL);
271 udelay(100);
272
273 clocktime = DIV_ROUND_UP(clk_rate, 64 * i2c->frequency);
274 if (clocktime < 1 || clocktime > 0xff) {
275 dev_err(i2c->dev, "can't set bus speed of %u Hz\n",
276 i2c->frequency);
277 return -EINVAL;
278 }
279 writeb_relaxed(clocktime - 1, i2c->regs + II_CLOCKTIME);
280
281 return 0;
282}
283
284static u32 dc_i2c_func(struct i2c_adapter *adap)
285{
286 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_NOSTART;
287}
288
289static const struct i2c_algorithm dc_i2c_algorithm = {
290 .master_xfer = dc_i2c_xfer,
291 .functionality = dc_i2c_func,
292};
293
294static int dc_i2c_probe(struct platform_device *pdev)
295{
296 struct device_node *np = pdev->dev.of_node;
297 struct dc_i2c *i2c;
298 struct resource *r;
299 int ret = 0, irq;
300
301 i2c = devm_kzalloc(&pdev->dev, sizeof(struct dc_i2c), GFP_KERNEL);
302 if (!i2c)
303 return -ENOMEM;
304
305 if (of_property_read_u32(pdev->dev.of_node, "clock-frequency",
306 &i2c->frequency))
307 i2c->frequency = DEFAULT_FREQ;
308
309 i2c->dev = &pdev->dev;
310 platform_set_drvdata(pdev, i2c);
311
312 spin_lock_init(&i2c->lock);
313 init_completion(&i2c->done);
314
315 i2c->clk = devm_clk_get(&pdev->dev, NULL);
316 if (IS_ERR(i2c->clk))
317 return PTR_ERR(i2c->clk);
318
319 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
320 i2c->regs = devm_ioremap_resource(&pdev->dev, r);
321 if (IS_ERR(i2c->regs))
322 return PTR_ERR(i2c->regs);
323
324 irq = platform_get_irq(pdev, 0);
325 if (irq < 0)
326 return irq;
327
328 ret = devm_request_irq(&pdev->dev, irq, dc_i2c_irq, 0,
329 dev_name(&pdev->dev), i2c);
330 if (ret < 0)
331 return ret;
332
333 strlcpy(i2c->adap.name, "Conexant Digicolor I2C adapter",
334 sizeof(i2c->adap.name));
335 i2c->adap.owner = THIS_MODULE;
336 i2c->adap.algo = &dc_i2c_algorithm;
337 i2c->adap.dev.parent = &pdev->dev;
338 i2c->adap.dev.of_node = np;
339 i2c->adap.algo_data = i2c;
340
341 ret = dc_i2c_init_hw(i2c);
342 if (ret)
343 return ret;
344
345 ret = clk_prepare_enable(i2c->clk);
346 if (ret < 0)
347 return ret;
348
349 ret = i2c_add_adapter(&i2c->adap);
350 if (ret < 0) {
351 clk_unprepare(i2c->clk);
352 return ret;
353 }
354
355 return 0;
356}
357
358static int dc_i2c_remove(struct platform_device *pdev)
359{
360 struct dc_i2c *i2c = platform_get_drvdata(pdev);
361
362 i2c_del_adapter(&i2c->adap);
363 clk_disable_unprepare(i2c->clk);
364
365 return 0;
366}
367
368static const struct of_device_id dc_i2c_match[] = {
369 { .compatible = "cnxt,cx92755-i2c" },
370 { },
371};
372
373static struct platform_driver dc_i2c_driver = {
374 .probe = dc_i2c_probe,
375 .remove = dc_i2c_remove,
376 .driver = {
377 .name = "digicolor-i2c",
378 .of_match_table = dc_i2c_match,
379 },
380};
381module_platform_driver(dc_i2c_driver);
382
383MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
384MODULE_DESCRIPTION("Conexant Digicolor I2C master driver");
385MODULE_LICENSE("GPL v2");