diff options
author | Andrew Lunn <andrew@lunn.ch> | 2014-01-10 18:23:59 -0500 |
---|---|---|
committer | Wolfram Sang <wsa@the-dreams.de> | 2014-01-13 07:56:56 -0500 |
commit | 83e53a8f120f2f273cf0ad717f5372ab79ac24fe (patch) | |
tree | 6c5934ce87dec5a66f618b2631d421b386555216 /drivers/i2c | |
parent | e5c6e7f296e17011848732b5a26b8251db59595b (diff) |
i2c: Add bus driver for for OSIF USB i2c device.
OSIF, Open Source InterFace, is a USB based i2c bus master. The
origional design was based on i2c-tiny-usb, but more modern versions
of the firmware running on the MegaAVR microcontroller use a different
protocol over the USB. This code is based on Barry Carter
<barry.carter@gmail.com> driver.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/busses/Kconfig | 10 | ||||
-rw-r--r-- | drivers/i2c/busses/Makefile | 1 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-robotfuzz-osif.c | 202 |
3 files changed, 213 insertions, 0 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 98a3f1ffc3b7..6bcdea5856af 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig | |||
@@ -874,6 +874,16 @@ config I2C_PARPORT_LIGHT | |||
874 | This support is also available as a module. If so, the module | 874 | This support is also available as a module. If so, the module |
875 | will be called i2c-parport-light. | 875 | will be called i2c-parport-light. |
876 | 876 | ||
877 | config I2C_ROBOTFUZZ_OSIF | ||
878 | tristate "RobotFuzz Open Source InterFace USB adapter" | ||
879 | depends on USB | ||
880 | help | ||
881 | If you say yes to this option, support will be included for the | ||
882 | RobotFuzz Open Source InterFace USB to I2C interface. | ||
883 | |||
884 | This driver can also be built as a module. If so, the module | ||
885 | will be called i2c-osif. | ||
886 | |||
877 | config I2C_TAOS_EVM | 887 | config I2C_TAOS_EVM |
878 | tristate "TAOS evaluation module" | 888 | tristate "TAOS evaluation module" |
879 | depends on TTY | 889 | depends on TTY |
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index dca041b1b99d..a08931fe73e1 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile | |||
@@ -84,6 +84,7 @@ obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o | |||
84 | obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o | 84 | obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o |
85 | obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o | 85 | obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o |
86 | obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o | 86 | obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o |
87 | obj-$(CONFIG_I2C_ROBOTFUZZ_OSIF) += i2c-robotfuzz-osif.o | ||
87 | obj-$(CONFIG_I2C_TAOS_EVM) += i2c-taos-evm.o | 88 | obj-$(CONFIG_I2C_TAOS_EVM) += i2c-taos-evm.o |
88 | obj-$(CONFIG_I2C_TINY_USB) += i2c-tiny-usb.o | 89 | obj-$(CONFIG_I2C_TINY_USB) += i2c-tiny-usb.o |
89 | obj-$(CONFIG_I2C_VIPERBOARD) += i2c-viperboard.o | 90 | obj-$(CONFIG_I2C_VIPERBOARD) += i2c-viperboard.o |
diff --git a/drivers/i2c/busses/i2c-robotfuzz-osif.c b/drivers/i2c/busses/i2c-robotfuzz-osif.c new file mode 100644 index 000000000000..ced9c6a308d1 --- /dev/null +++ b/drivers/i2c/busses/i2c-robotfuzz-osif.c | |||
@@ -0,0 +1,202 @@ | |||
1 | /* | ||
2 | * Driver for RobotFuzz OSIF | ||
3 | * | ||
4 | * Copyright (c) 2013 Andrew Lunn <andrew@lunn.ch> | ||
5 | * Copyright (c) 2007 Barry Carter <Barry.Carter@robotfuzz.com> | ||
6 | * | ||
7 | * Based on the i2c-tiny-usb by | ||
8 | * | ||
9 | * Copyright (C) 2006 Til Harbaum (Till@Harbaum.org) | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public License as | ||
13 | * published by the Free Software Foundation, version 2. | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/errno.h> | ||
19 | #include <linux/i2c.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/usb.h> | ||
22 | |||
23 | #define OSIFI2C_READ 20 | ||
24 | #define OSIFI2C_WRITE 21 | ||
25 | #define OSIFI2C_STOP 22 | ||
26 | #define OSIFI2C_STATUS 23 | ||
27 | #define OSIFI2C_SET_BIT_RATE 24 | ||
28 | |||
29 | #define STATUS_ADDRESS_ACK 0 | ||
30 | #define STATUS_ADDRESS_NAK 2 | ||
31 | |||
32 | struct osif_priv { | ||
33 | struct usb_device *usb_dev; | ||
34 | struct usb_interface *interface; | ||
35 | struct i2c_adapter adapter; | ||
36 | unsigned char status; | ||
37 | }; | ||
38 | |||
39 | static int osif_usb_read(struct i2c_adapter *adapter, int cmd, | ||
40 | int value, int index, void *data, int len) | ||
41 | { | ||
42 | struct osif_priv *priv = adapter->algo_data; | ||
43 | |||
44 | return usb_control_msg(priv->usb_dev, usb_rcvctrlpipe(priv->usb_dev, 0), | ||
45 | cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | | ||
46 | USB_DIR_IN, value, index, data, len, 2000); | ||
47 | } | ||
48 | |||
49 | static int osif_usb_write(struct i2c_adapter *adapter, int cmd, | ||
50 | int value, int index, void *data, int len) | ||
51 | { | ||
52 | |||
53 | struct osif_priv *priv = adapter->algo_data; | ||
54 | |||
55 | return usb_control_msg(priv->usb_dev, usb_sndctrlpipe(priv->usb_dev, 0), | ||
56 | cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE, | ||
57 | value, index, data, len, 2000); | ||
58 | } | ||
59 | |||
60 | static int osif_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, | ||
61 | int num) | ||
62 | { | ||
63 | struct osif_priv *priv = adapter->algo_data; | ||
64 | struct i2c_msg *pmsg; | ||
65 | int ret = 0; | ||
66 | int i, cmd; | ||
67 | |||
68 | for (i = 0; ret >= 0 && i < num; i++) { | ||
69 | pmsg = &msgs[i]; | ||
70 | |||
71 | if (pmsg->flags & I2C_M_RD) { | ||
72 | cmd = OSIFI2C_READ; | ||
73 | |||
74 | ret = osif_usb_read(adapter, cmd, pmsg->flags, | ||
75 | pmsg->addr, pmsg->buf, | ||
76 | pmsg->len); | ||
77 | if (ret != pmsg->len) { | ||
78 | dev_err(&adapter->dev, "failure reading data\n"); | ||
79 | return -EREMOTEIO; | ||
80 | } | ||
81 | } else { | ||
82 | cmd = OSIFI2C_WRITE; | ||
83 | |||
84 | ret = osif_usb_write(adapter, cmd, pmsg->flags, | ||
85 | pmsg->addr, pmsg->buf, pmsg->len); | ||
86 | if (ret != pmsg->len) { | ||
87 | dev_err(&adapter->dev, "failure writing data\n"); | ||
88 | return -EREMOTEIO; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | ret = osif_usb_read(adapter, OSIFI2C_STOP, 0, 0, NULL, 0); | ||
93 | if (ret) { | ||
94 | dev_err(&adapter->dev, "failure sending STOP\n"); | ||
95 | return -EREMOTEIO; | ||
96 | } | ||
97 | |||
98 | /* read status */ | ||
99 | ret = osif_usb_read(adapter, OSIFI2C_STATUS, 0, 0, | ||
100 | &priv->status, 1); | ||
101 | if (ret != 1) { | ||
102 | dev_err(&adapter->dev, "failure reading status\n"); | ||
103 | return -EREMOTEIO; | ||
104 | } | ||
105 | |||
106 | if (priv->status != STATUS_ADDRESS_ACK) { | ||
107 | dev_dbg(&adapter->dev, "status = %d\n", priv->status); | ||
108 | return -EREMOTEIO; | ||
109 | } | ||
110 | } | ||
111 | |||
112 | return i; | ||
113 | } | ||
114 | |||
115 | static u32 osif_func(struct i2c_adapter *adapter) | ||
116 | { | ||
117 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; | ||
118 | } | ||
119 | |||
120 | static struct i2c_algorithm osif_algorithm = { | ||
121 | .master_xfer = osif_xfer, | ||
122 | .functionality = osif_func, | ||
123 | }; | ||
124 | |||
125 | #define USB_OSIF_VENDOR_ID 0x1964 | ||
126 | #define USB_OSIF_PRODUCT_ID 0x0001 | ||
127 | |||
128 | static struct usb_device_id osif_table[] = { | ||
129 | { USB_DEVICE(USB_OSIF_VENDOR_ID, USB_OSIF_PRODUCT_ID) }, | ||
130 | { } | ||
131 | }; | ||
132 | MODULE_DEVICE_TABLE(usb, osif_table); | ||
133 | |||
134 | static int osif_probe(struct usb_interface *interface, | ||
135 | const struct usb_device_id *id) | ||
136 | { | ||
137 | int ret; | ||
138 | struct osif_priv *priv; | ||
139 | u16 version; | ||
140 | |||
141 | priv = devm_kzalloc(&interface->dev, sizeof(*priv), GFP_KERNEL); | ||
142 | if (!priv) | ||
143 | return -ENOMEM; | ||
144 | |||
145 | priv->usb_dev = usb_get_dev(interface_to_usbdev(interface)); | ||
146 | priv->interface = interface; | ||
147 | |||
148 | usb_set_intfdata(interface, priv); | ||
149 | |||
150 | priv->adapter.owner = THIS_MODULE; | ||
151 | priv->adapter.class = I2C_CLASS_HWMON; | ||
152 | priv->adapter.algo = &osif_algorithm; | ||
153 | priv->adapter.algo_data = priv; | ||
154 | snprintf(priv->adapter.name, sizeof(priv->adapter.name), | ||
155 | "OSIF at bus %03d device %03d", | ||
156 | priv->usb_dev->bus->busnum, priv->usb_dev->devnum); | ||
157 | |||
158 | /* | ||
159 | * Set bus frequency. The frequency is: | ||
160 | * 120,000,000 / ( 16 + 2 * div * 4^prescale). | ||
161 | * Using dev = 52, prescale = 0 give 100KHz */ | ||
162 | ret = osif_usb_read(&priv->adapter, OSIFI2C_SET_BIT_RATE, 52, 0, | ||
163 | NULL, 0); | ||
164 | if (ret) { | ||
165 | dev_err(&interface->dev, "failure sending bit rate"); | ||
166 | usb_put_dev(priv->usb_dev); | ||
167 | return ret; | ||
168 | } | ||
169 | |||
170 | i2c_add_adapter(&(priv->adapter)); | ||
171 | |||
172 | version = le16_to_cpu(priv->usb_dev->descriptor.bcdDevice); | ||
173 | dev_info(&interface->dev, | ||
174 | "version %x.%02x found at bus %03d address %03d", | ||
175 | version >> 8, version & 0xff, | ||
176 | priv->usb_dev->bus->busnum, priv->usb_dev->devnum); | ||
177 | |||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | static void osif_disconnect(struct usb_interface *interface) | ||
182 | { | ||
183 | struct osif_priv *priv = usb_get_intfdata(interface); | ||
184 | |||
185 | i2c_del_adapter(&(priv->adapter)); | ||
186 | usb_set_intfdata(interface, NULL); | ||
187 | usb_put_dev(priv->usb_dev); | ||
188 | } | ||
189 | |||
190 | static struct usb_driver osif_driver = { | ||
191 | .name = "RobotFuzz Open Source InterFace, OSIF", | ||
192 | .probe = osif_probe, | ||
193 | .disconnect = osif_disconnect, | ||
194 | .id_table = osif_table, | ||
195 | }; | ||
196 | |||
197 | module_usb_driver(osif_driver); | ||
198 | |||
199 | MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>"); | ||
200 | MODULE_AUTHOR("Barry Carter <barry.carter@robotfuzz.com>"); | ||
201 | MODULE_DESCRIPTION("RobotFuzz OSIF driver"); | ||
202 | MODULE_LICENSE("GPL v2"); | ||