#include <linux/console.h>
#include <linux/platform_device.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <asm/irq.h>
#include <asm/io.h>
#define USB_UART_MAJOR 200 /* You've to get this assigned */
#define USB_UART_MINOR_START 70 /* Start minor numbering here */
#define USB_UART_PORTS 2 /* The phone has 2 USB_UARTs */
#define PORT_USB_UART 30 /* UART type. Add this to
include/linux/serial_core.h */
/* Each USB_UART has a 3-byte register set consisting of
UU_STATUS_REGISTER at offset 0, UU_READ_DATA_REGISTER at
offset 1, and UU_WRITE_DATA_REGISTER at offset 2 as shown
in Table 6.1 */
#define USB_UART1_BASE 0xe8000000 /* Memory base for USB_UART1 */
#define USB_UART2_BASE 0xe9000000 /* Memory base for USB_UART2 */
#define USB_UART_REGISTER_SPACE 0x3
/* Semantics of bits in the status register */
#define USB_UART_TX_FULL 0x20 /* TX FIFO is full */
#define USB_UART_RX_EMPTY 0x10 /* TX FIFO is empty */
#define USB_UART_STATUS 0x0F /* Parity/frame/overruns? */
#define USB_UART1_IRQ 3 /* USB_UART1 IRQ */
#define USB_UART2_IRQ 4 /* USB_UART2 IRQ */
#define USB_UART_FIFO_SIZE 32 /* FIFO size */
#define USB_UART_CLK_FREQ 16000000
static struct uart_port usb_uart_port[]; /* Defined later on */
/* Write a character to the USB_UART port */
static void
usb_uart_putc(struct uart_port *port, unsigned char c)
{
/* Wait until there is space in the TX FIFO of the USB_UART.
Sense this by looking at the USB_UART_TX_FULL bit in the
status register */
while (__raw_readb(port->membase) & USB_UART_TX_FULL);
/* Write the character to the data port*/
__raw_writeb(c, (port->membase+1));
}
/* Read a character from the USB_UART */
static unsigned char
usb_uart_getc(struct uart_port *port)
{
/* Wait until data is available in the RX_FIFO */
while (__raw_readb(port->membase) & USB_UART_RX_EMPTY);
/* Obtain the data */
return(__raw_readb(port->membase+2));
}
/* Obtain USB_UART status */
static unsigned char
usb_uart_status(struct uart_port *port)
{
return(__raw_readb(port->membase) & USB_UART_STATUS);
}
/*
* Claim the memory region attached to USB_UART port. Called
* when the driver adds a USB_UART port via uart_add_one_port().
*/
static int
usb_uart_request_port(struct uart_port *port)
{
if (!request_mem_region(port->mapbase, USB_UART_REGISTER_SPACE,
"usb_uart")) {
return -EBUSY;
}
return 0;
}
/* Release the memory region attached to a USB_UART port.
* Called when the driver removes a USB_UART port via
* uart_remove_one_port().
*/
static void
usb_uart_release_port(struct uart_port *port)
{
release_mem_region(port->mapbase, USB_UART_REGISTER_SPACE);
}
/*
* Configure USB_UART. Called when the driver adds a USB_UART port.
*/
static void
usb_uart_config_port(struct uart_port *port, int flags)
{
if (flags & UART_CONFIG_TYPE && usb_uart_request_port(port) == 0)
{
port->type = PORT_USB_UART;
}
}
/* Receive interrupt handler */
static irqreturn_t
usb_uart_rxint(int irq, void *dev_id)
{
struct uart_port *port = (struct uart_port *) dev_id;
struct tty_struct *tty = port->info->tty;
unsigned int status, data;
/* ... */
do {
/* ... */
/* Read data */
data = usb_uart_getc(port);
/* Normal, overrun, parity, frame error? */
status = usb_uart_status(port);
/* Dispatch to the tty layer */
tty_insert_flip_char(tty, data, status);
/* ... */
} while (more_chars_to_be_read()); /* More chars */
/* ... */
tty_flip_buffer_push(tty);
return IRQ_HANDLED;
}
/* Called when an application opens a USB_UART */
static int
usb_uart_startup(struct uart_port *port)
{
int retval = 0;
/* ... */
/* Request IRQ */
if ((retval = request_irq(port->irq, usb_uart_rxint, 0,
"usb_uart", (void *)port))) {
return retval;
}
/* ... */
return retval;
}
/* Called when an application closes a USB_UART */
static void
usb_uart_shutdown(struct uart_port *port)
{
/* ... */
/* Free IRQ */
free_irq(port->irq, port);
/* Disable interrupts by writing to appropriate
registers */
/* ... */
}
/* Set UART type to USB_UART */
static const char *
usb_uart_type(struct uart_port *port)
{
return port->type == PORT_USB_UART ? "USB_UART" : NULL;
}
/* Start transmitting bytes */
static void
usb_uart_start_tx(struct uart_port *port)
{
while (1) {
/* Get the data from the UART circular buffer and
write it to the USB_UART's WRITE_DATA register */
usb_uart_putc(port,
port->info->xmit.buf[port->info->xmit.tail]);
/* Adjust the tail of the UART buffer */
port->info->xmit.tail = (port->info->xmit.tail + 1) &
(UART_XMIT_SIZE - 1);
/* Statistics */
port->icount.tx++;
/* Finish if no more data available in the UART buffer */
if (uart_circ_empty(&port->info->xmit)) break;
}
/* ... */
}
/* The UART operations structure */
static struct uart_ops usb_uart_ops = {
.start_tx = usb_uart_start_tx, /* Start transmitting */
.startup = usb_uart_startup, /* App opens USB_UART */
.shutdown = usb_uart_shutdown, /* App closes USB_UART */
.type = usb_uart_type, /* Set UART type */
.config_port = usb_uart_config_port, /* Configure when driver
adds a USB_UART port */
.request_port = usb_uart_request_port,/* Claim resources
associated with a
USB_UART port */
.release_port = usb_uart_release_port,/* Release resources
associated with a
USB_UART port */
#if 0 /* Left unimplemented for the USB_UART */
.tx_empty = usb_uart_tx_empty, /* Transmitter busy? */
.set_mctrl = usb_uart_set_mctrl, /* Set modem control */
.get_mctrl = usb_uart_get_mctrl, /* Get modem control */
.stop_tx = usb_uart_stop_tx, /* Stop transmission */
.stop_rx = usb_uart_stop_rx, /* Stop reception */
.enable_ms = usb_uart_enable_ms, /* Enable modem status
signals */
.set_termios = usb_uart_set_termios, /* Set termios */
#endif
};
static struct uart_driver usb_uart_reg = {
.owner = THIS_MODULE, /* Owner */
.driver_name = "usb_uart", /* Driver name */
.dev_name = "ttyUU", /* Node name */
.major = USB_UART_MAJOR, /* Major number */
.minor = USB_UART_MINOR_START, /* Minor number start */
.nr = USB_UART_PORTS, /* Number of UART ports */
.cons = &usb_uart_console, /* Pointer to the console
structure. Discussed in Chapter
12, "Video Drivers" */
};
/* Called when the platform driver is unregistered */
static int
usb_uart_remove(struct platform_device *dev)
{
platform_set_drvdata(dev, NULL);
/* Remove the USB_UART port from the serial core */
uart_remove_one_port(&usb_uart_reg, &usb_uart_port[dev->id]);
return 0;
}
/* Suspend power management event */
static int
usb_uart_suspend(struct platform_device *dev, pm_message_t state)
{
uart_suspend_port(&usb_uart_reg, &usb_uart_port[dev->id]);
return 0;
}
/* Resume after a previous suspend */
static int
usb_uart_resume(struct platform_device *dev)
{
uart_resume_port(&usb_uart_reg, &usb_uart_port[dev->id]);
return 0;
}
/* Parameters of each supported USB_UART port */
static struct uart_port usb_uart_port[] = {
{
.mapbase = (unsigned int) USB_UART1_BASE,
.iotype = UPIO_MEM, /* Memory mapped */
.irq = USB_UART1_IRQ, /* IRQ */
.uartclk = USB_UART_CLK_FREQ, /* Clock HZ */
.fifosize = USB_UART_FIFO_SIZE, /* Size of the FIFO */
.ops = &usb_uart_ops, /* UART operations */
.flags = UPF_BOOT_AUTOCONF, /* UART port flag */
.line = 0, /* UART port number */
},
{
.mapbase = (unsigned int)USB_UART2_BASE,
.iotype = UPIO_MEM, /* Memory mapped */
.irq = USB_UART2_IRQ, /* IRQ */
.uartclk = USB_UART_CLK_FREQ, /* CLock HZ */
.fifosize = USB_UART_FIFO_SIZE, /* Size of the FIFO */
.ops = &usb_uart_ops, /* UART operations */
.flags = UPF_BOOT_AUTOCONF, /* UART port flag */
.line = 1, /* UART port number */
}
};
/* Platform driver probe */
static int __init
usb_uart_probe(struct platform_device *dev)
{
/* ... */
/* Add a USB_UART port. This function also registers this device
with the tty layer and triggers invocation of the config_port()
entry point */
uart_add_one_port(&usb_uart_reg, &usb_uart_port[dev->id]);
platform_set_drvdata(dev, &usb_uart_port[dev->id]);
return 0;
}
struct platform_device *usb_uart_plat_device1; /* Platform device
for USB_UART 1 */
struct platform_device *usb_uart_plat_device2; /* Platform device
for USB_UART 2 */
static struct platform_driver usb_uart_driver = {
.probe = usb_uart_probe, /* Probe method */
.remove = __exit_p(usb_uart_remove), /* Detach method */
.suspend = usb_uart_suspend, /* Power suspend */
.resume = usb_uart_resume, /* Resume after a suspend */
.driver = {
.name = "usb_uart", /* Driver name */
},
};
/* Driver Initialization */
static int __init
usb_uart_init(void)
{
int retval;
/* Register the USB_UART driver with the serial core */
if ((retval = uart_register_driver(&usb_uart_reg))) {
return retval;
}
/* Register platform device for USB_UART 1. Usually called
during architecture-specific setup */
usb_uart_plat_device1 =
platform_device_register_simple("usb_uart", 0, NULL, 0);
if (IS_ERR(usb_uart_plat_device1)) {
uart_unregister_driver(&usb_uart_reg);
return PTR_ERR(usb_uart_plat_device1);
}
/* Register platform device for USB_UART 2. Usually called
during architecture-specific setup */
usb_uart_plat_device2 =
platform_device_register_simple("usb_uart", 1, NULL, 0);
if (IS_ERR(usb_uart_plat_device2)) {
uart_unregister_driver(&usb_uart_reg);
platform_device_unregister(usb_uart_plat_device1);
return PTR_ERR(usb_uart_plat_device2);
}
/* Announce a matching driver for the platform
devices registered above */
if ((retval = platform_driver_register(&usb_uart_driver))) {
uart_unregister_driver(&usb_uart_reg);
platform_device_unregister(usb_uart_plat_device1);
platform_device_unregister(usb_uart_plat_device2);
}
return 0;
}
/* Driver Exit */
static void __exit
usb_uart_exit(void)
{
/* The order of unregistration is important. Unregistering the
UART driver before the platform driver will crash the system */
/* Unregister the platform driver */
platform_driver_unregister(&usb_uart_driver);
/* Unregister the platform devices */
platform_device_unregister(usb_uart_plat_device1);
platform_device_unregister(usb_uart_plat_device2);
/* Unregister the USB_UART driver */
uart_unregister_driver(&usb_uart_reg);
}
module_init(usb_uart_init);
module_exit(usb_uart_exit);
syntax highlighted by Code2HTML, v. 0.9.1