diff options
author | Bryan Wu <bryan.wu@analog.com> | 2008-04-30 03:52:12 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-30 11:29:30 -0400 |
commit | 2f3517418dc0684a32318f2c5b53257416448b1e (patch) | |
tree | 46225459cb66dcd25e3b392b698b634d4d571012 /drivers | |
parent | 4e68852dca7a16271d09269f643a8e0eb8bb500d (diff) |
Blackfin serial driver: this driver enable SPORTs on Blackfin emulate UART
Signed-off-by: Bryan Wu <bryan.wu@analog.com>
Cc: Alan Cox <alan@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/serial/Kconfig | 43 | ||||
-rw-r--r-- | drivers/serial/Makefile | 1 | ||||
-rw-r--r-- | drivers/serial/bfin_sport_uart.c | 614 | ||||
-rw-r--r-- | drivers/serial/bfin_sport_uart.h | 63 |
4 files changed, 721 insertions, 0 deletions
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 34b809e3b596..36acbcca2d48 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig | |||
@@ -1355,4 +1355,47 @@ config SERIAL_SC26XX_CONSOLE | |||
1355 | help | 1355 | help |
1356 | Support for Console on SC2681/SC2692 serial ports. | 1356 | Support for Console on SC2681/SC2692 serial ports. |
1357 | 1357 | ||
1358 | config SERIAL_BFIN_SPORT | ||
1359 | tristate "Blackfin SPORT emulate UART (EXPERIMENTAL)" | ||
1360 | depends on BFIN && EXPERIMENTAL | ||
1361 | select SERIAL_CORE | ||
1362 | help | ||
1363 | Enble support SPORT emulate UART on Blackfin series. | ||
1364 | |||
1365 | To compile this driver as a module, choose M here: the | ||
1366 | module will be called bfin_sport_uart. | ||
1367 | |||
1368 | choice | ||
1369 | prompt "Baud rate for Blackfin SPORT UART" | ||
1370 | depends on SERIAL_BFIN_SPORT | ||
1371 | default SERIAL_SPORT_BAUD_RATE_57600 | ||
1372 | help | ||
1373 | Choose a baud rate for the SPORT UART, other uart settings are | ||
1374 | 8 bit, 1 stop bit, no parity, no flow control. | ||
1375 | |||
1376 | config SERIAL_SPORT_BAUD_RATE_115200 | ||
1377 | bool "115200" | ||
1378 | |||
1379 | config SERIAL_SPORT_BAUD_RATE_57600 | ||
1380 | bool "57600" | ||
1381 | |||
1382 | config SERIAL_SPORT_BAUD_RATE_38400 | ||
1383 | bool "38400" | ||
1384 | |||
1385 | config SERIAL_SPORT_BAUD_RATE_19200 | ||
1386 | bool "19200" | ||
1387 | |||
1388 | config SERIAL_SPORT_BAUD_RATE_9600 | ||
1389 | bool "9600" | ||
1390 | endchoice | ||
1391 | |||
1392 | config SPORT_BAUD_RATE | ||
1393 | int | ||
1394 | depends on SERIAL_BFIN_SPORT | ||
1395 | default 115200 if (SERIAL_SPORT_BAUD_RATE_115200) | ||
1396 | default 57600 if (SERIAL_SPORT_BAUD_RATE_57600) | ||
1397 | default 38400 if (SERIAL_SPORT_BAUD_RATE_38400) | ||
1398 | default 19200 if (SERIAL_SPORT_BAUD_RATE_19200) | ||
1399 | default 9600 if (SERIAL_SPORT_BAUD_RATE_9600) | ||
1400 | |||
1358 | endmenu | 1401 | endmenu |
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index f02ff9fad017..0d9c09b1e836 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile | |||
@@ -27,6 +27,7 @@ obj-$(CONFIG_SERIAL_PXA) += pxa.o | |||
27 | obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o | 27 | obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o |
28 | obj-$(CONFIG_SERIAL_SA1100) += sa1100.o | 28 | obj-$(CONFIG_SERIAL_SA1100) += sa1100.o |
29 | obj-$(CONFIG_SERIAL_BFIN) += bfin_5xx.o | 29 | obj-$(CONFIG_SERIAL_BFIN) += bfin_5xx.o |
30 | obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o | ||
30 | obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o | 31 | obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o |
31 | obj-$(CONFIG_SERIAL_SUNCORE) += suncore.o | 32 | obj-$(CONFIG_SERIAL_SUNCORE) += suncore.o |
32 | obj-$(CONFIG_SERIAL_SUNHV) += sunhv.o | 33 | obj-$(CONFIG_SERIAL_SUNHV) += sunhv.o |
diff --git a/drivers/serial/bfin_sport_uart.c b/drivers/serial/bfin_sport_uart.c new file mode 100644 index 000000000000..aca1240ad808 --- /dev/null +++ b/drivers/serial/bfin_sport_uart.c | |||
@@ -0,0 +1,614 @@ | |||
1 | /* | ||
2 | * File: linux/drivers/serial/bfin_sport_uart.c | ||
3 | * | ||
4 | * Based on: drivers/serial/bfin_5xx.c by Aubrey Li. | ||
5 | * Author: Roy Huang <roy.huang@analog.com> | ||
6 | * | ||
7 | * Created: Nov 22, 2006 | ||
8 | * Copyright: (c) 2006-2007 Analog Devices Inc. | ||
9 | * Description: this driver enable SPORTs on Blackfin emulate UART. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, see the file COPYING, or write | ||
23 | * to the Free Software Foundation, Inc., | ||
24 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
25 | */ | ||
26 | |||
27 | /* | ||
28 | * This driver and the hardware supported are in term of EE-191 of ADI. | ||
29 | * http://www.analog.com/UploadedFiles/Application_Notes/399447663EE191.pdf | ||
30 | * This application note describe how to implement a UART on a Sharc DSP, | ||
31 | * but this driver is implemented on Blackfin Processor. | ||
32 | */ | ||
33 | |||
34 | /* After reset, there is a prelude of low level pulse when transmit data first | ||
35 | * time. No addtional pulse in following transmit. | ||
36 | * According to document: | ||
37 | * The SPORTs are ready to start transmitting or receiving data no later than | ||
38 | * three serial clock cycles after they are enabled in the SPORTx_TCR1 or | ||
39 | * SPORTx_RCR1 register. No serial clock cycles are lost from this point on. | ||
40 | * The first internal frame sync will occur one frame sync delay after the | ||
41 | * SPORTs are ready. External frame syncs can occur as soon as the SPORT is | ||
42 | * ready. | ||
43 | */ | ||
44 | |||
45 | /* Thanks to Axel Alatalo <axel@rubico.se> for fixing sport rx bug. Sometimes | ||
46 | * sport receives data incorrectly. The following is Axel's words. | ||
47 | * As EE-191, sport rx samples 3 times of the UART baudrate and takes the | ||
48 | * middle smaple of every 3 samples as the data bit. For a 8-N-1 UART setting, | ||
49 | * 30 samples will be required for a byte. If transmitter sends a 1/3 bit short | ||
50 | * byte due to buadrate drift, then the 30th sample of a byte, this sample is | ||
51 | * also the third sample of the stop bit, will happens on the immediately | ||
52 | * following start bit which will be thrown away and missed. Thus since parts | ||
53 | * of the startbit will be missed and the receiver will begin to drift, the | ||
54 | * effect accumulates over time until synchronization is lost. | ||
55 | * If only require 2 samples of the stopbit (by sampling in total 29 samples), | ||
56 | * then a to short byte as in the case above will be tolerated. Then the 1/3 | ||
57 | * early startbit will trigger a framesync since the last read is complete | ||
58 | * after only 2/3 stopbit and framesync is active during the last 1/3 looking | ||
59 | * for a possible early startbit. */ | ||
60 | |||
61 | //#define DEBUG | ||
62 | |||
63 | #include <linux/module.h> | ||
64 | #include <linux/ioport.h> | ||
65 | #include <linux/init.h> | ||
66 | #include <linux/console.h> | ||
67 | #include <linux/sysrq.h> | ||
68 | #include <linux/platform_device.h> | ||
69 | #include <linux/tty.h> | ||
70 | #include <linux/tty_flip.h> | ||
71 | #include <linux/serial_core.h> | ||
72 | |||
73 | #include <asm/delay.h> | ||
74 | #include <asm/portmux.h> | ||
75 | |||
76 | #include "bfin_sport_uart.h" | ||
77 | |||
78 | unsigned short bfin_uart_pin_req_sport0[] = | ||
79 | {P_SPORT0_TFS, P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, \ | ||
80 | P_SPORT0_DRPRI, P_SPORT0_RSCLK, P_SPORT0_DRSEC, P_SPORT0_DTSEC, 0}; | ||
81 | |||
82 | unsigned short bfin_uart_pin_req_sport1[] = | ||
83 | {P_SPORT1_TFS, P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, \ | ||
84 | P_SPORT1_DRPRI, P_SPORT1_RSCLK, P_SPORT1_DRSEC, P_SPORT1_DTSEC, 0}; | ||
85 | |||
86 | #define DRV_NAME "bfin-sport-uart" | ||
87 | |||
88 | struct sport_uart_port { | ||
89 | struct uart_port port; | ||
90 | char *name; | ||
91 | |||
92 | int tx_irq; | ||
93 | int rx_irq; | ||
94 | int err_irq; | ||
95 | }; | ||
96 | |||
97 | static void sport_uart_tx_chars(struct sport_uart_port *up); | ||
98 | static void sport_stop_tx(struct uart_port *port); | ||
99 | |||
100 | static inline void tx_one_byte(struct sport_uart_port *up, unsigned int value) | ||
101 | { | ||
102 | pr_debug("%s value:%x\n", __FUNCTION__, value); | ||
103 | /* Place a Start and Stop bit */ | ||
104 | __asm__ volatile ( | ||
105 | "R2 = b#01111111100;\n\t" | ||
106 | "R3 = b#10000000001;\n\t" | ||
107 | "%0 <<= 2;\n\t" | ||
108 | "%0 = %0 & R2;\n\t" | ||
109 | "%0 = %0 | R3;\n\t" | ||
110 | :"=r"(value) | ||
111 | :"0"(value) | ||
112 | :"R2", "R3"); | ||
113 | pr_debug("%s value:%x\n", __FUNCTION__, value); | ||
114 | |||
115 | SPORT_PUT_TX(up, value); | ||
116 | } | ||
117 | |||
118 | static inline unsigned int rx_one_byte(struct sport_uart_port *up) | ||
119 | { | ||
120 | unsigned int value, extract; | ||
121 | |||
122 | value = SPORT_GET_RX32(up); | ||
123 | pr_debug("%s value:%x\n", __FUNCTION__, value); | ||
124 | |||
125 | /* Extract 8 bits data */ | ||
126 | __asm__ volatile ( | ||
127 | "R5 = 0;\n\t" | ||
128 | "P0 = 8;\n\t" | ||
129 | "R1 = 0x1801(Z);\n\t" | ||
130 | "R3 = 0x0300(Z);\n\t" | ||
131 | "R4 = 0;\n\t" | ||
132 | "LSETUP(loop_s, loop_e) LC0 = P0;\nloop_s:\t" | ||
133 | "R2 = extract(%1, R1.L)(Z);\n\t" | ||
134 | "R2 <<= R4;\n\t" | ||
135 | "R5 = R5 | R2;\n\t" | ||
136 | "R1 = R1 - R3;\nloop_e:\t" | ||
137 | "R4 += 1;\n\t" | ||
138 | "%0 = R5;\n\t" | ||
139 | :"=r"(extract) | ||
140 | :"r"(value) | ||
141 | :"P0", "R1", "R2","R3","R4", "R5"); | ||
142 | |||
143 | pr_debug(" extract:%x\n", extract); | ||
144 | return extract; | ||
145 | } | ||
146 | |||
147 | static int sport_uart_setup(struct sport_uart_port *up, int sclk, int baud_rate) | ||
148 | { | ||
149 | int tclkdiv, tfsdiv, rclkdiv; | ||
150 | |||
151 | /* Set TCR1 and TCR2 */ | ||
152 | SPORT_PUT_TCR1(up, (LTFS | ITFS | TFSR | TLSBIT | ITCLK)); | ||
153 | SPORT_PUT_TCR2(up, 10); | ||
154 | pr_debug("%s TCR1:%x, TCR2:%x\n", __FUNCTION__, SPORT_GET_TCR1(up), SPORT_GET_TCR2(up)); | ||
155 | |||
156 | /* Set RCR1 and RCR2 */ | ||
157 | SPORT_PUT_RCR1(up, (RCKFE | LARFS | LRFS | RFSR | IRCLK)); | ||
158 | SPORT_PUT_RCR2(up, 28); | ||
159 | pr_debug("%s RCR1:%x, RCR2:%x\n", __FUNCTION__, SPORT_GET_RCR1(up), SPORT_GET_RCR2(up)); | ||
160 | |||
161 | tclkdiv = sclk/(2 * baud_rate) - 1; | ||
162 | tfsdiv = 12; | ||
163 | rclkdiv = sclk/(2 * baud_rate * 3) - 1; | ||
164 | SPORT_PUT_TCLKDIV(up, tclkdiv); | ||
165 | SPORT_PUT_TFSDIV(up, tfsdiv); | ||
166 | SPORT_PUT_RCLKDIV(up, rclkdiv); | ||
167 | SSYNC(); | ||
168 | pr_debug("%s sclk:%d, baud_rate:%d, tclkdiv:%d, tfsdiv:%d, rclkdiv:%d\n", | ||
169 | __FUNCTION__, sclk, baud_rate, tclkdiv, tfsdiv, rclkdiv); | ||
170 | |||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static irqreturn_t sport_uart_rx_irq(int irq, void *dev_id) | ||
175 | { | ||
176 | struct sport_uart_port *up = dev_id; | ||
177 | struct tty_struct *tty = up->port.info->tty; | ||
178 | unsigned int ch; | ||
179 | |||
180 | do { | ||
181 | ch = rx_one_byte(up); | ||
182 | up->port.icount.rx++; | ||
183 | |||
184 | if (uart_handle_sysrq_char(&up->port, ch)) | ||
185 | ; | ||
186 | else | ||
187 | tty_insert_flip_char(tty, ch, TTY_NORMAL); | ||
188 | } while (SPORT_GET_STAT(up) & RXNE); | ||
189 | tty_flip_buffer_push(tty); | ||
190 | |||
191 | return IRQ_HANDLED; | ||
192 | } | ||
193 | |||
194 | static irqreturn_t sport_uart_tx_irq(int irq, void *dev_id) | ||
195 | { | ||
196 | sport_uart_tx_chars(dev_id); | ||
197 | |||
198 | return IRQ_HANDLED; | ||
199 | } | ||
200 | |||
201 | static irqreturn_t sport_uart_err_irq(int irq, void *dev_id) | ||
202 | { | ||
203 | struct sport_uart_port *up = dev_id; | ||
204 | struct tty_struct *tty = up->port.info->tty; | ||
205 | unsigned int stat = SPORT_GET_STAT(up); | ||
206 | |||
207 | /* Overflow in RX FIFO */ | ||
208 | if (stat & ROVF) { | ||
209 | up->port.icount.overrun++; | ||
210 | tty_insert_flip_char(tty, 0, TTY_OVERRUN); | ||
211 | SPORT_PUT_STAT(up, ROVF); /* Clear ROVF bit */ | ||
212 | } | ||
213 | /* These should not happen */ | ||
214 | if (stat & (TOVF | TUVF | RUVF)) { | ||
215 | printk(KERN_ERR "SPORT Error:%s %s %s\n", | ||
216 | (stat & TOVF)?"TX overflow":"", | ||
217 | (stat & TUVF)?"TX underflow":"", | ||
218 | (stat & RUVF)?"RX underflow":""); | ||
219 | SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN); | ||
220 | SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) & ~RSPEN); | ||
221 | } | ||
222 | SSYNC(); | ||
223 | |||
224 | return IRQ_HANDLED; | ||
225 | } | ||
226 | |||
227 | /* Reqeust IRQ, Setup clock */ | ||
228 | static int sport_startup(struct uart_port *port) | ||
229 | { | ||
230 | struct sport_uart_port *up = (struct sport_uart_port *)port; | ||
231 | char buffer[20]; | ||
232 | int retval; | ||
233 | |||
234 | pr_debug("%s enter\n", __FUNCTION__); | ||
235 | memset(buffer, 20, '\0'); | ||
236 | snprintf(buffer, 20, "%s rx", up->name); | ||
237 | retval = request_irq(up->rx_irq, sport_uart_rx_irq, IRQF_SAMPLE_RANDOM, buffer, up); | ||
238 | if (retval) { | ||
239 | printk(KERN_ERR "Unable to request interrupt %s\n", buffer); | ||
240 | return retval; | ||
241 | } | ||
242 | |||
243 | snprintf(buffer, 20, "%s tx", up->name); | ||
244 | retval = request_irq(up->tx_irq, sport_uart_tx_irq, IRQF_SAMPLE_RANDOM, buffer, up); | ||
245 | if (retval) { | ||
246 | printk(KERN_ERR "Unable to request interrupt %s\n", buffer); | ||
247 | goto fail1; | ||
248 | } | ||
249 | |||
250 | snprintf(buffer, 20, "%s err", up->name); | ||
251 | retval = request_irq(up->err_irq, sport_uart_err_irq, IRQF_SAMPLE_RANDOM, buffer, up); | ||
252 | if (retval) { | ||
253 | printk(KERN_ERR "Unable to request interrupt %s\n", buffer); | ||
254 | goto fail2; | ||
255 | } | ||
256 | |||
257 | if (port->line) { | ||
258 | if (peripheral_request_list(bfin_uart_pin_req_sport1, DRV_NAME)) | ||
259 | goto fail3; | ||
260 | } else { | ||
261 | if (peripheral_request_list(bfin_uart_pin_req_sport0, DRV_NAME)) | ||
262 | goto fail3; | ||
263 | } | ||
264 | |||
265 | sport_uart_setup(up, get_sclk(), port->uartclk); | ||
266 | |||
267 | /* Enable receive interrupt */ | ||
268 | SPORT_PUT_RCR1(up, (SPORT_GET_RCR1(up) | RSPEN)); | ||
269 | SSYNC(); | ||
270 | |||
271 | return 0; | ||
272 | |||
273 | |||
274 | fail3: | ||
275 | printk(KERN_ERR DRV_NAME | ||
276 | ": Requesting Peripherals failed\n"); | ||
277 | |||
278 | free_irq(up->err_irq, up); | ||
279 | fail2: | ||
280 | free_irq(up->tx_irq, up); | ||
281 | fail1: | ||
282 | free_irq(up->rx_irq, up); | ||
283 | |||
284 | return retval; | ||
285 | |||
286 | } | ||
287 | |||
288 | static void sport_uart_tx_chars(struct sport_uart_port *up) | ||
289 | { | ||
290 | struct circ_buf *xmit = &up->port.info->xmit; | ||
291 | |||
292 | if (SPORT_GET_STAT(up) & TXF) | ||
293 | return; | ||
294 | |||
295 | if (up->port.x_char) { | ||
296 | tx_one_byte(up, up->port.x_char); | ||
297 | up->port.icount.tx++; | ||
298 | up->port.x_char = 0; | ||
299 | return; | ||
300 | } | ||
301 | |||
302 | if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { | ||
303 | sport_stop_tx(&up->port); | ||
304 | return; | ||
305 | } | ||
306 | |||
307 | while(!(SPORT_GET_STAT(up) & TXF) && !uart_circ_empty(xmit)) { | ||
308 | tx_one_byte(up, xmit->buf[xmit->tail]); | ||
309 | xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE -1); | ||
310 | up->port.icount.tx++; | ||
311 | } | ||
312 | |||
313 | if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) | ||
314 | uart_write_wakeup(&up->port); | ||
315 | } | ||
316 | |||
317 | static unsigned int sport_tx_empty(struct uart_port *port) | ||
318 | { | ||
319 | struct sport_uart_port *up = (struct sport_uart_port *)port; | ||
320 | unsigned int stat; | ||
321 | |||
322 | stat = SPORT_GET_STAT(up); | ||
323 | pr_debug("%s stat:%04x\n", __FUNCTION__, stat); | ||
324 | if (stat & TXHRE) { | ||
325 | return TIOCSER_TEMT; | ||
326 | } else | ||
327 | return 0; | ||
328 | } | ||
329 | |||
330 | static unsigned int sport_get_mctrl(struct uart_port *port) | ||
331 | { | ||
332 | pr_debug("%s enter\n", __FUNCTION__); | ||
333 | return (TIOCM_CTS | TIOCM_CD | TIOCM_DSR); | ||
334 | } | ||
335 | |||
336 | static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl) | ||
337 | { | ||
338 | pr_debug("%s enter\n", __FUNCTION__); | ||
339 | } | ||
340 | |||
341 | static void sport_stop_tx(struct uart_port *port) | ||
342 | { | ||
343 | struct sport_uart_port *up = (struct sport_uart_port *)port; | ||
344 | unsigned int stat; | ||
345 | |||
346 | pr_debug("%s enter\n", __FUNCTION__); | ||
347 | |||
348 | stat = SPORT_GET_STAT(up); | ||
349 | while(!(stat & TXHRE)) { | ||
350 | udelay(1); | ||
351 | stat = SPORT_GET_STAT(up); | ||
352 | } | ||
353 | /* Although the hold register is empty, last byte is still in shift | ||
354 | * register and not sent out yet. If baud rate is lower than default, | ||
355 | * delay should be longer. For example, if the baud rate is 9600, | ||
356 | * the delay must be at least 2ms by experience */ | ||
357 | udelay(500); | ||
358 | |||
359 | SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN)); | ||
360 | SSYNC(); | ||
361 | |||
362 | return; | ||
363 | } | ||
364 | |||
365 | static void sport_start_tx(struct uart_port *port) | ||
366 | { | ||
367 | struct sport_uart_port *up = (struct sport_uart_port *)port; | ||
368 | |||
369 | pr_debug("%s enter\n", __FUNCTION__); | ||
370 | /* Write data into SPORT FIFO before enable SPROT to transmit */ | ||
371 | sport_uart_tx_chars(up); | ||
372 | |||
373 | /* Enable transmit, then an interrupt will generated */ | ||
374 | SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN)); | ||
375 | SSYNC(); | ||
376 | pr_debug("%s exit\n", __FUNCTION__); | ||
377 | } | ||
378 | |||
379 | static void sport_stop_rx(struct uart_port *port) | ||
380 | { | ||
381 | struct sport_uart_port *up = (struct sport_uart_port *)port; | ||
382 | |||
383 | pr_debug("%s enter\n", __FUNCTION__); | ||
384 | /* Disable sport to stop rx */ | ||
385 | SPORT_PUT_RCR1(up, (SPORT_GET_RCR1(up) & ~RSPEN)); | ||
386 | SSYNC(); | ||
387 | } | ||
388 | |||
389 | static void sport_enable_ms(struct uart_port *port) | ||
390 | { | ||
391 | pr_debug("%s enter\n", __FUNCTION__); | ||
392 | } | ||
393 | |||
394 | static void sport_break_ctl(struct uart_port *port, int break_state) | ||
395 | { | ||
396 | pr_debug("%s enter\n", __FUNCTION__); | ||
397 | } | ||
398 | |||
399 | static void sport_shutdown(struct uart_port *port) | ||
400 | { | ||
401 | struct sport_uart_port *up = (struct sport_uart_port *)port; | ||
402 | |||
403 | pr_debug("%s enter\n", __FUNCTION__); | ||
404 | |||
405 | /* Disable sport */ | ||
406 | SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN)); | ||
407 | SPORT_PUT_RCR1(up, (SPORT_GET_RCR1(up) & ~RSPEN)); | ||
408 | SSYNC(); | ||
409 | |||
410 | if (port->line) { | ||
411 | peripheral_free_list(bfin_uart_pin_req_sport1); | ||
412 | } else { | ||
413 | peripheral_free_list(bfin_uart_pin_req_sport0); | ||
414 | } | ||
415 | |||
416 | free_irq(up->rx_irq, up); | ||
417 | free_irq(up->tx_irq, up); | ||
418 | free_irq(up->err_irq, up); | ||
419 | } | ||
420 | |||
421 | static void sport_set_termios(struct uart_port *port, | ||
422 | struct termios *termios, struct termios *old) | ||
423 | { | ||
424 | pr_debug("%s enter, c_cflag:%08x\n", __FUNCTION__, termios->c_cflag); | ||
425 | uart_update_timeout(port, CS8 ,port->uartclk); | ||
426 | } | ||
427 | |||
428 | static const char *sport_type(struct uart_port *port) | ||
429 | { | ||
430 | struct sport_uart_port *up = (struct sport_uart_port *)port; | ||
431 | |||
432 | pr_debug("%s enter\n", __FUNCTION__); | ||
433 | return up->name; | ||
434 | } | ||
435 | |||
436 | static void sport_release_port(struct uart_port *port) | ||
437 | { | ||
438 | pr_debug("%s enter\n", __FUNCTION__); | ||
439 | } | ||
440 | |||
441 | static int sport_request_port(struct uart_port *port) | ||
442 | { | ||
443 | pr_debug("%s enter\n", __FUNCTION__); | ||
444 | return 0; | ||
445 | } | ||
446 | |||
447 | static void sport_config_port(struct uart_port *port, int flags) | ||
448 | { | ||
449 | struct sport_uart_port *up = (struct sport_uart_port *)port; | ||
450 | |||
451 | pr_debug("%s enter\n", __FUNCTION__); | ||
452 | up->port.type = PORT_BFIN_SPORT; | ||
453 | } | ||
454 | |||
455 | static int sport_verify_port(struct uart_port *port, struct serial_struct *ser) | ||
456 | { | ||
457 | pr_debug("%s enter\n", __FUNCTION__); | ||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | struct uart_ops sport_uart_ops = { | ||
462 | .tx_empty = sport_tx_empty, | ||
463 | .set_mctrl = sport_set_mctrl, | ||
464 | .get_mctrl = sport_get_mctrl, | ||
465 | .stop_tx = sport_stop_tx, | ||
466 | .start_tx = sport_start_tx, | ||
467 | .stop_rx = sport_stop_rx, | ||
468 | .enable_ms = sport_enable_ms, | ||
469 | .break_ctl = sport_break_ctl, | ||
470 | .startup = sport_startup, | ||
471 | .shutdown = sport_shutdown, | ||
472 | .set_termios = sport_set_termios, | ||
473 | .type = sport_type, | ||
474 | .release_port = sport_release_port, | ||
475 | .request_port = sport_request_port, | ||
476 | .config_port = sport_config_port, | ||
477 | .verify_port = sport_verify_port, | ||
478 | }; | ||
479 | |||
480 | static struct sport_uart_port sport_uart_ports[] = { | ||
481 | { /* SPORT 0 */ | ||
482 | .name = "SPORT0", | ||
483 | .tx_irq = IRQ_SPORT0_TX, | ||
484 | .rx_irq = IRQ_SPORT0_RX, | ||
485 | .err_irq= IRQ_SPORT0_ERROR, | ||
486 | .port = { | ||
487 | .type = PORT_BFIN_SPORT, | ||
488 | .iotype = UPIO_MEM, | ||
489 | .membase = (void __iomem *)SPORT0_TCR1, | ||
490 | .mapbase = SPORT0_TCR1, | ||
491 | .irq = IRQ_SPORT0_RX, | ||
492 | .uartclk = CONFIG_SPORT_BAUD_RATE, | ||
493 | .fifosize = 8, | ||
494 | .ops = &sport_uart_ops, | ||
495 | .line = 0, | ||
496 | }, | ||
497 | }, { /* SPORT 1 */ | ||
498 | .name = "SPORT1", | ||
499 | .tx_irq = IRQ_SPORT1_TX, | ||
500 | .rx_irq = IRQ_SPORT1_RX, | ||
501 | .err_irq= IRQ_SPORT1_ERROR, | ||
502 | .port = { | ||
503 | .type = PORT_BFIN_SPORT, | ||
504 | .iotype = UPIO_MEM, | ||
505 | .membase = (void __iomem *)SPORT1_TCR1, | ||
506 | .mapbase = SPORT1_TCR1, | ||
507 | .irq = IRQ_SPORT1_RX, | ||
508 | .uartclk = CONFIG_SPORT_BAUD_RATE, | ||
509 | .fifosize = 8, | ||
510 | .ops = &sport_uart_ops, | ||
511 | .line = 1, | ||
512 | }, | ||
513 | } | ||
514 | }; | ||
515 | |||
516 | static struct uart_driver sport_uart_reg = { | ||
517 | .owner = THIS_MODULE, | ||
518 | .driver_name = "SPORT-UART", | ||
519 | .dev_name = "ttySS", | ||
520 | .major = 204, | ||
521 | .minor = 84, | ||
522 | .nr = ARRAY_SIZE(sport_uart_ports), | ||
523 | .cons = NULL, | ||
524 | }; | ||
525 | |||
526 | static int sport_uart_suspend(struct platform_device *dev, pm_message_t state) | ||
527 | { | ||
528 | struct sport_uart_port *sport = platform_get_drvdata(dev); | ||
529 | |||
530 | pr_debug("%s enter\n", __FUNCTION__); | ||
531 | if (sport) | ||
532 | uart_suspend_port(&sport_uart_reg, &sport->port); | ||
533 | |||
534 | return 0; | ||
535 | } | ||
536 | |||
537 | static int sport_uart_resume(struct platform_device *dev) | ||
538 | { | ||
539 | struct sport_uart_port *sport = platform_get_drvdata(dev); | ||
540 | |||
541 | pr_debug("%s enter\n", __FUNCTION__); | ||
542 | if (sport) | ||
543 | uart_resume_port(&sport_uart_reg, &sport->port); | ||
544 | |||
545 | return 0; | ||
546 | } | ||
547 | |||
548 | static int sport_uart_probe(struct platform_device *dev) | ||
549 | { | ||
550 | pr_debug("%s enter\n", __FUNCTION__); | ||
551 | sport_uart_ports[dev->id].port.dev = &dev->dev; | ||
552 | uart_add_one_port(&sport_uart_reg, &sport_uart_ports[dev->id].port); | ||
553 | platform_set_drvdata(dev, &sport_uart_ports[dev->id]); | ||
554 | |||
555 | return 0; | ||
556 | } | ||
557 | |||
558 | static int sport_uart_remove(struct platform_device *dev) | ||
559 | { | ||
560 | struct sport_uart_port *sport = platform_get_drvdata(dev); | ||
561 | |||
562 | pr_debug("%s enter\n", __FUNCTION__); | ||
563 | platform_set_drvdata(dev, NULL); | ||
564 | |||
565 | if (sport) | ||
566 | uart_remove_one_port(&sport_uart_reg, &sport->port); | ||
567 | |||
568 | return 0; | ||
569 | } | ||
570 | |||
571 | static struct platform_driver sport_uart_driver = { | ||
572 | .probe = sport_uart_probe, | ||
573 | .remove = sport_uart_remove, | ||
574 | .suspend = sport_uart_suspend, | ||
575 | .resume = sport_uart_resume, | ||
576 | .driver = { | ||
577 | .name = DRV_NAME, | ||
578 | }, | ||
579 | }; | ||
580 | |||
581 | static int __init sport_uart_init(void) | ||
582 | { | ||
583 | int ret; | ||
584 | |||
585 | pr_debug("%s enter\n", __FUNCTION__); | ||
586 | ret = uart_register_driver(&sport_uart_reg); | ||
587 | if (ret != 0) { | ||
588 | printk(KERN_ERR "Failed to register %s:%d\n", | ||
589 | sport_uart_reg.driver_name, ret); | ||
590 | return ret; | ||
591 | } | ||
592 | |||
593 | ret = platform_driver_register(&sport_uart_driver); | ||
594 | if (ret != 0) { | ||
595 | printk(KERN_ERR "Failed to register sport uart driver:%d\n", ret); | ||
596 | uart_unregister_driver(&sport_uart_reg); | ||
597 | } | ||
598 | |||
599 | |||
600 | pr_debug("%s exit\n", __FUNCTION__); | ||
601 | return ret; | ||
602 | } | ||
603 | |||
604 | static void __exit sport_uart_exit(void) | ||
605 | { | ||
606 | pr_debug("%s enter\n", __FUNCTION__); | ||
607 | platform_driver_unregister(&sport_uart_driver); | ||
608 | uart_unregister_driver(&sport_uart_reg); | ||
609 | } | ||
610 | |||
611 | module_init(sport_uart_init); | ||
612 | module_exit(sport_uart_exit); | ||
613 | |||
614 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/serial/bfin_sport_uart.h b/drivers/serial/bfin_sport_uart.h new file mode 100644 index 000000000000..671d41cc1a3f --- /dev/null +++ b/drivers/serial/bfin_sport_uart.h | |||
@@ -0,0 +1,63 @@ | |||
1 | /* | ||
2 | * File: linux/drivers/serial/bfin_sport_uart.h | ||
3 | * | ||
4 | * Based on: include/asm-blackfin/mach-533/bfin_serial_5xx.h | ||
5 | * Author: Roy Huang <roy.huang>analog.com> | ||
6 | * | ||
7 | * Created: Nov 22, 2006 | ||
8 | * Copyright: (C) Analog Device Inc. | ||
9 | * Description: this driver enable SPORTs on Blackfin emulate UART. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, see the file COPYING, or write | ||
23 | * to the Free Software Foundation, Inc., | ||
24 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
25 | */ | ||
26 | |||
27 | |||
28 | #define OFFSET_TCR1 0x00 /* Transmit Configuration 1 Register */ | ||
29 | #define OFFSET_TCR2 0x04 /* Transmit Configuration 2 Register */ | ||
30 | #define OFFSET_TCLKDIV 0x08 /* Transmit Serial Clock Divider Register */ | ||
31 | #define OFFSET_TFSDIV 0x0C /* Transmit Frame Sync Divider Register */ | ||
32 | #define OFFSET_TX 0x10 /* Transmit Data Register */ | ||
33 | #define OFFSET_RX 0x18 /* Receive Data Register */ | ||
34 | #define OFFSET_RCR1 0x20 /* Receive Configuration 1 Register */ | ||
35 | #define OFFSET_RCR2 0x24 /* Receive Configuration 2 Register */ | ||
36 | #define OFFSET_RCLKDIV 0x28 /* Receive Serial Clock Divider Register */ | ||
37 | #define OFFSET_RFSDIV 0x2c /* Receive Frame Sync Divider Register */ | ||
38 | #define OFFSET_STAT 0x30 /* Status Register */ | ||
39 | |||
40 | #define SPORT_GET_TCR1(sport) bfin_read16(((sport)->port.membase + OFFSET_TCR1)) | ||
41 | #define SPORT_GET_TCR2(sport) bfin_read16(((sport)->port.membase + OFFSET_TCR2)) | ||
42 | #define SPORT_GET_TCLKDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_TCLKDIV)) | ||
43 | #define SPORT_GET_TFSDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_TFSDIV)) | ||
44 | #define SPORT_GET_TX(sport) bfin_read16(((sport)->port.membase + OFFSET_TX)) | ||
45 | #define SPORT_GET_RX(sport) bfin_read16(((sport)->port.membase + OFFSET_RX)) | ||
46 | #define SPORT_GET_RX32(sport) bfin_read32(((sport)->port.membase + OFFSET_RX)) | ||
47 | #define SPORT_GET_RCR1(sport) bfin_read16(((sport)->port.membase + OFFSET_RCR1)) | ||
48 | #define SPORT_GET_RCR2(sport) bfin_read16(((sport)->port.membase + OFFSET_RCR2)) | ||
49 | #define SPORT_GET_RCLKDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_RCLKDIV)) | ||
50 | #define SPORT_GET_RFSDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_RFSDIV)) | ||
51 | #define SPORT_GET_STAT(sport) bfin_read16(((sport)->port.membase + OFFSET_STAT)) | ||
52 | |||
53 | #define SPORT_PUT_TCR1(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TCR1), v) | ||
54 | #define SPORT_PUT_TCR2(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TCR2), v) | ||
55 | #define SPORT_PUT_TCLKDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TCLKDIV), v) | ||
56 | #define SPORT_PUT_TFSDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TFSDIV), v) | ||
57 | #define SPORT_PUT_TX(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TX), v) | ||
58 | #define SPORT_PUT_RX(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RX), v) | ||
59 | #define SPORT_PUT_RCR1(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RCR1), v) | ||
60 | #define SPORT_PUT_RCR2(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RCR2), v) | ||
61 | #define SPORT_PUT_RCLKDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RCLKDIV), v) | ||
62 | #define SPORT_PUT_RFSDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RFSDIV), v) | ||
63 | #define SPORT_PUT_STAT(sport, v) bfin_write16(((sport)->port.membase + OFFSET_STAT), v) | ||