diff options
author | Johan Hovold <jhovold@gmail.com> | 2010-08-18 18:13:48 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-10-22 13:21:20 -0400 |
commit | 90593899de83a6e6fdea563d058acd2f4334e3f9 (patch) | |
tree | 3ae2d6c6c76c0ef4710c2937dc66189278940b97 | |
parent | c6a76781da6b7ee2589a6ec87be30abdbc303384 (diff) |
USB: sam-ba: add driver for Atmel SAM Boot Assistant (SAM-BA)
Add new driver to access the SAM-BA boot application of Atmel AT91SAM
devices.
The SAM-BA firmware cannot handle merged write requests so we cannot use
the generic write implementation (which uses the port write fifo).
Tested with the SAM-BA 2.10 tools and an Atmel at91sam9260-ek.
Signed-off-by: Johan Hovold <jhovold@gmail.com>
Tested-by: Peter Korsgaard <jacmet@sunsite.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/usb/serial/Kconfig | 9 | ||||
-rw-r--r-- | drivers/usb/serial/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/serial/sam-ba.c | 206 |
3 files changed, 216 insertions, 0 deletions
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index 916b2b6d765f..95058109f9fa 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig | |||
@@ -527,6 +527,15 @@ config USB_SERIAL_SAFE_PADDED | |||
527 | bool "USB Secure Encapsulated Driver - Padded" | 527 | bool "USB Secure Encapsulated Driver - Padded" |
528 | depends on USB_SERIAL_SAFE | 528 | depends on USB_SERIAL_SAFE |
529 | 529 | ||
530 | config USB_SERIAL_SAMBA | ||
531 | tristate "USB Atmel SAM Boot Assistant (SAM-BA) driver" | ||
532 | help | ||
533 | Say Y here if you want to access the SAM-BA boot application of an | ||
534 | Atmel AT91SAM device. | ||
535 | |||
536 | To compile this driver as a module, choose M here: the | ||
537 | module will be called sam-ba. | ||
538 | |||
530 | config USB_SERIAL_SIEMENS_MPI | 539 | config USB_SERIAL_SIEMENS_MPI |
531 | tristate "USB Siemens MPI driver" | 540 | tristate "USB Siemens MPI driver" |
532 | help | 541 | help |
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index 40ebe17b6ea8..cf41b6209c74 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile | |||
@@ -48,6 +48,7 @@ obj-$(CONFIG_USB_SERIAL_PL2303) += pl2303.o | |||
48 | obj-$(CONFIG_USB_SERIAL_QCAUX) += qcaux.o | 48 | obj-$(CONFIG_USB_SERIAL_QCAUX) += qcaux.o |
49 | obj-$(CONFIG_USB_SERIAL_QUALCOMM) += qcserial.o | 49 | obj-$(CONFIG_USB_SERIAL_QUALCOMM) += qcserial.o |
50 | obj-$(CONFIG_USB_SERIAL_SAFE) += safe_serial.o | 50 | obj-$(CONFIG_USB_SERIAL_SAFE) += safe_serial.o |
51 | obj-$(CONFIG_USB_SERIAL_SAMBA) += sam-ba.o | ||
51 | obj-$(CONFIG_USB_SERIAL_SIEMENS_MPI) += siemens_mpi.o | 52 | obj-$(CONFIG_USB_SERIAL_SIEMENS_MPI) += siemens_mpi.o |
52 | obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS) += sierra.o | 53 | obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS) += sierra.o |
53 | obj-$(CONFIG_USB_SERIAL_SPCP8X5) += spcp8x5.o | 54 | obj-$(CONFIG_USB_SERIAL_SPCP8X5) += spcp8x5.o |
diff --git a/drivers/usb/serial/sam-ba.c b/drivers/usb/serial/sam-ba.c new file mode 100644 index 000000000000..e3bba64afc57 --- /dev/null +++ b/drivers/usb/serial/sam-ba.c | |||
@@ -0,0 +1,206 @@ | |||
1 | /* | ||
2 | * Atmel SAM Boot Assistant (SAM-BA) driver | ||
3 | * | ||
4 | * Copyright (C) 2010 Johan Hovold <jhovold@gmail.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License version | ||
8 | * 2 as published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/tty.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/moduleparam.h> | ||
15 | #include <linux/usb.h> | ||
16 | #include <linux/usb/serial.h> | ||
17 | |||
18 | |||
19 | #define DRIVER_VERSION "v1.0" | ||
20 | #define DRIVER_AUTHOR "Johan Hovold <jhovold@gmail.com>" | ||
21 | #define DRIVER_DESC "Atmel SAM Boot Assistant (SAM-BA) driver" | ||
22 | |||
23 | #define SAMBA_VENDOR_ID 0x3eb | ||
24 | #define SAMBA_PRODUCT_ID 0x6124 | ||
25 | |||
26 | |||
27 | static int debug; | ||
28 | |||
29 | static const struct usb_device_id id_table[] = { | ||
30 | /* | ||
31 | * NOTE: Only match the CDC Data interface. | ||
32 | */ | ||
33 | { USB_DEVICE_AND_INTERFACE_INFO(SAMBA_VENDOR_ID, SAMBA_PRODUCT_ID, | ||
34 | USB_CLASS_CDC_DATA, 0, 0) }, | ||
35 | { } | ||
36 | }; | ||
37 | MODULE_DEVICE_TABLE(usb, id_table); | ||
38 | |||
39 | static struct usb_driver samba_driver = { | ||
40 | .name = "sam-ba", | ||
41 | .probe = usb_serial_probe, | ||
42 | .disconnect = usb_serial_disconnect, | ||
43 | .id_table = id_table, | ||
44 | .no_dynamic_id = 1, | ||
45 | }; | ||
46 | |||
47 | |||
48 | /* | ||
49 | * NOTE: The SAM-BA firmware cannot handle merged write requests so we cannot | ||
50 | * use the generic write implementation (which uses the port write fifo). | ||
51 | */ | ||
52 | static int samba_write(struct tty_struct *tty, struct usb_serial_port *port, | ||
53 | const unsigned char *buf, int count) | ||
54 | { | ||
55 | struct urb *urb; | ||
56 | unsigned long flags; | ||
57 | int result; | ||
58 | int i; | ||
59 | |||
60 | if (!count) | ||
61 | return 0; | ||
62 | |||
63 | count = min_t(int, count, port->bulk_out_size); | ||
64 | |||
65 | spin_lock_irqsave(&port->lock, flags); | ||
66 | if (!port->write_urbs_free) { | ||
67 | spin_unlock_irqrestore(&port->lock, flags); | ||
68 | return 0; | ||
69 | } | ||
70 | i = find_first_bit(&port->write_urbs_free, | ||
71 | ARRAY_SIZE(port->write_urbs)); | ||
72 | __clear_bit(i, &port->write_urbs_free); | ||
73 | port->tx_bytes += count; | ||
74 | spin_unlock_irqrestore(&port->lock, flags); | ||
75 | |||
76 | urb = port->write_urbs[i]; | ||
77 | memcpy(urb->transfer_buffer, buf, count); | ||
78 | urb->transfer_buffer_length = count; | ||
79 | usb_serial_debug_data(debug, &port->dev, __func__, count, | ||
80 | urb->transfer_buffer); | ||
81 | result = usb_submit_urb(urb, GFP_ATOMIC); | ||
82 | if (result) { | ||
83 | dev_err(&port->dev, "%s - error submitting urb: %d\n", | ||
84 | __func__, result); | ||
85 | spin_lock_irqsave(&port->lock, flags); | ||
86 | __set_bit(i, &port->write_urbs_free); | ||
87 | port->tx_bytes -= count; | ||
88 | spin_unlock_irqrestore(&port->lock, flags); | ||
89 | |||
90 | return result; | ||
91 | } | ||
92 | |||
93 | return count; | ||
94 | } | ||
95 | |||
96 | static int samba_write_room(struct tty_struct *tty) | ||
97 | { | ||
98 | struct usb_serial_port *port = tty->driver_data; | ||
99 | unsigned long flags; | ||
100 | unsigned long free; | ||
101 | int count; | ||
102 | int room; | ||
103 | |||
104 | spin_lock_irqsave(&port->lock, flags); | ||
105 | free = port->write_urbs_free; | ||
106 | spin_unlock_irqrestore(&port->lock, flags); | ||
107 | |||
108 | count = hweight_long(free); | ||
109 | room = count * port->bulk_out_size; | ||
110 | |||
111 | dbg("%s - returns %d", __func__, room); | ||
112 | |||
113 | return room; | ||
114 | } | ||
115 | |||
116 | static int samba_chars_in_buffer(struct tty_struct *tty) | ||
117 | { | ||
118 | struct usb_serial_port *port = tty->driver_data; | ||
119 | unsigned long flags; | ||
120 | int chars; | ||
121 | |||
122 | spin_lock_irqsave(&port->lock, flags); | ||
123 | chars = port->tx_bytes; | ||
124 | spin_unlock_irqrestore(&port->lock, flags); | ||
125 | |||
126 | dbg("%s - returns %d", __func__, chars); | ||
127 | |||
128 | return chars; | ||
129 | } | ||
130 | |||
131 | static void samba_write_bulk_callback(struct urb *urb) | ||
132 | { | ||
133 | struct usb_serial_port *port = urb->context; | ||
134 | unsigned long flags; | ||
135 | int i; | ||
136 | |||
137 | dbg("%s - port %d", __func__, port->number); | ||
138 | |||
139 | for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) { | ||
140 | if (port->write_urbs[i] == urb) | ||
141 | break; | ||
142 | } | ||
143 | spin_lock_irqsave(&port->lock, flags); | ||
144 | __set_bit(i, &port->write_urbs_free); | ||
145 | port->tx_bytes -= urb->transfer_buffer_length; | ||
146 | spin_unlock_irqrestore(&port->lock, flags); | ||
147 | |||
148 | if (urb->status) | ||
149 | dbg("%s - non-zero urb status: %d", __func__, urb->status); | ||
150 | |||
151 | usb_serial_port_softint(port); | ||
152 | } | ||
153 | |||
154 | static struct usb_serial_driver samba_device = { | ||
155 | .driver = { | ||
156 | .owner = THIS_MODULE, | ||
157 | .name = "sam-ba", | ||
158 | }, | ||
159 | .usb_driver = &samba_driver, | ||
160 | .id_table = id_table, | ||
161 | .num_ports = 1, | ||
162 | .bulk_in_size = 512, | ||
163 | .bulk_out_size = 2048, | ||
164 | .write = samba_write, | ||
165 | .write_room = samba_write_room, | ||
166 | .chars_in_buffer = samba_chars_in_buffer, | ||
167 | .write_bulk_callback = samba_write_bulk_callback, | ||
168 | .throttle = usb_serial_generic_throttle, | ||
169 | .unthrottle = usb_serial_generic_unthrottle, | ||
170 | }; | ||
171 | |||
172 | static int __init samba_init(void) | ||
173 | { | ||
174 | int retval; | ||
175 | |||
176 | retval = usb_serial_register(&samba_device); | ||
177 | if (retval) | ||
178 | return retval; | ||
179 | |||
180 | retval = usb_register(&samba_driver); | ||
181 | if (retval) { | ||
182 | usb_serial_deregister(&samba_device); | ||
183 | return retval; | ||
184 | } | ||
185 | |||
186 | printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ": " | ||
187 | DRIVER_DESC "\n"); | ||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | static void __exit samba_exit(void) | ||
192 | { | ||
193 | usb_deregister(&samba_driver); | ||
194 | usb_serial_deregister(&samba_device); | ||
195 | } | ||
196 | |||
197 | module_init(samba_init); | ||
198 | module_exit(samba_exit); | ||
199 | |||
200 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
201 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
202 | MODULE_VERSION(DRIVER_VERSION); | ||
203 | MODULE_LICENSE("GPL"); | ||
204 | |||
205 | module_param(debug, bool, S_IRUGO | S_IWUSR); | ||
206 | MODULE_PARM_DESC(debug, "Enable verbose debugging messages"); | ||