diff options
author | Laurentiu Palcu <laurentiu.palcu@intel.com> | 2014-11-06 08:48:04 -0500 |
---|---|---|
committer | Lee Jones <lee.jones@linaro.org> | 2014-11-10 11:30:07 -0500 |
commit | db23e5001f75304af5345a04901061bdfabcc165 (patch) | |
tree | f06fcc39db474fb78e61156ba50bfd106c6c1fad | |
parent | 338a128142975439a19ab3c91480bc9d5a71f033 (diff) |
i2c: add support for Diolan DLN-2 USB-I2C adapter
This patch adds support for the Diolan DLN-2 I2C master module. Due
to hardware limitations it does not support SMBUS quick commands.
Information about the USB protocol interface can be found in the
Programmer's Reference Manual [1], see section 6.2.2 for the I2C
master module commands and responses.
[1] https://www.diolan.com/downloads/dln-api-manual.pdf
Signed-off-by: Laurentiu Palcu <laurentiu.palcu@intel.com>
Signed-off-by: Octavian Purdila <octavian.purdila@intel.com>
Acked-by: Wolfram Sang <wsa@the-dreams.de>
Reviewed-by: Johan Hovold <johan@kernel.org>
[Lee: Fixed some whitespace issues in Kconfig]
Signed-off-by: Lee Jones <lee.jones@linaro.org>
-rw-r--r-- | drivers/i2c/busses/Kconfig | 10 | ||||
-rw-r--r-- | drivers/i2c/busses/Makefile | 1 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-dln2.c | 267 |
3 files changed, 278 insertions, 0 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 917c3585f45b..b4d135cc2f39 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig | |||
@@ -881,6 +881,16 @@ config I2C_DIOLAN_U2C | |||
881 | This driver can also be built as a module. If so, the module | 881 | This driver can also be built as a module. If so, the module |
882 | will be called i2c-diolan-u2c. | 882 | will be called i2c-diolan-u2c. |
883 | 883 | ||
884 | config I2C_DLN2 | ||
885 | tristate "Diolan DLN-2 USB I2C adapter" | ||
886 | depends on MFD_DLN2 | ||
887 | help | ||
888 | If you say yes to this option, support will be included for Diolan | ||
889 | DLN2, a USB to I2C interface. | ||
890 | |||
891 | This driver can also be built as a module. If so, the module | ||
892 | will be called i2c-dln2. | ||
893 | |||
884 | config I2C_PARPORT | 894 | config I2C_PARPORT |
885 | tristate "Parallel port adapter" | 895 | tristate "Parallel port adapter" |
886 | depends on PARPORT | 896 | depends on PARPORT |
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 78d56c54ba2b..cdac7f15eab5 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile | |||
@@ -87,6 +87,7 @@ obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o | |||
87 | 87 | ||
88 | # External I2C/SMBus adapter drivers | 88 | # External I2C/SMBus adapter drivers |
89 | obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o | 89 | obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o |
90 | obj-$(CONFIG_I2C_DLN2) += i2c-dln2.o | ||
90 | obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o | 91 | obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o |
91 | obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o | 92 | obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o |
92 | obj-$(CONFIG_I2C_ROBOTFUZZ_OSIF) += i2c-robotfuzz-osif.o | 93 | obj-$(CONFIG_I2C_ROBOTFUZZ_OSIF) += i2c-robotfuzz-osif.o |
diff --git a/drivers/i2c/busses/i2c-dln2.c b/drivers/i2c/busses/i2c-dln2.c new file mode 100644 index 000000000000..010a5fa8c883 --- /dev/null +++ b/drivers/i2c/busses/i2c-dln2.c | |||
@@ -0,0 +1,267 @@ | |||
1 | /* | ||
2 | * Driver for the Diolan DLN-2 USB-I2C adapter | ||
3 | * | ||
4 | * Copyright (c) 2014 Intel Corporation | ||
5 | * | ||
6 | * Derived from: | ||
7 | * i2c-diolan-u2c.c | ||
8 | * Copyright (c) 2010-2011 Ericsson AB | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License as | ||
12 | * published by the Free Software Foundation, version 2. | ||
13 | */ | ||
14 | |||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/types.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/i2c.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/mfd/dln2.h> | ||
22 | |||
23 | #define DLN2_I2C_MODULE_ID 0x03 | ||
24 | #define DLN2_I2C_CMD(cmd) DLN2_CMD(cmd, DLN2_I2C_MODULE_ID) | ||
25 | |||
26 | /* I2C commands */ | ||
27 | #define DLN2_I2C_GET_PORT_COUNT DLN2_I2C_CMD(0x00) | ||
28 | #define DLN2_I2C_ENABLE DLN2_I2C_CMD(0x01) | ||
29 | #define DLN2_I2C_DISABLE DLN2_I2C_CMD(0x02) | ||
30 | #define DLN2_I2C_IS_ENABLED DLN2_I2C_CMD(0x03) | ||
31 | #define DLN2_I2C_WRITE DLN2_I2C_CMD(0x06) | ||
32 | #define DLN2_I2C_READ DLN2_I2C_CMD(0x07) | ||
33 | #define DLN2_I2C_SCAN_DEVICES DLN2_I2C_CMD(0x08) | ||
34 | #define DLN2_I2C_PULLUP_ENABLE DLN2_I2C_CMD(0x09) | ||
35 | #define DLN2_I2C_PULLUP_DISABLE DLN2_I2C_CMD(0x0A) | ||
36 | #define DLN2_I2C_PULLUP_IS_ENABLED DLN2_I2C_CMD(0x0B) | ||
37 | #define DLN2_I2C_TRANSFER DLN2_I2C_CMD(0x0C) | ||
38 | #define DLN2_I2C_SET_MAX_REPLY_COUNT DLN2_I2C_CMD(0x0D) | ||
39 | #define DLN2_I2C_GET_MAX_REPLY_COUNT DLN2_I2C_CMD(0x0E) | ||
40 | |||
41 | #define DLN2_I2C_MAX_XFER_SIZE 256 | ||
42 | #define DLN2_I2C_BUF_SIZE (DLN2_I2C_MAX_XFER_SIZE + 16) | ||
43 | |||
44 | struct dln2_i2c { | ||
45 | struct platform_device *pdev; | ||
46 | struct i2c_adapter adapter; | ||
47 | u8 port; | ||
48 | /* | ||
49 | * Buffer to hold the packet for read or write transfers. One is enough | ||
50 | * since we can't have multiple transfers in parallel on the i2c bus. | ||
51 | */ | ||
52 | void *buf; | ||
53 | }; | ||
54 | |||
55 | static int dln2_i2c_enable(struct dln2_i2c *dln2, bool enable) | ||
56 | { | ||
57 | int ret; | ||
58 | u16 cmd; | ||
59 | struct { | ||
60 | u8 port; | ||
61 | } tx; | ||
62 | |||
63 | tx.port = dln2->port; | ||
64 | |||
65 | if (enable) | ||
66 | cmd = DLN2_I2C_ENABLE; | ||
67 | else | ||
68 | cmd = DLN2_I2C_DISABLE; | ||
69 | |||
70 | ret = dln2_transfer_tx(dln2->pdev, cmd, &tx, sizeof(tx)); | ||
71 | if (ret < 0) | ||
72 | return ret; | ||
73 | |||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | static int dln2_i2c_write(struct dln2_i2c *dln2, u8 addr, | ||
78 | u8 *data, u16 data_len) | ||
79 | { | ||
80 | int ret; | ||
81 | struct { | ||
82 | u8 port; | ||
83 | u8 addr; | ||
84 | u8 mem_addr_len; | ||
85 | __le32 mem_addr; | ||
86 | __le16 buf_len; | ||
87 | u8 buf[DLN2_I2C_MAX_XFER_SIZE]; | ||
88 | } __packed *tx = dln2->buf; | ||
89 | unsigned len; | ||
90 | |||
91 | BUILD_BUG_ON(sizeof(*tx) > DLN2_I2C_BUF_SIZE); | ||
92 | |||
93 | tx->port = dln2->port; | ||
94 | tx->addr = addr; | ||
95 | tx->mem_addr_len = 0; | ||
96 | tx->mem_addr = 0; | ||
97 | tx->buf_len = cpu_to_le16(data_len); | ||
98 | memcpy(tx->buf, data, data_len); | ||
99 | |||
100 | len = sizeof(*tx) + data_len - DLN2_I2C_MAX_XFER_SIZE; | ||
101 | ret = dln2_transfer_tx(dln2->pdev, DLN2_I2C_WRITE, tx, len); | ||
102 | if (ret < 0) | ||
103 | return ret; | ||
104 | |||
105 | return data_len; | ||
106 | } | ||
107 | |||
108 | static int dln2_i2c_read(struct dln2_i2c *dln2, u16 addr, u8 *data, | ||
109 | u16 data_len) | ||
110 | { | ||
111 | int ret; | ||
112 | struct { | ||
113 | u8 port; | ||
114 | u8 addr; | ||
115 | u8 mem_addr_len; | ||
116 | __le32 mem_addr; | ||
117 | __le16 buf_len; | ||
118 | } __packed tx; | ||
119 | struct { | ||
120 | __le16 buf_len; | ||
121 | u8 buf[DLN2_I2C_MAX_XFER_SIZE]; | ||
122 | } __packed *rx = dln2->buf; | ||
123 | unsigned rx_len = sizeof(*rx); | ||
124 | |||
125 | BUILD_BUG_ON(sizeof(*rx) > DLN2_I2C_BUF_SIZE); | ||
126 | |||
127 | tx.port = dln2->port; | ||
128 | tx.addr = addr; | ||
129 | tx.mem_addr_len = 0; | ||
130 | tx.mem_addr = 0; | ||
131 | tx.buf_len = cpu_to_le16(data_len); | ||
132 | |||
133 | ret = dln2_transfer(dln2->pdev, DLN2_I2C_READ, &tx, sizeof(tx), | ||
134 | rx, &rx_len); | ||
135 | if (ret < 0) | ||
136 | return ret; | ||
137 | if (rx_len < sizeof(rx->buf_len) + data_len) | ||
138 | return -EPROTO; | ||
139 | if (le16_to_cpu(rx->buf_len) != data_len) | ||
140 | return -EPROTO; | ||
141 | |||
142 | memcpy(data, rx->buf, data_len); | ||
143 | |||
144 | return data_len; | ||
145 | } | ||
146 | |||
147 | static int dln2_i2c_xfer(struct i2c_adapter *adapter, | ||
148 | struct i2c_msg *msgs, int num) | ||
149 | { | ||
150 | struct dln2_i2c *dln2 = i2c_get_adapdata(adapter); | ||
151 | struct i2c_msg *pmsg; | ||
152 | struct device *dev = &dln2->adapter.dev; | ||
153 | int i; | ||
154 | |||
155 | for (i = 0; i < num; i++) { | ||
156 | int ret; | ||
157 | |||
158 | pmsg = &msgs[i]; | ||
159 | |||
160 | if (pmsg->len > DLN2_I2C_MAX_XFER_SIZE) { | ||
161 | dev_warn(dev, "maximum transfer size exceeded\n"); | ||
162 | return -EOPNOTSUPP; | ||
163 | } | ||
164 | |||
165 | if (pmsg->flags & I2C_M_RD) { | ||
166 | ret = dln2_i2c_read(dln2, pmsg->addr, pmsg->buf, | ||
167 | pmsg->len); | ||
168 | if (ret < 0) | ||
169 | return ret; | ||
170 | |||
171 | pmsg->len = ret; | ||
172 | } else { | ||
173 | ret = dln2_i2c_write(dln2, pmsg->addr, pmsg->buf, | ||
174 | pmsg->len); | ||
175 | if (ret != pmsg->len) | ||
176 | return -EPROTO; | ||
177 | } | ||
178 | } | ||
179 | |||
180 | return num; | ||
181 | } | ||
182 | |||
183 | static u32 dln2_i2c_func(struct i2c_adapter *a) | ||
184 | { | ||
185 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | | ||
186 | I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | | ||
187 | I2C_FUNC_SMBUS_I2C_BLOCK; | ||
188 | } | ||
189 | |||
190 | static const struct i2c_algorithm dln2_i2c_usb_algorithm = { | ||
191 | .master_xfer = dln2_i2c_xfer, | ||
192 | .functionality = dln2_i2c_func, | ||
193 | }; | ||
194 | |||
195 | static int dln2_i2c_probe(struct platform_device *pdev) | ||
196 | { | ||
197 | int ret; | ||
198 | struct dln2_i2c *dln2; | ||
199 | struct device *dev = &pdev->dev; | ||
200 | struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev); | ||
201 | |||
202 | dln2 = devm_kzalloc(dev, sizeof(*dln2), GFP_KERNEL); | ||
203 | if (!dln2) | ||
204 | return -ENOMEM; | ||
205 | |||
206 | dln2->buf = devm_kmalloc(dev, DLN2_I2C_BUF_SIZE, GFP_KERNEL); | ||
207 | if (!dln2->buf) | ||
208 | return -ENOMEM; | ||
209 | |||
210 | dln2->pdev = pdev; | ||
211 | dln2->port = pdata->port; | ||
212 | |||
213 | /* setup i2c adapter description */ | ||
214 | dln2->adapter.owner = THIS_MODULE; | ||
215 | dln2->adapter.class = I2C_CLASS_HWMON; | ||
216 | dln2->adapter.algo = &dln2_i2c_usb_algorithm; | ||
217 | dln2->adapter.dev.parent = dev; | ||
218 | i2c_set_adapdata(&dln2->adapter, dln2); | ||
219 | snprintf(dln2->adapter.name, sizeof(dln2->adapter.name), "%s-%s-%d", | ||
220 | "dln2-i2c", dev_name(pdev->dev.parent), dln2->port); | ||
221 | |||
222 | platform_set_drvdata(pdev, dln2); | ||
223 | |||
224 | /* initialize the i2c interface */ | ||
225 | ret = dln2_i2c_enable(dln2, true); | ||
226 | if (ret < 0) { | ||
227 | dev_err(dev, "failed to initialize adapter: %d\n", ret); | ||
228 | return ret; | ||
229 | } | ||
230 | |||
231 | /* and finally attach to i2c layer */ | ||
232 | ret = i2c_add_adapter(&dln2->adapter); | ||
233 | if (ret < 0) { | ||
234 | dev_err(dev, "failed to add I2C adapter: %d\n", ret); | ||
235 | goto out_disable; | ||
236 | } | ||
237 | |||
238 | return 0; | ||
239 | |||
240 | out_disable: | ||
241 | dln2_i2c_enable(dln2, false); | ||
242 | |||
243 | return ret; | ||
244 | } | ||
245 | |||
246 | static int dln2_i2c_remove(struct platform_device *pdev) | ||
247 | { | ||
248 | struct dln2_i2c *dln2 = platform_get_drvdata(pdev); | ||
249 | |||
250 | i2c_del_adapter(&dln2->adapter); | ||
251 | dln2_i2c_enable(dln2, false); | ||
252 | |||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | static struct platform_driver dln2_i2c_driver = { | ||
257 | .driver.name = "dln2-i2c", | ||
258 | .probe = dln2_i2c_probe, | ||
259 | .remove = dln2_i2c_remove, | ||
260 | }; | ||
261 | |||
262 | module_platform_driver(dln2_i2c_driver); | ||
263 | |||
264 | MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>"); | ||
265 | MODULE_DESCRIPTION("Driver for the Diolan DLN2 I2C master interface"); | ||
266 | MODULE_LICENSE("GPL v2"); | ||
267 | MODULE_ALIAS("platform:dln2-i2c"); | ||