Commit 195fd228 authored by sonic zhang's avatar sonic zhang Committed by james toy

Fit blackfin uart over sport driver into common uart inftrastructure. It

is based on the early platform interfaces to get the platform data early
when the console is initilized.

1. Enable sport uart driver to change uart baud, data bit, stop bit at
   runtime.  Bind the index of uart device nodes to physical index of
   sports.

2. Move all platform data into arch specific board files.  Register
   and probe platform device data in both early and normal stages.

3. Console is registered in sport uart driver as well.

4. Remove 500 us block waiting in sport tx stop code by putting a
   dummy data into tx fifo to make sure the sport tx stops when all bytes
   are shifted out except for the dummy data.

5. clean up a bit and fix up coding style.
Signed-off-by: default avatarSonic Zhang <sonic.zhang@analog.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Mike Frysinger <vapier@gentoo.org>
Cc: Bryan Wu <cooloney@kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent e3b18d89
...@@ -1418,38 +1418,34 @@ config SERIAL_BFIN_SPORT ...@@ -1418,38 +1418,34 @@ config SERIAL_BFIN_SPORT
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called bfin_sport_uart. module will be called bfin_sport_uart.
choice config SERIAL_BFIN_SPORT_CONSOLE
prompt "Baud rate for Blackfin SPORT UART" bool "Console on Blackfin sport emulated uart"
depends on SERIAL_BFIN_SPORT depends on SERIAL_BFIN_SPORT=y
default SERIAL_SPORT_BAUD_RATE_57600 select SERIAL_CORE_CONSOLE
help
Choose a baud rate for the SPORT UART, other uart settings are
8 bit, 1 stop bit, no parity, no flow control.
config SERIAL_SPORT_BAUD_RATE_115200
bool "115200"
config SERIAL_SPORT_BAUD_RATE_57600
bool "57600"
config SERIAL_SPORT_BAUD_RATE_38400 config SERIAL_BFIN_SPORT0_UART
bool "38400" bool "Enable UART over SPORT0"
depends on SERIAL_BFIN_SPORT && !(BF542 || BF542M || BF544 || BF544M)
help
Enable UART over SPORT0
config SERIAL_SPORT_BAUD_RATE_19200 config SERIAL_BFIN_SPORT1_UART
bool "19200" bool "Enable UART over SPORT1"
depends on SERIAL_BFIN_SPORT
help
Enable UART over SPORT1
config SERIAL_SPORT_BAUD_RATE_9600 config SERIAL_BFIN_SPORT2_UART
bool "9600" bool "Enable UART over SPORT2"
endchoice depends on SERIAL_BFIN_SPORT && (BF54x || BF538 || BF539)
help
Enable UART over SPORT2
config SPORT_BAUD_RATE config SERIAL_BFIN_SPORT3_UART
int bool "Enable UART over SPORT3"
depends on SERIAL_BFIN_SPORT depends on SERIAL_BFIN_SPORT && (BF54x || BF538 || BF539)
default 115200 if (SERIAL_SPORT_BAUD_RATE_115200) help
default 57600 if (SERIAL_SPORT_BAUD_RATE_57600) Enable UART over SPORT3
default 38400 if (SERIAL_SPORT_BAUD_RATE_38400)
default 19200 if (SERIAL_SPORT_BAUD_RATE_19200)
default 9600 if (SERIAL_SPORT_BAUD_RATE_9600)
config SERIAL_TIMBERDALE config SERIAL_TIMBERDALE
tristate "Support for timberdale UART" tristate "Support for timberdale UART"
......
/* /*
* File: linux/drivers/serial/bfin_sport_uart.c * Blackfin On-Chip Sport Emulated UART Driver
* *
* Based on: drivers/serial/bfin_5xx.c by Aubrey Li. * Copyright 2006-2009 Analog Devices Inc.
* Author: Roy Huang <roy.huang@analog.com>
* *
* Created: Nov 22, 2006 * Enter bugs at http://blackfin.uclinux.org/
* Copyright: (c) 2006-2007 Analog Devices Inc.
* Description: this driver enable SPORTs on Blackfin emulate UART.
* *
* This program is free software; you can redistribute it and/or modify * Licensed under the GPL-2 or later.
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see the file COPYING, or write
* to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
/* /*
...@@ -29,39 +13,18 @@ ...@@ -29,39 +13,18 @@
* http://www.analog.com/UploadedFiles/Application_Notes/399447663EE191.pdf * http://www.analog.com/UploadedFiles/Application_Notes/399447663EE191.pdf
* This application note describe how to implement a UART on a Sharc DSP, * This application note describe how to implement a UART on a Sharc DSP,
* but this driver is implemented on Blackfin Processor. * but this driver is implemented on Blackfin Processor.
* Transmit Frame Sync is not used by this driver to transfer data out.
*/ */
/* After reset, there is a prelude of low level pulse when transmit data first /* #define DEBUG */
* time. No addtional pulse in following transmit.
* According to document:
* The SPORTs are ready to start transmitting or receiving data no later than
* three serial clock cycles after they are enabled in the SPORTx_TCR1 or
* SPORTx_RCR1 register. No serial clock cycles are lost from this point on.
* The first internal frame sync will occur one frame sync delay after the
* SPORTs are ready. External frame syncs can occur as soon as the SPORT is
* ready.
*/
/* Thanks to Axel Alatalo <axel@rubico.se> for fixing sport rx bug. Sometimes #define DRV_NAME "bfin-sport-uart"
* sport receives data incorrectly. The following is Axel's words. #define DEVICE_NAME "ttySS"
* As EE-191, sport rx samples 3 times of the UART baudrate and takes the #define pr_fmt(fmt) DRV_NAME ": " fmt
* middle smaple of every 3 samples as the data bit. For a 8-N-1 UART setting,
* 30 samples will be required for a byte. If transmitter sends a 1/3 bit short
* byte due to buadrate drift, then the 30th sample of a byte, this sample is
* also the third sample of the stop bit, will happens on the immediately
* following start bit which will be thrown away and missed. Thus since parts
* of the startbit will be missed and the receiver will begin to drift, the
* effect accumulates over time until synchronization is lost.
* If only require 2 samples of the stopbit (by sampling in total 29 samples),
* then a to short byte as in the case above will be tolerated. Then the 1/3
* early startbit will trigger a framesync since the last read is complete
* after only 2/3 stopbit and framesync is active during the last 1/3 looking
* for a possible early startbit. */
//#define DEBUG
#include <linux/module.h> #include <linux/module.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/io.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/console.h> #include <linux/console.h>
#include <linux/sysrq.h> #include <linux/sysrq.h>
...@@ -75,23 +38,36 @@ ...@@ -75,23 +38,36 @@
#include "bfin_sport_uart.h" #include "bfin_sport_uart.h"
#ifdef CONFIG_SERIAL_BFIN_SPORT0_UART
unsigned short bfin_uart_pin_req_sport0[] = unsigned short bfin_uart_pin_req_sport0[] =
{P_SPORT0_TFS, P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, \ {P_SPORT0_TFS, P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, \
P_SPORT0_DRPRI, P_SPORT0_RSCLK, P_SPORT0_DRSEC, P_SPORT0_DTSEC, 0}; P_SPORT0_DRPRI, P_SPORT0_RSCLK, P_SPORT0_DRSEC, P_SPORT0_DTSEC, 0};
#endif
#ifdef CONFIG_SERIAL_BFIN_SPORT1_UART
unsigned short bfin_uart_pin_req_sport1[] = unsigned short bfin_uart_pin_req_sport1[] =
{P_SPORT1_TFS, P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, \ {P_SPORT1_TFS, P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, \
P_SPORT1_DRPRI, P_SPORT1_RSCLK, P_SPORT1_DRSEC, P_SPORT1_DTSEC, 0}; P_SPORT1_DRPRI, P_SPORT1_RSCLK, P_SPORT1_DRSEC, P_SPORT1_DTSEC, 0};
#endif
#define DRV_NAME "bfin-sport-uart" #ifdef CONFIG_SERIAL_BFIN_SPORT2_UART
unsigned short bfin_uart_pin_req_sport2[] =
{P_SPORT2_TFS, P_SPORT2_DTPRI, P_SPORT2_TSCLK, P_SPORT2_RFS, \
P_SPORT2_DRPRI, P_SPORT2_RSCLK, P_SPORT2_DRSEC, P_SPORT2_DTSEC, 0};
#endif
#ifdef CONFIG_SERIAL_BFIN_SPORT3_UART
unsigned short bfin_uart_pin_req_sport3[] =
{P_SPORT3_TFS, P_SPORT3_DTPRI, P_SPORT3_TSCLK, P_SPORT3_RFS, \
P_SPORT3_DRPRI, P_SPORT3_RSCLK, P_SPORT3_DRSEC, P_SPORT3_DTSEC, 0};
#endif
struct sport_uart_port { struct sport_uart_port {
struct uart_port port; struct uart_port port;
char *name;
int tx_irq;
int rx_irq;
int err_irq; int err_irq;
unsigned short csize;
unsigned short rxmask;
unsigned short txmask1;
unsigned short txmask2;
unsigned char stopb;
/* unsigned char parib; */
}; };
static void sport_uart_tx_chars(struct sport_uart_port *up); static void sport_uart_tx_chars(struct sport_uart_port *up);
...@@ -99,36 +75,42 @@ static void sport_stop_tx(struct uart_port *port); ...@@ -99,36 +75,42 @@ static void sport_stop_tx(struct uart_port *port);
static inline void tx_one_byte(struct sport_uart_port *up, unsigned int value) static inline void tx_one_byte(struct sport_uart_port *up, unsigned int value)
{ {
pr_debug("%s value:%x\n", __func__, value); pr_debug("%s value:%x, mask1=0x%x, mask2=0x%x\n", __func__, value,
/* Place a Start and Stop bit */ up->txmask1, up->txmask2);
/* Place Start and Stop bits */
__asm__ __volatile__ ( __asm__ __volatile__ (
"R2 = b#01111111100;" "%[val] <<= 1;"
"R3 = b#10000000001;" "%[val] = %[val] & %[mask1];"
"%0 <<= 2;" "%[val] = %[val] | %[mask2];"
"%0 = %0 & R2;" : [val]"+d"(value)
"%0 = %0 | R3;" : [mask1]"d"(up->txmask1), [mask2]"d"(up->txmask2)
: "=d"(value) : "ASTAT"
: "d"(value)
: "ASTAT", "R2", "R3"
); );
pr_debug("%s value:%x\n", __func__, value); pr_debug("%s value:%x\n", __func__, value);
SPORT_PUT_TX(up, value); SPORT_PUT_TX(up, value);
} }
static inline unsigned int rx_one_byte(struct sport_uart_port *up) static inline unsigned char rx_one_byte(struct sport_uart_port *up)
{ {
unsigned int value, extract; unsigned int value;
unsigned char extract;
u32 tmp_mask1, tmp_mask2, tmp_shift, tmp; u32 tmp_mask1, tmp_mask2, tmp_shift, tmp;
if ((up->csize + up->stopb) > 7)
value = SPORT_GET_RX32(up); value = SPORT_GET_RX32(up);
pr_debug("%s value:%x\n", __func__, value); else
value = SPORT_GET_RX(up);
pr_debug("%s value:%x, cs=%d, mask=0x%x\n", __func__, value,
up->csize, up->rxmask);
/* Extract 8 bits data */ /* Extract data */
__asm__ __volatile__ ( __asm__ __volatile__ (
"%[extr] = 0;" "%[extr] = 0;"
"%[mask1] = 0x1801(Z);" "%[mask1] = %[rxmask];"
"%[mask2] = 0x0300(Z);" "%[mask2] = 0x0200(Z);"
"%[shift] = 0;" "%[shift] = 0;"
"LSETUP(.Lloop_s, .Lloop_e) LC0 = %[lc];" "LSETUP(.Lloop_s, .Lloop_e) LC0 = %[lc];"
".Lloop_s:" ".Lloop_s:"
...@@ -138,9 +120,9 @@ static inline unsigned int rx_one_byte(struct sport_uart_port *up) ...@@ -138,9 +120,9 @@ static inline unsigned int rx_one_byte(struct sport_uart_port *up)
"%[mask1] = %[mask1] - %[mask2];" "%[mask1] = %[mask1] - %[mask2];"
".Lloop_e:" ".Lloop_e:"
"%[shift] += 1;" "%[shift] += 1;"
: [val]"=d"(value), [extr]"=d"(extract), [shift]"=d"(tmp_shift), [tmp]"=d"(tmp), : [extr]"=&d"(extract), [shift]"=&d"(tmp_shift), [tmp]"=&d"(tmp),
[mask1]"=d"(tmp_mask1), [mask2]"=d"(tmp_mask2) [mask1]"=&d"(tmp_mask1), [mask2]"=&d"(tmp_mask2)
: "d"(value), [lc]"a"(8) : [val]"d"(value), [rxmask]"d"(up->rxmask), [lc]"a"(up->csize)
: "ASTAT", "LB0", "LC0", "LT0" : "ASTAT", "LB0", "LC0", "LT0"
); );
...@@ -148,29 +130,28 @@ static inline unsigned int rx_one_byte(struct sport_uart_port *up) ...@@ -148,29 +130,28 @@ static inline unsigned int rx_one_byte(struct sport_uart_port *up)
return extract; return extract;
} }
static int sport_uart_setup(struct sport_uart_port *up, int sclk, int baud_rate) static int sport_uart_setup(struct sport_uart_port *up, int size, int baud_rate)
{ {
int tclkdiv, tfsdiv, rclkdiv; int tclkdiv, rclkdiv;
unsigned int sclk = get_sclk();
/* Set TCR1 and TCR2 */ /* Set TCR1 and TCR2, TFSR is not enabled for uart */
SPORT_PUT_TCR1(up, (LATFS | ITFS | TFSR | TLSBIT | ITCLK)); SPORT_PUT_TCR1(up, (ITFS | TLSBIT | ITCLK));
SPORT_PUT_TCR2(up, 10); SPORT_PUT_TCR2(up, size + 1);
pr_debug("%s TCR1:%x, TCR2:%x\n", __func__, SPORT_GET_TCR1(up), SPORT_GET_TCR2(up)); pr_debug("%s TCR1:%x, TCR2:%x\n", __func__, SPORT_GET_TCR1(up), SPORT_GET_TCR2(up));
/* Set RCR1 and RCR2 */ /* Set RCR1 and RCR2 */
SPORT_PUT_RCR1(up, (RCKFE | LARFS | LRFS | RFSR | IRCLK)); SPORT_PUT_RCR1(up, (RCKFE | LARFS | LRFS | RFSR | IRCLK));
SPORT_PUT_RCR2(up, 28); SPORT_PUT_RCR2(up, (size + 1) * 2 - 1);
pr_debug("%s RCR1:%x, RCR2:%x\n", __func__, SPORT_GET_RCR1(up), SPORT_GET_RCR2(up)); pr_debug("%s RCR1:%x, RCR2:%x\n", __func__, SPORT_GET_RCR1(up), SPORT_GET_RCR2(up));
tclkdiv = sclk/(2 * baud_rate) - 1; tclkdiv = sclk / (2 * baud_rate) - 1;
tfsdiv = 12; rclkdiv = sclk / (2 * baud_rate * 2) - 1;
rclkdiv = sclk/(2 * baud_rate * 3) - 1;
SPORT_PUT_TCLKDIV(up, tclkdiv); SPORT_PUT_TCLKDIV(up, tclkdiv);
SPORT_PUT_TFSDIV(up, tfsdiv);
SPORT_PUT_RCLKDIV(up, rclkdiv); SPORT_PUT_RCLKDIV(up, rclkdiv);
SSYNC(); SSYNC();
pr_debug("%s sclk:%d, baud_rate:%d, tclkdiv:%d, tfsdiv:%d, rclkdiv:%d\n", pr_debug("%s sclk:%d, baud_rate:%d, tclkdiv:%d, rclkdiv:%d\n",
__func__, sclk, baud_rate, tclkdiv, tfsdiv, rclkdiv); __func__, sclk, baud_rate, tclkdiv, rclkdiv);
return 0; return 0;
} }
...@@ -181,23 +162,29 @@ static irqreturn_t sport_uart_rx_irq(int irq, void *dev_id) ...@@ -181,23 +162,29 @@ static irqreturn_t sport_uart_rx_irq(int irq, void *dev_id)
struct tty_struct *tty = up->port.state->port.tty; struct tty_struct *tty = up->port.state->port.tty;
unsigned int ch; unsigned int ch;
do { spin_lock(&up->port.lock);
while (SPORT_GET_STAT(up) & RXNE) {
ch = rx_one_byte(up); ch = rx_one_byte(up);
up->port.icount.rx++; up->port.icount.rx++;
if (uart_handle_sysrq_char(&up->port, ch)) if (!uart_handle_sysrq_char(&up->port, ch))
;
else
tty_insert_flip_char(tty, ch, TTY_NORMAL); tty_insert_flip_char(tty, ch, TTY_NORMAL);
} while (SPORT_GET_STAT(up) & RXNE); }
tty_flip_buffer_push(tty); tty_flip_buffer_push(tty);
spin_unlock(&up->port.lock);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static irqreturn_t sport_uart_tx_irq(int irq, void *dev_id) static irqreturn_t sport_uart_tx_irq(int irq, void *dev_id)
{ {
sport_uart_tx_chars(dev_id); struct sport_uart_port *up = dev_id;
spin_lock(&up->port.lock);
sport_uart_tx_chars(up);
spin_unlock(&up->port.lock);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -208,6 +195,8 @@ static irqreturn_t sport_uart_err_irq(int irq, void *dev_id) ...@@ -208,6 +195,8 @@ static irqreturn_t sport_uart_err_irq(int irq, void *dev_id)
struct tty_struct *tty = up->port.state->port.tty; struct tty_struct *tty = up->port.state->port.tty;
unsigned int stat = SPORT_GET_STAT(up); unsigned int stat = SPORT_GET_STAT(up);
spin_lock(&up->port.lock);
/* Overflow in RX FIFO */ /* Overflow in RX FIFO */
if (stat & ROVF) { if (stat & ROVF) {
up->port.icount.overrun++; up->port.icount.overrun++;
...@@ -216,15 +205,16 @@ static irqreturn_t sport_uart_err_irq(int irq, void *dev_id) ...@@ -216,15 +205,16 @@ static irqreturn_t sport_uart_err_irq(int irq, void *dev_id)
} }
/* These should not happen */ /* These should not happen */
if (stat & (TOVF | TUVF | RUVF)) { if (stat & (TOVF | TUVF | RUVF)) {
printk(KERN_ERR "SPORT Error:%s %s %s\n", pr_err("SPORT Error:%s %s %s\n",
(stat & TOVF)?"TX overflow":"", (stat & TOVF) ? "TX overflow" : "",
(stat & TUVF)?"TX underflow":"", (stat & TUVF) ? "TX underflow" : "",
(stat & RUVF)?"RX underflow":""); (stat & RUVF) ? "RX underflow" : "");
SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN); SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN);
SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) & ~RSPEN); SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) & ~RSPEN);
} }
SSYNC(); SSYNC();
spin_unlock(&up->port.lock);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -232,60 +222,37 @@ static irqreturn_t sport_uart_err_irq(int irq, void *dev_id) ...@@ -232,60 +222,37 @@ static irqreturn_t sport_uart_err_irq(int irq, void *dev_id)
static int sport_startup(struct uart_port *port) static int sport_startup(struct uart_port *port)
{ {
struct sport_uart_port *up = (struct sport_uart_port *)port; struct sport_uart_port *up = (struct sport_uart_port *)port;
char buffer[20]; int ret;
int retval;
pr_debug("%s enter\n", __func__); pr_debug("%s enter\n", __func__);
snprintf(buffer, 20, "%s rx", up->name); ret = request_irq(up->port.irq, sport_uart_rx_irq, 0,
retval = request_irq(up->rx_irq, sport_uart_rx_irq, IRQF_SAMPLE_RANDOM, buffer, up); "SPORT_UART_RX", up);
if (retval) { if (ret) {
printk(KERN_ERR "Unable to request interrupt %s\n", buffer); dev_err(port->dev, "unable to request SPORT RX interrupt\n");
return retval; return ret;
} }
snprintf(buffer, 20, "%s tx", up->name); ret = request_irq(up->port.irq+1, sport_uart_tx_irq, 0,
retval = request_irq(up->tx_irq, sport_uart_tx_irq, IRQF_SAMPLE_RANDOM, buffer, up); "SPORT_UART_TX", up);
if (retval) { if (ret) {
printk(KERN_ERR "Unable to request interrupt %s\n", buffer); dev_err(port->dev, "unable to request SPORT TX interrupt\n");
goto fail1; goto fail1;
} }
snprintf(buffer, 20, "%s err", up->name); ret = request_irq(up->err_irq, sport_uart_err_irq, 0,
retval = request_irq(up->err_irq, sport_uart_err_irq, IRQF_SAMPLE_RANDOM, buffer, up); "SPORT_UART_STATUS", up);
if (retval) { if (ret) {
printk(KERN_ERR "Unable to request interrupt %s\n", buffer); dev_err(port->dev, "unable to request SPORT status interrupt\n");
goto fail2; goto fail2;
} }
if (port->line) {
if (peripheral_request_list(bfin_uart_pin_req_sport1, DRV_NAME))
goto fail3;
} else {
if (peripheral_request_list(bfin_uart_pin_req_sport0, DRV_NAME))
goto fail3;
}
sport_uart_setup(up, get_sclk(), port->uartclk);
/* Enable receive interrupt */
SPORT_PUT_RCR1(up, (SPORT_GET_RCR1(up) | RSPEN));
SSYNC();
return 0; return 0;
fail2:
free_irq(up->port.irq+1, up);
fail1:
free_irq(up->port.irq, up);
return ret;
fail3:
printk(KERN_ERR DRV_NAME
": Requesting Peripherals failed\n");
free_irq(up->err_irq, up);
fail2:
free_irq(up->tx_irq, up);
fail1:
free_irq(up->rx_irq, up);
return retval;
} }
static void sport_uart_tx_chars(struct sport_uart_port *up) static void sport_uart_tx_chars(struct sport_uart_port *up)
...@@ -344,20 +311,17 @@ static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl) ...@@ -344,20 +311,17 @@ static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl)
static void sport_stop_tx(struct uart_port *port) static void sport_stop_tx(struct uart_port *port)
{ {
struct sport_uart_port *up = (struct sport_uart_port *)port; struct sport_uart_port *up = (struct sport_uart_port *)port;
unsigned int stat;
pr_debug("%s enter\n", __func__); pr_debug("%s enter\n", __func__);
stat = SPORT_GET_STAT(up);
while(!(stat & TXHRE)) {
udelay(1);
stat = SPORT_GET_STAT(up);
}
/* Although the hold register is empty, last byte is still in shift /* Although the hold register is empty, last byte is still in shift
* register and not sent out yet. If baud rate is lower than default, * register and not sent out yet. So, put a dummy data into TX FIFO.
* delay should be longer. For example, if the baud rate is 9600, * Then, sport tx stops when last byte is shift out and the dummy
* the delay must be at least 2ms by experience */ * data is moved into the shift register.
udelay(500); */
SPORT_PUT_TX(up, 0xffff);
while (!(SPORT_GET_STAT(up) & TXHRE))
cpu_relax();
SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN)); SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN));
SSYNC(); SSYNC();
...@@ -370,6 +334,7 @@ static void sport_start_tx(struct uart_port *port) ...@@ -370,6 +334,7 @@ static void sport_start_tx(struct uart_port *port)
struct sport_uart_port *up = (struct sport_uart_port *)port; struct sport_uart_port *up = (struct sport_uart_port *)port;
pr_debug("%s enter\n", __func__); pr_debug("%s enter\n", __func__);
/* Write data into SPORT FIFO before enable SPROT to transmit */ /* Write data into SPORT FIFO before enable SPROT to transmit */
sport_uart_tx_chars(up); sport_uart_tx_chars(up);
...@@ -403,37 +368,24 @@ static void sport_shutdown(struct uart_port *port) ...@@ -403,37 +368,24 @@ static void sport_shutdown(struct uart_port *port)
{ {
struct sport_uart_port *up = (struct sport_uart_port *)port; struct sport_uart_port *up = (struct sport_uart_port *)port;
pr_debug("%s enter\n", __func__); dev_dbg(port->dev, "%s enter\n", __func__);
/* Disable sport */ /* Disable sport */
SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN)); SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN));
SPORT_PUT_RCR1(up, (SPORT_GET_RCR1(up) & ~RSPEN)); SPORT_PUT_RCR1(up, (SPORT_GET_RCR1(up) & ~RSPEN));
SSYNC(); SSYNC();
if (port->line) { free_irq(up->port.irq, up);
peripheral_free_list(bfin_uart_pin_req_sport1); free_irq(up->port.irq+1, up);
} else {
peripheral_free_list(bfin_uart_pin_req_sport0);
}
free_irq(up->rx_irq, up);
free_irq(up->tx_irq, up);
free_irq(up->err_irq, up); free_irq(up->err_irq, up);
} }
static void sport_set_termios(struct uart_port *port,
struct ktermios *termios, struct ktermios *old)
{
pr_debug("%s enter, c_cflag:%08x\n", __func__, termios->c_cflag);
uart_update_timeout(port, CS8 ,port->uartclk);
}
static const char *sport_type(struct uart_port *port) static const char *sport_type(struct uart_port *port)
{ {
struct sport_uart_port *up = (struct sport_uart_port *)port; struct sport_uart_port *up = (struct sport_uart_port *)port;
pr_debug("%s enter\n", __func__); pr_debug("%s enter\n", __func__);
return up->name; return up->port.type == PORT_BFIN_SPORT ? "BFIN-SPORT-UART" : NULL;
} }
static void sport_release_port(struct uart_port *port) static void sport_release_port(struct uart_port *port)
...@@ -461,6 +413,110 @@ static int sport_verify_port(struct uart_port *port, struct serial_struct *ser) ...@@ -461,6 +413,110 @@ static int sport_verify_port(struct uart_port *port, struct serial_struct *ser)
return 0; return 0;
} }
static void sport_set_termios(struct uart_port *port,
struct ktermios *termios, struct ktermios *old)
{
struct sport_uart_port *up = (struct sport_uart_port *)port;
unsigned long flags;
int i;
pr_debug("%s enter, c_cflag:%08x\n", __func__, termios->c_cflag);
switch (termios->c_cflag & CSIZE) {
case CS8:
up->csize = 8;
break;
case CS7:
up->csize = 7;
break;
case CS6:
up->csize = 6;
break;
case CS5:
up->csize = 5;
break;
default:
pr_warning("requested word length not supported\n");
}
if (termios->c_cflag & CSTOPB) {
up->stopb = 1;
}
if (termios->c_cflag & PARENB) {
pr_warning("PAREN bits is not supported yet\n");
/* up->parib = 1; */
}
port->read_status_mask = OE;
if (termios->c_iflag & INPCK)
port->read_status_mask |= (FE | PE);
if (termios->c_iflag & (BRKINT | PARMRK))
port->read_status_mask |= BI;
/*
* Characters to ignore
*/
port->ignore_status_mask = 0;
if (termios->c_iflag & IGNPAR)
port->ignore_status_mask |= FE | PE;
if (termios->c_iflag & IGNBRK) {
port->ignore_status_mask |= BI;
/*
* If we're ignoring parity and break indicators,
* ignore overruns too (for real raw support).
*/
if (termios->c_iflag & IGNPAR)
port->ignore_status_mask |= OE;
}
/* RX extract mask */
up->rxmask = 0x01 | (((up->csize + up->stopb) * 2 - 1) << 0x8);
/* TX masks, 8 bit data and 1 bit stop for example:
* mask1 = b#0111111110
* mask2 = b#1000000000
*/
for (i = 0, up->txmask1 = 0; i < up->csize; i++)
up->txmask1 |= (1<<i);
up->txmask2 = (1<<i);
if (up->stopb) {
++i;
up->txmask2 |= (1<<i);
}
up->txmask1 <<= 1;
up->txmask2 <<= 1;
/* uart baud rate */
port->uartclk = uart_get_baud_rate(port, termios, old, 0, get_sclk()/16);
spin_lock_irqsave(&up->port.lock, flags);
/* Disable UART */
SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN);
SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) & ~RSPEN);
sport_uart_setup(up, up->csize + up->stopb, port->uartclk);
/* driver TX line high after config, one dummy data is
* necessary to stop sport after shift one byte
*/
SPORT_PUT_TX(up, 0xffff);
SPORT_PUT_TX(up, 0xffff);
SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN));
SSYNC();
while (!(SPORT_GET_STAT(up) & TXHRE))
cpu_relax();
SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN);
SSYNC();
/* Port speed changed, update the per-port timeout. */
uart_update_timeout(port, termios->c_cflag, port->uartclk);
/* Enable sport rx */
SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) | RSPEN);
SSYNC();
spin_unlock_irqrestore(&up->port.lock, flags);
}
struct uart_ops sport_uart_ops = { struct uart_ops sport_uart_ops = {
.tx_empty = sport_tx_empty, .tx_empty = sport_tx_empty,
.set_mctrl = sport_set_mctrl, .set_mctrl = sport_set_mctrl,
...@@ -480,138 +536,319 @@ struct uart_ops sport_uart_ops = { ...@@ -480,138 +536,319 @@ struct uart_ops sport_uart_ops = {
.verify_port = sport_verify_port, .verify_port = sport_verify_port,
}; };
static struct sport_uart_port sport_uart_ports[] = { #define BFIN_SPORT_UART_MAX_PORTS 4
{ /* SPORT 0 */
.name = "SPORT0", static struct sport_uart_port *bfin_sport_uart_ports[BFIN_SPORT_UART_MAX_PORTS];
.tx_irq = IRQ_SPORT0_TX,
.rx_irq = IRQ_SPORT0_RX, #ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE
.err_irq= IRQ_SPORT0_ERROR, static int __init
.port = { sport_uart_console_setup(struct console *co, char *options)
.type = PORT_BFIN_SPORT, {
.iotype = UPIO_MEM, struct sport_uart_port *up;
.membase = (void __iomem *)SPORT0_TCR1, int baud = 57600;
.mapbase = SPORT0_TCR1, int bits = 8;
.irq = IRQ_SPORT0_RX, int parity = 'n';
.uartclk = CONFIG_SPORT_BAUD_RATE, int flow = 'n';
.fifosize = 8,
.ops = &sport_uart_ops, /* Check whether an invalid uart number has been specified */
.line = 0, if (co->index < 0 || co->index >= BFIN_SPORT_UART_MAX_PORTS)
}, return -ENODEV;
}, { /* SPORT 1 */
.name = "SPORT1", up = bfin_sport_uart_ports[co->index];
.tx_irq = IRQ_SPORT1_TX, if (!up)
.rx_irq = IRQ_SPORT1_RX, return -ENODEV;
.err_irq= IRQ_SPORT1_ERROR,
.port = { if (options)
.type = PORT_BFIN_SPORT, uart_parse_options(options, &baud, &parity, &bits, &flow);
.iotype = UPIO_MEM,
.membase = (void __iomem *)SPORT1_TCR1, return uart_set_options(&up->port, co, baud, parity, bits, flow);
.mapbase = SPORT1_TCR1, }
.irq = IRQ_SPORT1_RX,
.uartclk = CONFIG_SPORT_BAUD_RATE, static void sport_uart_console_putchar(struct uart_port *port, int ch)
.fifosize = 8, {
.ops = &sport_uart_ops, struct sport_uart_port *up = (struct sport_uart_port *)port;
.line = 1,
}, while (SPORT_GET_STAT(up) & TXF)
barrier();
tx_one_byte(up, ch);
}
/*
* Interrupts are disabled on entering
*/
static void
sport_uart_console_write(struct console *co, const char *s, unsigned int count)
{
struct sport_uart_port *up = bfin_sport_uart_ports[co->index];
unsigned long flags;
spin_lock_irqsave(&up->port.lock, flags);
if (SPORT_GET_TCR1(up) & TSPEN)
uart_console_write(&up->port, s, count, sport_uart_console_putchar);
else {
/* dummy data to start sport */
while (SPORT_GET_STAT(up) & TXF)
barrier();
SPORT_PUT_TX(up, 0xffff);
/* Enable transmit, then an interrupt will generated */
SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN));
SSYNC();
uart_console_write(&up->port, s, count, sport_uart_console_putchar);
/* Although the hold register is empty, last byte is still in shift
* register and not sent out yet. So, put a dummy data into TX FIFO.
* Then, sport tx stops when last byte is shift out and the dummy
* data is moved into the shift register.
*/
while (SPORT_GET_STAT(up) & TXF)
barrier();
SPORT_PUT_TX(up, 0xffff);
while (!(SPORT_GET_STAT(up) & TXHRE))
barrier();
/* Stop sport tx transfer */
SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN));
SSYNC();
} }
spin_unlock_irqrestore(&up->port.lock, flags);
}
static struct uart_driver sport_uart_reg;
static struct console sport_uart_console = {
.name = DEVICE_NAME,
.write = sport_uart_console_write,
.device = uart_console_device,
.setup = sport_uart_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &sport_uart_reg,
}; };
#define SPORT_UART_CONSOLE (&sport_uart_console)
#else
#define SPORT_UART_CONSOLE NULL
#endif /* CONFIG_SERIAL_BFIN_SPORT_CONSOLE */
static struct uart_driver sport_uart_reg = { static struct uart_driver sport_uart_reg = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.driver_name = "SPORT-UART", .driver_name = DRV_NAME,
.dev_name = "ttySS", .dev_name = DEVICE_NAME,
.major = 204, .major = 204,
.minor = 84, .minor = 84,
.nr = ARRAY_SIZE(sport_uart_ports), .nr = BFIN_SPORT_UART_MAX_PORTS,
.cons = NULL, .cons = SPORT_UART_CONSOLE,
}; };
static int sport_uart_suspend(struct platform_device *dev, pm_message_t state) #ifdef CONFIG_PM
static int sport_uart_suspend(struct device *dev)
{ {
struct sport_uart_port *sport = platform_get_drvdata(dev); struct sport_uart_port *sport = dev_get_drvdata(dev);
pr_debug("%s enter\n", __func__); dev_dbg(dev, "%s enter\n", __func__);
if (sport) if (sport)
uart_suspend_port(&sport_uart_reg, &sport->port); uart_suspend_port(&sport_uart_reg, &sport->port);
return 0; return 0;
} }
static int sport_uart_resume(struct platform_device *dev) static int sport_uart_resume(struct device *dev)
{ {
struct sport_uart_port *sport = platform_get_drvdata(dev); struct sport_uart_port *sport = dev_get_drvdata(dev);
pr_debug("%s enter\n", __func__); dev_dbg(dev, "%s enter\n", __func__);
if (sport) if (sport)
uart_resume_port(&sport_uart_reg, &sport->port); uart_resume_port(&sport_uart_reg, &sport->port);
return 0; return 0;
} }
static int sport_uart_probe(struct platform_device *dev) static struct dev_pm_ops bfin_sport_uart_dev_pm_ops = {
.suspend = sport_uart_suspend,
.resume = sport_uart_resume,
};
#endif
static int __devinit sport_uart_probe(struct platform_device *pdev)
{ {
pr_debug("%s enter\n", __func__); struct resource *res;
sport_uart_ports[dev->id].port.dev = &dev->dev; struct sport_uart_port *sport;
uart_add_one_port(&sport_uart_reg, &sport_uart_ports[dev->id].port); int ret = 0;
platform_set_drvdata(dev, &sport_uart_ports[dev->id]);
dev_dbg(&pdev->dev, "%s enter\n", __func__);
if (pdev->id < 0 || pdev->id >= BFIN_SPORT_UART_MAX_PORTS) {
dev_err(&pdev->dev, "Wrong sport uart platform device id.\n");
return -ENOENT;
}
if (bfin_sport_uart_ports[pdev->id] == NULL) {
bfin_sport_uart_ports[pdev->id] =
kmalloc(sizeof(struct sport_uart_port), GFP_KERNEL);
sport = bfin_sport_uart_ports[pdev->id];
if (!sport) {
dev_err(&pdev->dev,
"Fail to kmalloc sport_uart_port\n");
return -ENOMEM;
}
ret = peripheral_request_list(
(unsigned short *)pdev->dev.platform_data, DRV_NAME);
if (ret) {
dev_err(&pdev->dev,
"Fail to request SPORT peripherals\n");
goto out_error_free_mem;
}
spin_lock_init(&sport->port.lock);
sport->port.fifosize = SPORT_TX_FIFO_SIZE,
sport->port.ops = &sport_uart_ops;
sport->port.line = pdev->id;
sport->port.iotype = UPIO_MEM;
sport->port.flags = UPF_BOOT_AUTOCONF;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
ret = -ENOENT;
goto out_error_free_peripherals;
}
sport->port.membase = ioremap(res->start,
res->end - res->start);
if (!sport->port.membase) {
dev_err(&pdev->dev, "Cannot map sport IO\n");
ret = -ENXIO;
goto out_error_free_peripherals;
}
sport->port.irq = platform_get_irq(pdev, 0);
if (sport->port.irq < 0) {
dev_err(&pdev->dev, "No sport RX/TX IRQ specified\n");
ret = -ENOENT;
goto out_error_unmap;
}
sport->err_irq = platform_get_irq(pdev, 1);
if (sport->err_irq < 0) {
dev_err(&pdev->dev, "No sport status IRQ specified\n");
ret = -ENOENT;
goto out_error_unmap;
}
}
#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE
if (!is_early_platform_device(pdev)) {
#endif
sport = bfin_sport_uart_ports[pdev->id];
sport->port.dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, sport);
ret = uart_add_one_port(&sport_uart_reg, &sport->port);
#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE
}
#endif
if (!ret)
return 0; return 0;
if (sport) {
out_error_unmap:
iounmap(sport->port.membase);
out_error_free_peripherals:
peripheral_free_list(
(unsigned short *)pdev->dev.platform_data);
out_error_free_mem:
kfree(sport);
bfin_sport_uart_ports[pdev->id] = NULL;
}
return ret;
} }
static int sport_uart_remove(struct platform_device *dev) static int __devexit sport_uart_remove(struct platform_device *pdev)
{ {
struct sport_uart_port *sport = platform_get_drvdata(dev); struct sport_uart_port *sport = platform_get_drvdata(pdev);
pr_debug("%s enter\n", __func__); dev_dbg(&pdev->dev, "%s enter\n", __func__);
platform_set_drvdata(dev, NULL); dev_set_drvdata(&pdev->dev, NULL);
if (sport) if (sport) {
uart_remove_one_port(&sport_uart_reg, &sport->port); uart_remove_one_port(&sport_uart_reg, &sport->port);
iounmap(sport->port.membase);
peripheral_free_list(
(unsigned short *)pdev->dev.platform_data);
kfree(sport);
bfin_sport_uart_ports[pdev->id] = NULL;
}
return 0; return 0;
} }
static struct platform_driver sport_uart_driver = { static struct platform_driver sport_uart_driver = {
.probe = sport_uart_probe, .probe = sport_uart_probe,
.remove = sport_uart_remove, .remove = __devexit_p(sport_uart_remove),
.suspend = sport_uart_suspend,
.resume = sport_uart_resume,
.driver = { .driver = {
.name = DRV_NAME, .name = DRV_NAME,
#ifdef CONFIG_PM
.pm = &bfin_sport_uart_dev_pm_ops,
#endif
}, },
}; };
#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE
static __initdata struct early_platform_driver early_sport_uart_driver = {
.class_str = DRV_NAME,
.pdrv = &sport_uart_driver,
.requested_id = EARLY_PLATFORM_ID_UNSET,
};
static int __init sport_uart_rs_console_init(void)
{
early_platform_driver_register(&early_sport_uart_driver, DRV_NAME);
early_platform_driver_probe(DRV_NAME, BFIN_SPORT_UART_MAX_PORTS, 0);
register_console(&sport_uart_console);
return 0;
}
console_initcall(sport_uart_rs_console_init);
#endif
static int __init sport_uart_init(void) static int __init sport_uart_init(void)
{ {
int ret; int ret;
pr_debug("%s enter\n", __func__); pr_info("Serial: Blackfin uart over sport driver\n");
ret = uart_register_driver(&sport_uart_reg); ret = uart_register_driver(&sport_uart_reg);
if (ret != 0) { if (ret) {
printk(KERN_ERR "Failed to register %s:%d\n", pr_err("failed to register %s:%d\n",
sport_uart_reg.driver_name, ret); sport_uart_reg.driver_name, ret);
return ret; return ret;
} }
ret = platform_driver_register(&sport_uart_driver); ret = platform_driver_register(&sport_uart_driver);
if (ret != 0) { if (ret) {
printk(KERN_ERR "Failed to register sport uart driver:%d\n", ret); pr_err("failed to register sport uart driver:%d\n", ret);
uart_unregister_driver(&sport_uart_reg); uart_unregister_driver(&sport_uart_reg);
} }
pr_debug("%s exit\n", __func__);
return ret; return ret;
} }
module_init(sport_uart_init);
static void __exit sport_uart_exit(void) static void __exit sport_uart_exit(void)
{ {
pr_debug("%s enter\n", __func__);
platform_driver_unregister(&sport_uart_driver); platform_driver_unregister(&sport_uart_driver);
uart_unregister_driver(&sport_uart_reg); uart_unregister_driver(&sport_uart_reg);
} }
module_init(sport_uart_init);
module_exit(sport_uart_exit); module_exit(sport_uart_exit);
MODULE_AUTHOR("Sonic Zhang, Roy Huang");
MODULE_DESCRIPTION("Blackfin serial over SPORT driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
/* /*
* File: linux/drivers/serial/bfin_sport_uart.h * Blackfin On-Chip Sport Emulated UART Driver
* *
* Based on: include/asm-blackfin/mach-533/bfin_serial_5xx.h * Copyright 2006-2008 Analog Devices Inc.
* Author: Roy Huang <roy.huang>analog.com>
* *
* Created: Nov 22, 2006 * Enter bugs at http://blackfin.uclinux.org/
* Copyright: (C) Analog Device Inc.
* Description: this driver enable SPORTs on Blackfin emulate UART.
* *
* This program is free software; you can redistribute it and/or modify * Licensed under the GPL-2 or later.
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see the file COPYING, or write
* to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
/*
* This driver and the hardware supported are in term of EE-191 of ADI.
* http://www.analog.com/UploadedFiles/Application_Notes/399447663EE191.pdf
* This application note describe how to implement a UART on a Sharc DSP,
* but this driver is implemented on Blackfin Processor.
* Transmit Frame Sync is not used by this driver to transfer data out.
*/
#ifndef _BFIN_SPORT_UART_H
#define _BFIN_SPORT_UART_H
#define OFFSET_TCR1 0x00 /* Transmit Configuration 1 Register */ #define OFFSET_TCR1 0x00 /* Transmit Configuration 1 Register */
#define OFFSET_TCR2 0x04 /* Transmit Configuration 2 Register */ #define OFFSET_TCR2 0x04 /* Transmit Configuration 2 Register */
...@@ -61,3 +55,7 @@ ...@@ -61,3 +55,7 @@
#define SPORT_PUT_RCLKDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RCLKDIV), v) #define SPORT_PUT_RCLKDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RCLKDIV), v)
#define SPORT_PUT_RFSDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RFSDIV), v) #define SPORT_PUT_RFSDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RFSDIV), v)
#define SPORT_PUT_STAT(sport, v) bfin_write16(((sport)->port.membase + OFFSET_STAT), v) #define SPORT_PUT_STAT(sport, v) bfin_write16(((sport)->port.membase + OFFSET_STAT), v)
#define SPORT_TX_FIFO_SIZE 8
#endif /* _BFIN_SPORT_UART_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment