Monday, July 28, 2008

blackfin spi device driver



/********************** ADAM **************************************/

The SPI master is registered as a platform devices, while SPI slave devices are registered
as devices in the SPI bus.

SPI Bus
~~~~~~~
spi.c: spi_init()
status = bus_register(&spi_bus_type);
status = class_register(&spi_master_class);

root:/sys/bus/spi> ls -lR
.:
drwxr-xr-x 2 root root 0 Jan 1 01:47 devices
drwxr-xr-x 3 root root 0 Jan 1 2007 drivers
-rw-r--r-- 1 root root 4096 Jan 1 01:47 drivers_autoprobe
--w------- 1 root root 4096 Jan 1 01:47 drivers_probe

./devices:
lrwxrwxrwx 1 root root 0 Jan 1 01:47 spi0.1 -> ../../../devices/platform/bfin-spi.0/spi0.1

./drivers:
drwxr-xr-x 2 root root 0 Jan 1 01:47 spidev

./drivers/spidev:
--w------- 1 root root 4096 Jan 1 01:47 bind
lrwxrwxrwx 1 root root 0 Jan 1 01:47 spi0.1 -> ../../../../devices/platform/bfin-spi.0/spi0.1
--w------- 1 root root 4096 Jan 1 01:47 unbind



Setup the master (SPI controller)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The SPI master device is registered as a platform device:
// stamp.c:

struct platform_device {
const char *name;
u32 id;
struct device dev;
u32 num_resources;
struct resource *resource;
};

//---------------------------------------------------------------
stamp.c:

static struct resource bfin_spi0_resource[] = {
[0] = {
.start = SPI0_REGBASE,
.end = SPI0_REGBASE + 0xFF,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = CH_SPI,
.end = CH_SPI,
.flags = IORESOURCE_IRQ,
}
};

/* SPI controller data */
static struct bfin5xx_spi_master bfin_spi0_info = {
.num_chipselect = 8,
.enable_dma = 1, /* master has the ability to do dma transfer */
.pin_req = {P_SPI0_SCK, P_SPI0_MISO, P_SPI0_MOSI, 0},
};

static struct platform_device bfin_spi0_device = {
.name = "bfin-spi",
.id = 0, /* Bus number */
.num_resources = ARRAY_SIZE(bfin_spi0_resource),
.resource = bfin_spi0_resource,
.dev = {
.platform_data = &bfin_spi0_info, /* Passed to driver */
},
};

static int __init stamp_init(void) {
platform_add_devices(stamp_devices, ARRAY_SIZE(stamp_devices));

}
//-------------------------------------------------------------------------

The driver for SPI master:

static struct platform_driver bfin5xx_spi_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
.suspend = bfin5xx_spi_suspend,
.resume = bfin5xx_spi_resume,
.remove = __devexit_p(bfin5xx_spi_remove),
}

/**
* platform_driver_probe - register driver for non-hotpluggable device
* @drv: platform driver structure
* @probe: the driver probe routine, probably from an __init section
*
* Use this instead of platform_driver_register() when you know the device
* is not hotpluggable and has already been registered, and you want to
* remove its run-once probe() infrastructure from memory after the driver
* has bound to the device.
*
* One typical use for this would be with drivers for controllers integrated
* into system-on-chip processors, where the controller devices have been
* configured as part of board setup.
*
* Returns zero if the driver registered and bound to a device, else returns
* a negative error code and with the driver not registered.
*/
static int __init bfin5xx_spi_init(void)
{
return platform_driver_probe(&bfin5xx_spi_driver, bfin5xx_spi_probe);
}

------------------------------------------------------------------------------------------
Bind spi slave devices and spi driver.

platform_probe()
--> bfin_spi_probe() // spi_bf5xx.c
--> spi_register_master() // spi.c
--> scan_board_info() // for all the devices in the table defined in board.c (stamp.c)
--> spi_new_device()
--> master->setup() // set up the new device HW
--> device_register()

-----------------------------------------------------------------
This driver defines bellow routine for spi master device
master->cleanup = cleanup;
master->setup = setup;
master->transfer = transfer;
------------------------------------------------------------------
Set up queues for transfer:
// spi_bf5xx.c
bfin_spi_probe()
---> init_queue()
---> tasklet_init(&drv_data->pump_transfers,
pump_transfers, (unsigned long)drv_data);
---> INIT_WORK(&drv_data->pump_messages, pump_messages);

root:/sys/devices/platform/bfin-spi.0> ls -lR
.:
lrwxrwxrwx 1 root root 0 Jan 1 01:48 bus -> ../../../bus/platform
lrwxrwxrwx 1 root root 0 Jan 1 01:48 driver -> ../../../bus/platform/drivers/bfin-spi
-r--r--r-- 1 root root 4096 Jan 1 01:48 modalias
drwxr-xr-x 2 root root 0 Jan 1 2007 power
drwxr-xr-x 3 root root 0 Jan 1 2007 spi0.1
lrwxrwxrwx 1 root root 0 Jan 1 01:48 spi_master:spi0 -> ../../../class/spi_master/spi0
lrwxrwxrwx 1 root root 0 Jan 1 01:48 subsystem -> ../../../bus/platform
-rw-r--r-- 1 root root 4096 Jan 1 01:48 uevent

./power:
-rw-r--r-- 1 root root 4096 Jan 1 01:49 wakeup

./spi0.1:
lrwxrwxrwx 1 root root 0 Jan 1 01:49 bus -> ../../../../bus/spi
lrwxrwxrwx 1 root root 0 Jan 1 01:49 driver -> ../../../../bus/spi/drivers/spidev
-r--r--r-- 1 root root 4096 Jan 1 01:49 modalias
drwxr-xr-x 2 root root 0 Jan 1 2007 power
lrwxrwxrwx 1 root root 0 Jan 1 01:49 spidev:spidev0.1 -> ../../../../class/spidev/spidev0.1
lrwxrwxrwx 1 root root 0 Jan 1 01:49 subsystem -> ../../../../bus/spi
-rw-r--r-- 1 root root 4096 Jan 1 01:49 uevent

./spi0.1/power:
-rw-r--r-- 1 root root 4096 Jan 1 01:49 wakeup



For a SPI slave driver:
~~~~~~~~~~~~~~~~~

* This represents the kind of device driver that uses SPI messages to
* interact with the hardware at the other end of a SPI link. It's called
* a "protocol" driver because it works through messages rather than talking
* directly to SPI hardware (which is what the underlying SPI controller
* driver does to pass those messages). These protocols are defined in the
* specification for the device(s) supported by the driver.
*
* As a rule, those device protocols represent the lowest level interface
* supported by a driver, and it will support upper level interfaces too.
* Examples of such upper levels include frameworks like MTD, networking,
* MMC, RTC, filesystem character device nodes, and hardware monitoring.

struct spi_driver {
int (*probe)(struct spi_device *spi);
int (*remove)(struct spi_device *spi);
void (*shutdown)(struct spi_device *spi);
int (*suspend)(struct spi_device *spi, pm_message_t mesg);
int (*resume)(struct spi_device *spi);
struct device_driver driver;
};

static struct spi_driver bfin_spi_adc_driver = {
.driver = {
.name = "bfin_spi_adc",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = bfin_spi_adc_probe,
.remove = __devexit_p(bfin_spi_adc_remove),
};


spi_register_driver(&bfin_spi_adc_driver);


struct bfin_spi_adc {
int opened;
unsigned short *buffer;
int hz;
int cont;
struct spi_device *spidev;
dma_addr_t dma_handle;
};

struct bfin_spi_adc spi_adc;


struct spi_device {
struct device dev;
struct spi_master *master;
u32 max_speed_hz;
u8 chip_select;
u8 mode;
#define SPI_CPHA 0x01 /* clock phase */
#define SPI_CPOL 0x02 /* clock polarity */
#define SPI_MODE_0 (0|0) /* (original MicroWire) */
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH 0x04 /* chipselect active high? */
#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
#define SPI_3WIRE 0x10 /* SI/SO signals shared */
#define SPI_LOOP 0x20 /* loopback mode */
u8 bits_per_word;
int irq;
void *controller_state;
void *controller_data;
const char *modalias;
};


root:/sys/bus/spi/drivers/spidev> ls -lR
.:
--w------- 1 root root 4096 Jan 1 01:47 bind
lrwxrwxrwx 1 root root 0 Jan 1 01:47 spi0.1 -> ../../../../devices/platform/bfin-spi.0/spi0.1
--w------- 1 root root 4096 Jan 1 01:47 unbind

root:/sys/devices/platform/bfin-spi.0> ls -lR
.:
lrwxrwxrwx 1 root root 0 Jan 1 01:48 bus -> ../../../bus/platform
lrwxrwxrwx 1 root root 0 Jan 1 01:48 driver -> ../../../bus/platform/drivers/bfin-spi
-r--r--r-- 1 root root 4096 Jan 1 01:48 modalias
drwxr-xr-x 2 root root 0 Jan 1 01:49 power
drwxr-xr-x 3 root root 0 Jan 1 01:49 spi0.1
lrwxrwxrwx 1 root root 0 Jan 1 01:48 spi_master:spi0 -> ../../../class/spi_master/spi0
lrwxrwxrwx 1 root root 0 Jan 1 01:48 subsystem -> ../../../bus/platform
-rw-r--r-- 1 root root 4096 Jan 1 01:48 uevent

./power:
-rw-r--r-- 1 root root 4096 Jan 1 01:49 wakeup

./spi0.1:
lrwxrwxrwx 1 root root 0 Jan 1 01:49 bus -> ../../../../bus/spi
lrwxrwxrwx 1 root root 0 Jan 1 01:49 driver -> ../../../../bus/spi/drivers/spidev
-r--r--r-- 1 root root 4096 Jan 1 01:49 modalias
drwxr-xr-x 2 root root 0 Jan 1 01:49 power
lrwxrwxrwx 1 root root 0 Jan 1 01:49 spidev:spidev0.1 -> ../../../../class/spidev/spidev0.1
lrwxrwxrwx 1 root root 0 Jan 1 01:49 subsystem -> ../../../../bus/spi
-rw-r--r-- 1 root root 4096 Jan 1 01:49 uevent

./spi0.1/power:
-rw-r--r-- 1 root root 4096 Jan 1 01:49 wakeup

-------------------------------------------------
Bind the SPI protocol driver to SPI device:

1. In driver init: calls spi_register_driver() --> driver_register()

2. when device_register() is called, the "Driver Core" will search the list of drivers,
and check whether the device "match" with a driver.

3. when driver_register() get called, the "Driver Core" will also scan existing devices



To send a message:
~~~~~~~~~~~~~~~~~

spi_sync() // spi.c
--> spi_async();
--> master->transfer(); // spi_bf5xx.c
--> queue_work(drv_data->workqueue, &drv_data->pump_messages);
--> pump_messages()
--> tasklet_schedule(&drv_data->pump_transfers) //activate the tasklet
--> pump_taskfer()
--> wait_for_completion(&done)


miscellaneous device
~~~~~~~~~~~~~~~~~~~~~
Misc (or miscellaneous) drivers are simple char drivers that share certain common characteristics. The kernel
abstracts these commonalities into an API (implemented in drivers/char/misc.c), and this simplifies the way
these drivers are initialized. All misc devices are assigned a major number of 10, but each can choose a single
minor number. So, if a char driver needs to drive multiple devices as in the CMOS example discussed earlier, it's
probably not a candidate for being a misc driver.



Platform devices
~~~~~~~~~~~~~~~~
Platform devices are devices that typically appear as autonomous
entities in the system. This includes legacy port-based devices and
host bridges to peripheral buses, and most controllers integrated
into system-on-chip platforms. What they usually have in common
is direct addressing from a CPU bus. Rarely, a platform_device will
be connected through a segment of some other kind of bus; but its
registers will still be directly addressable.

Platform devices are given a name, used in driver binding, and a
list of resources such as addresses and IRQs.


Wednesday, July 02, 2008

blackfin serial driver

 
/*
Adam: Blackfin Serial Driver

UART and RS232:

http://www.freebsd.org/doc/en/articles/serial-uart/index.html#UART

UART: Universal Asynchronous Receiver/Transmitter

Asynchronous transmission allows data to be transmitted without the sender having to send a clock signal to the receiver. Instead, the sender and receiver must agree on timing parameters in advance and special bits are added to each word which are used to synchronize the sending and receiving units.

When a word is given to the UART for Asynchronous transmissions, a bit called the "Start Bit" is added to the beginning of each word that is to be transmitted. The Start Bit is used to alert the receiver that a word of data is about to be sent, and to force the clock in the receiver into synchronization with the clock in the transmitter. These two clocks must be accurate enough to not have the frequency drift by more than 10% during the transmission of the remaining bits in the word. (This requirement was set in the days of mechanical teleprinters and is easily met by modern electronic equipment.)

After the Start Bit, the individual bits of the word of data are sent, with the Least Significant Bit (LSB) being sent first. Each bit in the transmission is transmitted for exactly the same amount of time as all of the other bits, and the receiver “looks” at the wire at approximately halfway through the period assigned to each bit to determine if the bit is a 1 or a 0. For example, if it takes two seconds to send each bit, the receiver will examine the signal to determine if it is a 1 or a 0 after one second has passed, then it will wait two seconds and then examine the value of the next bit, and so on.

The sender does not know when the receiver has “looked” at the value of the bit. The sender only knows when the clock says to begin transmitting the next bit of the word.

When the entire data word has been sent, the transmitter may add a Parity Bit that the transmitter generates. The Parity Bit may be used by the receiver to perform simple error checking. Then at least one Stop Bit is sent by the transmitter.

When the receiver has received all of the bits in the data word, it may check for the Parity Bits (both sender and receiver must agree on whether a Parity Bit is to be used), and then the receiver looks for a Stop Bit. If the Stop Bit does not appear when it is supposed to, the UART considers the entire word to be garbled and will report a Framing Error to the host processor when the data word is read. The usual cause of a Framing Error is that the sender and receiver clocks were not running at the same speed, or that the signal was interrupted.

Regardless of whether the data was received correctly or not, the UART automatically discards the Start, Parity and Stop bits. If the sender and receiver are configured identically, these bits are not passed to the host.

If another word is ready for transmission, the Start Bit for the new word can be sent as soon as the Stop Bit for the previous word has been sent.

Because asynchronous data is “self synchronizing”, if there is no data to transmit, the transmission line can be idle.

RS232: pins, voltage, etc.

*/

/*
Adam: What do we have in this driver:

1. Implements functions for linux "serial-core" interface, this include:
static struct uart_ops bfin_serial_pops = {
.tx_empty = bfin_serial_tx_empty,
.set_mctrl = bfin_serial_set_mctrl,
.get_mctrl = bfin_serial_get_mctrl,
.stop_tx = bfin_serial_stop_tx,
.start_tx = bfin_serial_start_tx,
.stop_rx = bfin_serial_stop_rx,
.enable_ms = bfin_serial_enable_ms,
.break_ctl = bfin_serial_break_ctl,
.startup = bfin_serial_startup,
.shutdown = bfin_serial_shutdown,
.set_termios = bfin_serial_set_termios,
.type = bfin_serial_type,
.release_port = bfin_serial_release_port,
.request_port = bfin_serial_request_port,
.config_port = bfin_serial_config_port,
.verify_port = bfin_serial_verify_port,
};

For definition of these functions, see: Document/serial/driver

This makes up a serial driver:

/dev/ttyBFx -> tty-core -> (line discipline if any) -> serial-core -> bfin_driver

2. Implements functions for serial console, used by printk():

static struct console bfin_serial_console = {
.name = BFIN_SERIAL_NAME,
.write = bfin_serial_console_write,
.device = uart_console_device,
.setup = bfin_serial_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &bfin_serial_reg,
};

This would define a console driver.

System Console: The system console is the device which receives all kernel messages
and warnings and which allows logins in single user mode.

Virtual Terminals: support for terminal devices with display and keyboard devices.
These are called "virtual" because you can run several virtual
terminals (also called virtual consoles) on one physical terminal.

1) /dev/console - system console implemented in drivers/char/vt.c
2) /dev/tty0 - virtual terminal - drivers/char/vt.c
Virtual Terminals (CONFIG_VT)
- CONFIG_VT_CONSOLE: System console on VT
VT requires a HW specific driver for output:
- CONFIG_VGA_CONSOLE
- CONFIG_FRAMEBUFFER_CONSOLE
- CONFIG_DUMMY_CONSOLE

Console drivers lie in two tiers: a top level constituting drivers such as the virtual terminal driver,
the printer console driver,, and bottom-level drivers that are responsible for advanced operations.
Consequently, there are two main interface definition structures
used by console drivers. Top-level console drivers revolve around struct console, which defines basic
operations such as setup() and write(). Bottom-level drivers center on struct consw that specifies advanced
operations such as setting cursor properties, console switching, blanking, resizing, and setting palette
information. These structures are defined in include/linux/console.h.

Bottom Console driver:
- CONFIG_VGA_CONSOLE
- CONFIG_FRAMEBUFFER_CONSOLE
- CONFIG_DUMMY_CONSOLE
e.g, the fb_con.c:
static const struct consw fb_con = {
.owner = THIS_MODULE,
.con_startup = fbcon_startup,
.con_init = fbcon_init,
.con_deinit = fbcon_deinit,
.con_clear = fbcon_clear,
.con_putc = fbcon_putc,
.con_putcs = fbcon_putcs,
.con_cursor = fbcon_cursor,
.con_scroll = fbcon_scroll,
.con_bmove = fbcon_bmove,
.con_switch = fbcon_switch,
.con_blank = fbcon_blank,
.con_font_set = fbcon_set_font,
.con_font_get = fbcon_get_font,
.con_font_default = fbcon_set_def_font,
.con_font_copy = fbcon_copy_font,
.con_set_palette = fbcon_set_palette,
.con_scrolldelta = fbcon_scrolldelta,
.con_set_origin = fbcon_set_origin,
.con_invert_region = fbcon_invert_region,
.con_screen_pos = fbcon_screen_pos,
.con_getxy = fbcon_getxy,
.con_resize = fbcon_resize,
};

Top console driver (printer driver) - devices that want to capture printk.
CONFIG_SERIAL_*_CONSOLE
CONFIG_LP_CONSOLE
CONFIG_NETCONSOLE

Early printk is a console driver.

3. Implements bfin earlyprintk functions:
static struct __init console bfin_early_serial_console = {
.name = "early_BFuart",
.write = early_serial_write,
.device = uart_console_device,
.flags = CON_PRINTBUFFER,
.setup = bfin_serial_console_setup,
.index = -1,
.data = &bfin_serial_reg,
}

4. Each file (*.c) may implements several drivers:

*/


/*
* Blackfin On-Chip Serial Driver
*
* Copyright 2006-2007 Analog Devices Inc.
*
* Enter bugs at http://blackfin.uclinux.org/
*
* Licensed under the GPL-2 or later.
*/

#if defined(CONFIG_SERIAL_BFIN_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif

#include
#include
#include
#include
#include
#include
#include
#include
#include

#ifdef CONFIG_KGDB_UART
#include
#include
#endif

#include
#include

#ifdef CONFIG_SERIAL_BFIN_DMA
#include
#include
#include
#include
#endif

/* UART name and device definitions */

#define BFIN_SERIAL_NAME "ttyBF"
#define BFIN_SERIAL_MAJOR 204
#define BFIN_SERIAL_MINOR 64

/*
* Setup for console. Argument comes from the menuconfig
*/
#define DMA_RX_XCOUNT 512
#define DMA_RX_YCOUNT (PAGE_SIZE / DMA_RX_XCOUNT)

#define DMA_RX_FLUSH_JIFFIES (HZ / 50)
#define CTS_CHECK_JIFFIES (HZ / 50)

#ifdef CONFIG_SERIAL_BFIN_DMA
static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart);
#else
static void bfin_serial_tx_chars(struct bfin_serial_port *uart);
#endif

static void bfin_serial_mctrl_check(struct bfin_serial_port *uart);

/*
* interrupts are disabled on entry
*/
static void bfin_serial_stop_tx(struct uart_port *port)
{
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
struct circ_buf *xmit = &uart->port.info->xmit;
#if !defined(CONFIG_BF54x) && !defined(CONFIG_SERIAL_BFIN_DMA)
unsigned short ier;
#endif

#ifdef CONFIG_SERIAL_BFIN_DMA
disable_dma(uart->tx_dma_channel);
xmit->tail = (xmit->tail + uart->tx_count) & (UART_XMIT_SIZE - 1);
uart->port.icount.tx += uart->tx_count;
uart->tx_count = 0;
uart->tx_done = 1;
#else
#ifdef CONFIG_BF54x
/* Clear TFI bit */
UART_PUT_LSR(uart, TFI);
UART_CLEAR_IER(uart, ETBEI);
#else
ier = UART_GET_IER(uart);
ier &= ~ETBEI;
UART_PUT_IER(uart, ier);
#endif
#endif
}

/*
* port is locked and interrupts are disabled
*/
static void bfin_serial_start_tx(struct uart_port *port)
{
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;

#ifdef CONFIG_SERIAL_BFIN_DMA
if (uart->tx_done)
bfin_serial_dma_tx_chars(uart);
#else
#ifdef CONFIG_BF54x
UART_SET_IER(uart, ETBEI);
#else
unsigned short ier;
ier = UART_GET_IER(uart);
ier |= ETBEI;
UART_PUT_IER(uart, ier);
#endif
bfin_serial_tx_chars(uart);
#endif
}

/*
* Interrupts are enabled
*/
static void bfin_serial_stop_rx(struct uart_port *port)
{
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
#ifdef CONFIG_KGDB_UART
if (uart->port.line != CONFIG_KGDB_UART_PORT) {
#endif
#ifdef CONFIG_BF54x
UART_CLEAR_IER(uart, ERBFI);
#else
unsigned short ier;

ier = UART_GET_IER(uart);
ier &= ~ERBFI;
UART_PUT_IER(uart, ier);
#endif
#ifdef CONFIG_KGDB_UART
}
#endif
}

/*
* Set the modem control timer to fire immediately.
*/
static void bfin_serial_enable_ms(struct uart_port *port)
{
}

#ifdef CONFIG_KGDB_UART
static int kgdb_entry_state;

void kgdb_put_debug_char(int chr)
{
struct bfin_serial_port *uart;

if (CONFIG_KGDB_UART_PORT<0 || CONFIG_KGDB_UART_PORT>=NR_PORTS)
uart = &bfin_serial_ports[0];
else
uart = &bfin_serial_ports[CONFIG_KGDB_UART_PORT];

while (!(UART_GET_LSR(uart) & THRE)) {
SSYNC();
}

#ifndef CONFIG_BF54x
UART_PUT_LCR(uart, UART_GET_LCR(uart)&(~DLAB));
SSYNC();
#endif
UART_PUT_CHAR(uart, (unsigned char)chr);
SSYNC();
}

int kgdb_get_debug_char(void)
{
struct bfin_serial_port *uart;
unsigned char chr;

if (CONFIG_KGDB_UART_PORT<0 || CONFIG_KGDB_UART_PORT>=NR_PORTS)
uart = &bfin_serial_ports[0];
else
uart = &bfin_serial_ports[CONFIG_KGDB_UART_PORT];

while(!(UART_GET_LSR(uart) & DR)) {
SSYNC();
}
#ifndef CONFIG_BF54x
UART_PUT_LCR(uart, UART_GET_LCR(uart)&(~DLAB));
SSYNC();
#endif
chr = UART_GET_CHAR(uart);
SSYNC();

return chr;
}
#endif

#if ANOMALY_05000230 && defined(CONFIG_SERIAL_BFIN_PIO)
# define UART_GET_ANOMALY_THRESHOLD(uart) ((uart)->anomaly_threshold)
# define UART_SET_ANOMALY_THRESHOLD(uart, v) ((uart)->anomaly_threshold = (v))
#else
# define UART_GET_ANOMALY_THRESHOLD(uart) 0
# define UART_SET_ANOMALY_THRESHOLD(uart, v)
#endif

#ifdef CONFIG_SERIAL_BFIN_PIO
static void bfin_serial_rx_chars(struct bfin_serial_port *uart)
{
struct tty_struct *tty = uart->port.info->tty;
unsigned int status, ch, flg;
static struct timeval anomaly_start = { .tv_sec = 0 };
#ifdef CONFIG_KGDB_UART
struct pt_regs *regs = get_irq_regs();
#endif

status = UART_GET_LSR(uart);
UART_CLEAR_LSR(uart);

ch = UART_GET_CHAR(uart);
uart->port.icount.rx++;

#ifdef CONFIG_KGDB_UART
if (uart->port.line == CONFIG_KGDB_UART_PORT) {
if (uart->port.cons->index == CONFIG_KGDB_UART_PORT && ch == 0x1) { /* Ctrl + A */
kgdb_breakkey_pressed(regs);
return;
} else if (kgdb_entry_state == 0 && ch == '$') {/* connection from KGDB */
kgdb_entry_state = 1;
} else if (kgdb_entry_state == 1 && ch == 'q') {
kgdb_entry_state = 0;
kgdb_breakkey_pressed(regs);
return;
} else if (ch == 0x3) {/* Ctrl + C */
kgdb_entry_state = 0;
kgdb_breakkey_pressed(regs);
return;
} else {
kgdb_entry_state = 0;
}
}
#endif

if (ANOMALY_05000230) {
/* The BF533 (and BF561) family of processors have a nice anomaly
* where they continuously generate characters for a "single" break.
* We have to basically ignore this flood until the "next" valid
* character comes across. Due to the nature of the flood, it is
* not possible to reliably catch bytes that are sent too quickly
* after this break. So application code talking to the Blackfin
* which sends a break signal must allow at least 1.5 character
* times after the end of the break for things to stabilize. This
* timeout was picked as it must absolutely be larger than 1
* character time +/- some percent. So 1.5 sounds good. All other
* Blackfin families operate properly. Woo.
* Note: While Anomaly 05000230 does not directly address this,
* the changes that went in for it also fixed this issue.
* That anomaly was fixed in 0.5+ silicon. I like bunnies.
*/
if (anomaly_start.tv_sec) {
struct timeval curr;
suseconds_t usecs;

if ((~ch & (~ch + 1)) & 0xff)
goto known_good_char;

do_gettimeofday(&curr);
if (curr.tv_sec - anomaly_start.tv_sec > 1)
goto known_good_char;

usecs = 0;
if (curr.tv_sec != anomaly_start.tv_sec)
usecs += USEC_PER_SEC;
usecs += curr.tv_usec - anomaly_start.tv_usec;

if (usecs > UART_GET_ANOMALY_THRESHOLD(uart))
goto known_good_char;

if (ch)
anomaly_start.tv_sec = 0;
else
anomaly_start = curr;

return;

known_good_char:
anomaly_start.tv_sec = 0;
}
}

if (status & BI) {
if (ANOMALY_05000230)
if (bfin_revid() < 5)
do_gettimeofday(&anomaly_start);
uart->port.icount.brk++;
if (uart_handle_break(&uart->port))
goto ignore_char;
status &= ~(PE | FE);
}
if (status & PE)
uart->port.icount.parity++;
if (status & OE)
uart->port.icount.overrun++;
if (status & FE)
uart->port.icount.frame++;

status &= uart->port.read_status_mask;

if (status & BI)
flg = TTY_BREAK;
else if (status & PE)
flg = TTY_PARITY;
else if (status & FE)
flg = TTY_FRAME;
else
flg = TTY_NORMAL;

if (uart_handle_sysrq_char(&uart->port, ch))
goto ignore_char;

uart_insert_char(&uart->port, status, OE, ch, flg);

ignore_char:
tty_flip_buffer_push(tty);
}

static void bfin_serial_tx_chars(struct bfin_serial_port *uart)
{
struct circ_buf *xmit = &uart->port.info->xmit;

/*
* Check the modem control lines before
* transmitting anything.
*/
bfin_serial_mctrl_check(uart);

if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) {
bfin_serial_stop_tx(&uart->port);
return;
}

if (uart->port.x_char) {
UART_PUT_CHAR(uart, uart->port.x_char);
uart->port.icount.tx++;
uart->port.x_char = 0;
}

while ((UART_GET_LSR(uart) & THRE) && xmit->tail != xmit->head) {
UART_PUT_CHAR(uart, xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
uart->port.icount.tx++;
SSYNC();
}

if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&uart->port);

if (uart_circ_empty(xmit))
bfin_serial_stop_tx(&uart->port);
}

static irqreturn_t bfin_serial_rx_int(int irq, void *dev_id)
{
struct bfin_serial_port *uart = dev_id;

spin_lock(&uart->port.lock);
while (UART_GET_LSR(uart) & DR)
bfin_serial_rx_chars(uart);
spin_unlock(&uart->port.lock);

return IRQ_HANDLED;
}

static irqreturn_t bfin_serial_tx_int(int irq, void *dev_id)
{
struct bfin_serial_port *uart = dev_id;

spin_lock(&uart->port.lock);
if (UART_GET_LSR(uart) & THRE)
bfin_serial_tx_chars(uart);
spin_unlock(&uart->port.lock);

return IRQ_HANDLED;
}
#endif

#ifdef CONFIG_SERIAL_BFIN_DMA
static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart)
{
struct circ_buf *xmit = &uart->port.info->xmit;
unsigned short ier;

uart->tx_done = 0;

/*
* Check the modem control lines before
* transmitting anything.
*/
bfin_serial_mctrl_check(uart);

if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) {
uart->tx_count = 0;
uart->tx_done = 1;
return;
}

if (uart->port.x_char) {
UART_PUT_CHAR(uart, uart->port.x_char);
uart->port.icount.tx++;
uart->port.x_char = 0;
}

uart->tx_count = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE);
if (uart->tx_count > (UART_XMIT_SIZE - xmit->tail))
uart->tx_count = UART_XMIT_SIZE - xmit->tail;
blackfin_dcache_flush_range((unsigned long)(xmit->buf+xmit->tail),
(unsigned long)(xmit->buf+xmit->tail+uart->tx_count));
set_dma_config(uart->tx_dma_channel,
set_bfin_dma_config(DIR_READ, DMA_FLOW_STOP,
INTR_ON_BUF,
DIMENSION_LINEAR,
DATA_SIZE_8,
DMA_SYNC_RESTART));
set_dma_start_addr(uart->tx_dma_channel, (unsigned long)(xmit->buf+xmit->tail));
set_dma_x_count(uart->tx_dma_channel, uart->tx_count);
set_dma_x_modify(uart->tx_dma_channel, 1);
enable_dma(uart->tx_dma_channel);

#ifdef CONFIG_BF54x
UART_SET_IER(uart, ETBEI);
#else
ier = UART_GET_IER(uart);
ier |= ETBEI;
UART_PUT_IER(uart, ier);
#endif
}

static void bfin_serial_dma_rx_chars(struct bfin_serial_port *uart)
{
struct tty_struct *tty = uart->port.info->tty;
int i, flg, status;

status = UART_GET_LSR(uart);
UART_CLEAR_LSR(uart);

uart->port.icount.rx +=
CIRC_CNT(uart->rx_dma_buf.head, uart->rx_dma_buf.tail,
UART_XMIT_SIZE);

if (status & BI) {
uart->port.icount.brk++;
if (uart_handle_break(&uart->port))
goto dma_ignore_char;
status &= ~(PE | FE);
}
if (status & PE)
uart->port.icount.parity++;
if (status & OE)
uart->port.icount.overrun++;
if (status & FE)
uart->port.icount.frame++;

status &= uart->port.read_status_mask;

if (status & BI)
flg = TTY_BREAK;
else if (status & PE)
flg = TTY_PARITY;
else if (status & FE)
flg = TTY_FRAME;
else
flg = TTY_NORMAL;

for (i = uart->rx_dma_buf.tail; i != uart->rx_dma_buf.head; i++) {
if (i >= UART_XMIT_SIZE)
i = 0;
if (!uart_handle_sysrq_char(&uart->port, uart->rx_dma_buf.buf[i]))
uart_insert_char(&uart->port, status, OE,
uart->rx_dma_buf.buf[i], flg);
}

dma_ignore_char:
tty_flip_buffer_push(tty);
}

void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart)
{
int x_pos, pos;

uart->rx_dma_nrows = get_dma_curr_ycount(uart->rx_dma_channel);
x_pos = get_dma_curr_xcount(uart->rx_dma_channel);
uart->rx_dma_nrows = DMA_RX_YCOUNT - uart->rx_dma_nrows;
if (uart->rx_dma_nrows == DMA_RX_YCOUNT)
uart->rx_dma_nrows = 0;
x_pos = DMA_RX_XCOUNT - x_pos;
if (x_pos == DMA_RX_XCOUNT)
x_pos = 0;

pos = uart->rx_dma_nrows * DMA_RX_XCOUNT + x_pos;
if (pos != uart->rx_dma_buf.tail) {
uart->rx_dma_buf.head = pos;
bfin_serial_dma_rx_chars(uart);
uart->rx_dma_buf.tail = uart->rx_dma_buf.head;
}

mod_timer(&(uart->rx_dma_timer), jiffies + DMA_RX_FLUSH_JIFFIES);
}

static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id)
{
struct bfin_serial_port *uart = dev_id;
struct circ_buf *xmit = &uart->port.info->xmit;
unsigned short ier;

spin_lock(&uart->port.lock);
if (!(get_dma_curr_irqstat(uart->tx_dma_channel)&DMA_RUN)) {
disable_dma(uart->tx_dma_channel);
clear_dma_irqstat(uart->tx_dma_channel);
#ifdef CONFIG_BF54x
UART_CLEAR_IER(uart, ETBEI);
#else
ier = UART_GET_IER(uart);
ier &= ~ETBEI;
UART_PUT_IER(uart, ier);
#endif
xmit->tail = (xmit->tail + uart->tx_count) & (UART_XMIT_SIZE - 1);
uart->port.icount.tx += uart->tx_count;

if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&uart->port);

bfin_serial_dma_tx_chars(uart);
}

spin_unlock(&uart->port.lock);
return IRQ_HANDLED;
}

static irqreturn_t bfin_serial_dma_rx_int(int irq, void *dev_id)
{
struct bfin_serial_port *uart = dev_id;
unsigned short irqstat;

spin_lock(&uart->port.lock);
irqstat = get_dma_curr_irqstat(uart->rx_dma_channel);
clear_dma_irqstat(uart->rx_dma_channel);
spin_unlock(&uart->port.lock);

mod_timer(&(uart->rx_dma_timer), jiffies);

return IRQ_HANDLED;
}
#endif

/*
* Return TIOCSER_TEMT when transmitter is not busy.
*/
static unsigned int bfin_serial_tx_empty(struct uart_port *port)
{
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
unsigned short lsr;

lsr = UART_GET_LSR(uart);
if (lsr & TEMT)
return TIOCSER_TEMT;
else
return 0;
}

static unsigned int bfin_serial_get_mctrl(struct uart_port *port)
{
#ifdef CONFIG_SERIAL_BFIN_CTSRTS
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
if (uart->cts_pin < 0)
return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;

# ifdef BF54x
if (UART_GET_MSR(uart) & CTS)
# else
if (gpio_get_value(uart->cts_pin))
# endif
return TIOCM_DSR | TIOCM_CAR;
else
#endif
return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
}

static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
#ifdef CONFIG_SERIAL_BFIN_CTSRTS
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
if (uart->rts_pin < 0)
return;

if (mctrl & TIOCM_RTS)
# ifdef BF54x
UART_PUT_MCR(uart, UART_GET_MCR(uart) & ~MRTS);
# else
gpio_set_value(uart->rts_pin, 0);
# endif
else
# ifdef BF54x
UART_PUT_MCR(uart, UART_GET_MCR(uart) | MRTS);
# else
gpio_set_value(uart->rts_pin, 1);
# endif
#endif
}

/*
* Handle any change of modem status signal since we were last called.
*/
static void bfin_serial_mctrl_check(struct bfin_serial_port *uart)
{
#ifdef CONFIG_SERIAL_BFIN_CTSRTS
unsigned int status;
struct uart_info *info = uart->port.info;
struct tty_struct *tty = info->tty;

status = bfin_serial_get_mctrl(&uart->port);
uart_handle_cts_change(&uart->port, status & TIOCM_CTS);
if (!(status & TIOCM_CTS)) {
tty->hw_stopped = 1;
uart->cts_timer.data = (unsigned long)(uart);
uart->cts_timer.function = (void *)bfin_serial_mctrl_check;
uart->cts_timer.expires = jiffies + CTS_CHECK_JIFFIES;
add_timer(&(uart->cts_timer));
} else {
tty->hw_stopped = 0;
}
#endif
}

/*
* Interrupts are always disabled.
*/
static void bfin_serial_break_ctl(struct uart_port *port, int break_state)
{
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
u16 lcr = UART_GET_LCR(uart);
if (break_state)
lcr |= SB;
else
lcr &= ~SB;
UART_PUT_LCR(uart, lcr);
SSYNC();
}

static int bfin_serial_startup(struct uart_port *port)
{
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;

#ifdef CONFIG_SERIAL_BFIN_DMA
dma_addr_t dma_handle;

if (request_dma(uart->rx_dma_channel, "BFIN_UART_RX") < 0) {
printk(KERN_NOTICE "Unable to attach Blackfin UART RX DMA channel\n");
return -EBUSY;
}

if (request_dma(uart->tx_dma_channel, "BFIN_UART_TX") < 0) {
printk(KERN_NOTICE "Unable to attach Blackfin UART TX DMA channel\n");
free_dma(uart->rx_dma_channel);
return -EBUSY;
}

set_dma_callback(uart->rx_dma_channel, bfin_serial_dma_rx_int, uart);
set_dma_callback(uart->tx_dma_channel, bfin_serial_dma_tx_int, uart);

uart->rx_dma_buf.buf = (unsigned char *)dma_alloc_coherent(NULL, PAGE_SIZE, &dma_handle, GFP_DMA);
uart->rx_dma_buf.head = 0;
uart->rx_dma_buf.tail = 0;
uart->rx_dma_nrows = 0;

set_dma_config(uart->rx_dma_channel,
set_bfin_dma_config(DIR_WRITE, DMA_FLOW_AUTO,
INTR_ON_ROW, DIMENSION_2D,
DATA_SIZE_8,
DMA_SYNC_RESTART));
set_dma_x_count(uart->rx_dma_channel, DMA_RX_XCOUNT);
set_dma_x_modify(uart->rx_dma_channel, 1);
set_dma_y_count(uart->rx_dma_channel, DMA_RX_YCOUNT);
set_dma_y_modify(uart->rx_dma_channel, 1);
set_dma_start_addr(uart->rx_dma_channel, (unsigned long)uart->rx_dma_buf.buf);
enable_dma(uart->rx_dma_channel);

uart->rx_dma_timer.data = (unsigned long)(uart);
uart->rx_dma_timer.function = (void *)bfin_serial_rx_dma_timeout;
uart->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES;
add_timer(&(uart->rx_dma_timer));
#else
if (request_irq(uart->port.irq, bfin_serial_rx_int, IRQF_DISABLED,
"BFIN_UART_RX", uart)) {
# ifdef CONFIG_KGDB_UART
if (uart->port.line != CONFIG_KGDB_UART_PORT) {
# endif
printk(KERN_NOTICE "Unable to attach BlackFin UART RX interrupt\n");
return -EBUSY;
# ifdef CONFIG_KGDB_UART
}
# endif
}


if (request_irq
(uart->port.irq+1, bfin_serial_tx_int, IRQF_DISABLED,
"BFIN_UART_TX", uart)) {
printk(KERN_NOTICE "Unable to attach BlackFin UART TX interrupt\n");
free_irq(uart->port.irq, uart);
return -EBUSY;
}
#endif
#ifdef CONFIG_BF54x
UART_SET_IER(uart, ERBFI);
#else
UART_PUT_IER(uart, UART_GET_IER(uart) | ERBFI);
#endif
return 0;
}

static void bfin_serial_shutdown(struct uart_port *port)
{
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;

#ifdef CONFIG_SERIAL_BFIN_DMA
disable_dma(uart->tx_dma_channel);
free_dma(uart->tx_dma_channel);
disable_dma(uart->rx_dma_channel);
free_dma(uart->rx_dma_channel);
del_timer(&(uart->rx_dma_timer));
dma_free_coherent(NULL, PAGE_SIZE, uart->rx_dma_buf.buf, 0);
#else
#ifdef CONFIG_KGDB_UART
if (uart->port.line != CONFIG_KGDB_UART_PORT)
#endif
free_irq(uart->port.irq, uart);
free_irq(uart->port.irq+1, uart);
#endif
}

/*
Adam: Document/serial/driver:

set_termios(port,termios,oldtermios)
Change the port parameters, including word length, parity, stop
bits. Update read_status_mask and ignore_status_mask to indicate
the types of events we are interested in receiving. Relevant
termios->c_cflag bits are:
CSIZE - word size
CSTOPB - 2 stop bits
PARENB - parity enable
PARODD - odd parity (when PARENB is in force)
CREAD - enable reception of characters (if not set,
still receive characters from the port, but
throw them away.
CRTSCTS - if set, enable CTS status change reporting
CLOCAL - if not set, enable modem status change
reporting.
Relevant termios->c_iflag bits are:
INPCK - enable frame and parity error events to be
passed to the TTY layer.
BRKINT
PARMRK - both of these enable break events to be
passed to the TTY layer.

IGNPAR - ignore parity and framing errors
IGNBRK - ignore break errors, If IGNPAR is also
set, ignore overrun errors as well.

*/

static void
bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
unsigned long flags;
unsigned int baud, quot;
unsigned short val, ier, lsr, lcr = 0;

switch (termios->c_cflag & CSIZE) {
case CS8:
lcr = WLS(8);
break;
case CS7:
lcr = WLS(7);
break;
case CS6:
lcr = WLS(6);
break;
case CS5:
lcr = WLS(5);
break;
default:
printk(KERN_ERR "%s: word lengh not supported\n",
__FUNCTION__);
}

if (termios->c_cflag & CSTOPB)
lcr |= STB;
if (termios->c_cflag & PARENB)
lcr |= PEN;
if (!(termios->c_cflag & PARODD))
lcr |= EPS;
if (termios->c_cflag & CMSPAR)
lcr |= STP;

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;
}

baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
quot = uart_get_divisor(port, baud);
spin_lock_irqsave(&uart->port.lock, flags);

UART_SET_ANOMALY_THRESHOLD(uart, USEC_PER_SEC / baud * 15);

/* Disable UART */
ier = UART_GET_IER(uart);
#ifdef CONFIG_BF54x
UART_CLEAR_IER(uart, 0xF);
#else
UART_PUT_IER(uart, 0);
#endif

#ifndef CONFIG_BF54x
/* Set DLAB in LCR to Access DLL and DLH */
val = UART_GET_LCR(uart);
val |= DLAB;
UART_PUT_LCR(uart, val);
SSYNC();
#endif

UART_PUT_DLL(uart, quot & 0xFF);
SSYNC();
UART_PUT_DLH(uart, (quot >> 8) & 0xFF);
SSYNC();

#ifndef CONFIG_BF54x
/* Clear DLAB in LCR to Access THR RBR IER */
val = UART_GET_LCR(uart);
val &= ~DLAB;
UART_PUT_LCR(uart, val);
SSYNC();
#endif

UART_PUT_LCR(uart, lcr);

/* Enable UART */
#ifdef CONFIG_BF54x
UART_SET_IER(uart, ier);
#else
UART_PUT_IER(uart, ier);
#endif

val = UART_GET_GCTL(uart);
val |= UCEN;
UART_PUT_GCTL(uart, val);

spin_unlock_irqrestore(&uart->port.lock, flags);
}

static const char *bfin_serial_type(struct uart_port *port)
{
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;

return uart->port.type == PORT_BFIN ? "BFIN-UART" : NULL;
}

/*
* Release the memory region(s) being used by 'port'.
*/
static void bfin_serial_release_port(struct uart_port *port)
{
}

/*
* Request the memory region(s) being used by 'port'.
*/
static int bfin_serial_request_port(struct uart_port *port)
{
return 0;
}

/*
* Configure/autoconfigure the port.
*/
static void bfin_serial_config_port(struct uart_port *port, int flags)
{
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;

if (flags & UART_CONFIG_TYPE &&
bfin_serial_request_port(&uart->port) == 0)
uart->port.type = PORT_BFIN;
}

/*
* Verify the new serial_struct (for TIOCSSERIAL).
* The only change we allow are to the flags and type, and
* even then only between PORT_BFIN and PORT_UNKNOWN
*/
static int
bfin_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
{
return 0;
}

static struct uart_ops bfin_serial_pops = {
.tx_empty = bfin_serial_tx_empty,
.set_mctrl = bfin_serial_set_mctrl,
.get_mctrl = bfin_serial_get_mctrl,
.stop_tx = bfin_serial_stop_tx,
.start_tx = bfin_serial_start_tx,
.stop_rx = bfin_serial_stop_rx,
.enable_ms = bfin_serial_enable_ms,
.break_ctl = bfin_serial_break_ctl,
.startup = bfin_serial_startup,
.shutdown = bfin_serial_shutdown,
.set_termios = bfin_serial_set_termios,
.type = bfin_serial_type,
.release_port = bfin_serial_release_port,
.request_port = bfin_serial_request_port,
.config_port = bfin_serial_config_port,
.verify_port = bfin_serial_verify_port,
};

static void __init bfin_serial_init_ports(void)
{
static int first = 1;
int i;

if (!first)
return;
first = 0;

for (i = 0; i < nr_ports; i++) {
bfin_serial_ports[i].port.uartclk = get_sclk();
bfin_serial_ports[i].port.ops = &bfin_serial_pops;
bfin_serial_ports[i].port.line = i;
bfin_serial_ports[i].port.iotype = UPIO_MEM;
bfin_serial_ports[i].port.membase =
(void __iomem *)bfin_serial_resource[i].uart_base_addr;
bfin_serial_ports[i].port.mapbase =
bfin_serial_resource[i].uart_base_addr;
bfin_serial_ports[i].port.irq =
bfin_serial_resource[i].uart_irq;
bfin_serial_ports[i].port.flags = UPF_BOOT_AUTOCONF;
#ifdef CONFIG_SERIAL_BFIN_DMA
bfin_serial_ports[i].tx_done = 1;
bfin_serial_ports[i].tx_count = 0;
bfin_serial_ports[i].tx_dma_channel =
bfin_serial_resource[i].uart_tx_dma_channel;
bfin_serial_ports[i].rx_dma_channel =
bfin_serial_resource[i].uart_rx_dma_channel;
init_timer(&(bfin_serial_ports[i].rx_dma_timer));
#endif
#ifdef CONFIG_SERIAL_BFIN_CTSRTS
init_timer(&(bfin_serial_ports[i].cts_timer));
bfin_serial_ports[i].cts_pin =
bfin_serial_resource[i].uart_cts_pin;
bfin_serial_ports[i].rts_pin =
bfin_serial_resource[i].uart_rts_pin;
#endif
bfin_serial_hw_init(&bfin_serial_ports[i]);
}

}

#ifdef CONFIG_SERIAL_BFIN_CONSOLE
/*
* If the port was already initialised (eg, by a boot loader),
* try to determine the current setup.
*/
static void __init
bfin_serial_console_get_options(struct bfin_serial_port *uart, int *baud,
int *parity, int *bits)
{
unsigned short status;

status = UART_GET_IER(uart) & (ERBFI | ETBEI);
if (status == (ERBFI | ETBEI)) {
/* ok, the port was enabled */
unsigned short lcr, val;
unsigned short dlh, dll;

lcr = UART_GET_LCR(uart);

*parity = 'n';
if (lcr & PEN) {
if (lcr & EPS)
*parity = 'e';
else
*parity = 'o';
}
switch (lcr & 0x03) {
case 0: *bits = 5; break;
case 1: *bits = 6; break;
case 2: *bits = 7; break;
case 3: *bits = 8; break;
}
#ifndef CONFIG_BF54x
/* Set DLAB in LCR to Access DLL and DLH */
val = UART_GET_LCR(uart);
val |= DLAB;
UART_PUT_LCR(uart, val);
#endif

dll = UART_GET_DLL(uart);
dlh = UART_GET_DLH(uart);

#ifndef CONFIG_BF54x
/* Clear DLAB in LCR to Access THR RBR IER */
val = UART_GET_LCR(uart);
val &= ~DLAB;
UART_PUT_LCR(uart, val);
#endif

*baud = get_sclk() / (16*(dll | dlh << 8));
}
pr_debug("%s:baud = %d, parity = %c, bits= %d\n", __FUNCTION__, *baud, *parity, *bits);
}
#endif

#if defined(CONFIG_SERIAL_BFIN_CONSOLE) || defined(CONFIG_EARLY_PRINTK)
static struct uart_driver bfin_serial_reg;

static int __init
bfin_serial_console_setup(struct console *co, char *options)
{
struct bfin_serial_port *uart;
# ifdef CONFIG_SERIAL_BFIN_CONSOLE
int baud = 57600;
int bits = 8;
int parity = 'n';
# ifdef CONFIG_SERIAL_BFIN_CTSRTS
int flow = 'r';
# else
int flow = 'n';
# endif
# endif

/*
* Check whether an invalid uart number has been specified, and
* if so, search for the first available port that does have
* console support.
*/
if (co->index == -1 || co->index >= nr_ports)
co->index = 0;
uart = &bfin_serial_ports[co->index];

# ifdef CONFIG_SERIAL_BFIN_CONSOLE
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
else
bfin_serial_console_get_options(uart, &baud, &parity, &bits);

return uart_set_options(&uart->port, co, baud, parity, bits, flow);
# else
return 0;
# endif
}
#endif /* defined (CONFIG_SERIAL_BFIN_CONSOLE) ||
defined (CONFIG_EARLY_PRINTK) */

#ifdef CONFIG_SERIAL_BFIN_CONSOLE
static void bfin_serial_console_putchar(struct uart_port *port, int ch)
{
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
while (!(UART_GET_LSR(uart) & THRE))
barrier();
UART_PUT_CHAR(uart, ch);
SSYNC();
}

/*
* Interrupts are disabled on entering
*/
static void
bfin_serial_console_write(struct console *co, const char *s, unsigned int count)
{
struct bfin_serial_port *uart = &bfin_serial_ports[co->index];
int flags = 0;

spin_lock_irqsave(&uart->port.lock, flags);
uart_console_write(&uart->port, s, count, bfin_serial_console_putchar);
spin_unlock_irqrestore(&uart->port.lock, flags);

}

static struct console bfin_serial_console = {
.name = BFIN_SERIAL_NAME,
.write = bfin_serial_console_write,
.device = uart_console_device,
.setup = bfin_serial_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &bfin_serial_reg,
};

static int __init bfin_serial_rs_console_init(void)
{
bfin_serial_init_ports();
/* Adam:
* kernel/printk.c: register_console()
* The console driver calls this routine during kernel initialization
* to register the console printing procedure with printk() and to
* print any messages that were printed by the kernel before the
* console driver was initialized.
*/
register_console(&bfin_serial_console);
#ifdef CONFIG_KGDB_UART
kgdb_entry_state = 0;
init_kgdb_uart();
#endif
return 0;
}
console_initcall(bfin_serial_rs_console_init);

#define BFIN_SERIAL_CONSOLE &bfin_serial_console
#else
#define BFIN_SERIAL_CONSOLE NULL
#endif /* CONFIG_SERIAL_BFIN_CONSOLE */


#ifdef CONFIG_EARLY_PRINTK
static __init void early_serial_putc(struct uart_port *port, int ch)
{
unsigned timeout = 0xffff;
struct bfin_serial_port *uart = (struct bfin_serial_port *)port;

while ((!(UART_GET_LSR(uart) & THRE)) && --timeout)
cpu_relax();
UART_PUT_CHAR(uart, ch);
}

static __init void early_serial_write(struct console *con, const char *s,
unsigned int n)
{
struct bfin_serial_port *uart = &bfin_serial_ports[con->index];
unsigned int i;

for (i = 0; i < n; i++, s++) {
if (*s == '\n')
early_serial_putc(&uart->port, '\r');
early_serial_putc(&uart->port, *s);
}
}

static struct __init console bfin_early_serial_console = {
.name = "early_BFuart",
.write = early_serial_write,
.device = uart_console_device,
.flags = CON_PRINTBUFFER,
.setup = bfin_serial_console_setup,
.index = -1,
.data = &bfin_serial_reg,
};


/* Adam:
* Called from: arch/blackfin/kernel/early_printk.c: bfin_earlyserial_init()

*/
struct console __init *bfin_earlyserial_init(unsigned int port,
unsigned int cflag)
{
struct bfin_serial_port *uart;
struct ktermios t;

if (port == -1 || port >= nr_ports)
port = 0;
bfin_serial_init_ports();
bfin_early_serial_console.index = port;
uart = &bfin_serial_ports[port];
t.c_cflag = cflag;
t.c_iflag = 0;
t.c_oflag = 0;
t.c_lflag = ICANON;
t.c_line = port;
bfin_serial_set_termios(&uart->port, &t, &t);
return &bfin_early_serial_console;
}

#endif /* CONFIG_SERIAL_BFIN_CONSOLE */

static struct uart_driver bfin_serial_reg = {
.owner = THIS_MODULE,
.driver_name = "bfin-uart",
.dev_name = BFIN_SERIAL_NAME,
.major = BFIN_SERIAL_MAJOR,
.minor = BFIN_SERIAL_MINOR,
.nr = NR_PORTS,
.cons = BFIN_SERIAL_CONSOLE,
};

static int bfin_serial_suspend(struct platform_device *dev, pm_message_t state)
{
struct bfin_serial_port *uart = platform_get_drvdata(dev);

if (uart)
uart_suspend_port(&bfin_serial_reg, &uart->port);

return 0;
}

static int bfin_serial_resume(struct platform_device *dev)
{
struct bfin_serial_port *uart = platform_get_drvdata(dev);

if (uart)
uart_resume_port(&bfin_serial_reg, &uart->port);

return 0;
}

static int bfin_serial_probe(struct platform_device *dev)
{
struct resource *res = dev->resource;
int i;

for (i = 0; i < dev->num_resources; i++, res++)
if (res->flags & IORESOURCE_MEM)
break;

if (i < dev->num_resources) {
for (i = 0; i < nr_ports; i++, res++) {
if (bfin_serial_ports[i].port.mapbase != res->start)
continue;
bfin_serial_ports[i].port.dev = &dev->dev;
uart_add_one_port(&bfin_serial_reg, &bfin_serial_ports[i].port);
platform_set_drvdata(dev, &bfin_serial_ports[i]);
}
}

return 0;
}

static int bfin_serial_remove(struct platform_device *pdev)
{
struct bfin_serial_port *uart = platform_get_drvdata(pdev);


#ifdef CONFIG_SERIAL_BFIN_CTSRTS
gpio_free(uart->cts_pin);
gpio_free(uart->rts_pin);
#endif

platform_set_drvdata(pdev, NULL);

if (uart)
uart_remove_one_port(&bfin_serial_reg, &uart->port);

return 0;
}

static struct platform_driver bfin_serial_driver = {
.probe = bfin_serial_probe,
.remove = bfin_serial_remove,
.suspend = bfin_serial_suspend,
.resume = bfin_serial_resume,
.driver = {
.name = "bfin-uart",
},
};

static int __init bfin_serial_init(void)
{
int ret;
#ifdef CONFIG_KGDB_UART
struct bfin_serial_port *uart = &bfin_serial_ports[CONFIG_KGDB_UART_PORT];
struct ktermios t;
#endif

pr_info("Serial: Blackfin serial driver\n");

bfin_serial_init_ports();

/**
* uart_register_driver - register a driver with the uart core layer
* @drv: low level driver structure
*
* Register a uart driver with the core driver. We in turn register
* with the tty layer, and initialise the core driver per-port state.
*
* We have a proc file in /proc/tty/driver which is named after the
* normal driver.
*
* drv->port should be NULL, and the per-port structures should be
* registered using uart_add_one_port after this call has succeeded.
*/

ret = uart_register_driver(&bfin_serial_reg);
if (ret == 0) {
ret = platform_driver_register(&bfin_serial_driver);
if (ret) {
pr_debug("uart register failed\n");
uart_unregister_driver(&bfin_serial_reg);
}
}
#ifdef CONFIG_KGDB_UART
if (uart->port.cons->index != CONFIG_KGDB_UART_PORT) {
request_irq(uart->port.irq, bfin_serial_rx_int,
IRQF_DISABLED, "BFIN_UART_RX", uart);
pr_info("Request irq for kgdb uart port\n");
#ifdef CONFIG_BF54x
UART_SET_IER(uart, ERBFI);
#else
UART_PUT_IER(uart, UART_GET_IER(uart) | ERBFI);
#endif
SSYNC();
t.c_cflag = CS8|B57600;
t.c_iflag = 0;
t.c_oflag = 0;
t.c_lflag = ICANON;
t.c_line = CONFIG_KGDB_UART_PORT;
bfin_serial_set_termios(&uart->port, &t, &t);
}
#endif
return ret;
}

static void __exit bfin_serial_exit(void)
{
platform_driver_unregister(&bfin_serial_driver);
uart_unregister_driver(&bfin_serial_reg);
}

module_init(bfin_serial_init);
module_exit(bfin_serial_exit);

Blog Archive