mirror of
https://github.com/Fishwaldo/u-boot.git
synced 2025-03-28 01:51:33 +00:00
dm: serial: Add driver model support for ns16550
Add driver model support so that ns16550 can support operation both with and without driver model. The driver needs a clock frequency so cannot stand alone unfortunately. The clock frequency must be provided by a separate driver. Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
parent
8bbe33c829
commit
12e431b277
4 changed files with 218 additions and 3 deletions
10
doc/device-tree-bindings/serial/ns16550.txt
Normal file
10
doc/device-tree-bindings/serial/ns16550.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
NS16550 UART
|
||||||
|
|
||||||
|
This UART driver supports many chip variants and is used in mamy SoCs.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: "ns16550" or "nvidia,tegra20-uart"
|
||||||
|
- reg: start address and size of registers
|
||||||
|
- reg-shift: shift value indicating register size: 0=byte, 1=16bit,2=32bit etc.
|
||||||
|
- clock-frequency: input clock frequency for the UART (used to calculate the
|
||||||
|
baud rate divisor)
|
|
@ -9,6 +9,7 @@ ifdef CONFIG_DM_SERIAL
|
||||||
obj-y += serial-uclass.o
|
obj-y += serial-uclass.o
|
||||||
else
|
else
|
||||||
obj-y += serial.o
|
obj-y += serial.o
|
||||||
|
obj-$(CONFIG_SYS_NS16550_SERIAL) += serial_ns16550.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
obj-$(CONFIG_ALTERA_UART) += altera_uart.o
|
obj-$(CONFIG_ALTERA_UART) += altera_uart.o
|
||||||
|
@ -20,7 +21,6 @@ obj-$(CONFIG_MCFUART) += mcfuart.o
|
||||||
obj-$(CONFIG_OPENCORES_YANU) += opencores_yanu.o
|
obj-$(CONFIG_OPENCORES_YANU) += opencores_yanu.o
|
||||||
obj-$(CONFIG_SYS_NS16550) += ns16550.o
|
obj-$(CONFIG_SYS_NS16550) += ns16550.o
|
||||||
obj-$(CONFIG_S5P) += serial_s5p.o
|
obj-$(CONFIG_S5P) += serial_s5p.o
|
||||||
obj-$(CONFIG_SYS_NS16550_SERIAL) += serial_ns16550.o
|
|
||||||
obj-$(CONFIG_IMX_SERIAL) += serial_imx.o
|
obj-$(CONFIG_IMX_SERIAL) += serial_imx.o
|
||||||
obj-$(CONFIG_KS8695_SERIAL) += serial_ks8695.o
|
obj-$(CONFIG_KS8695_SERIAL) += serial_ks8695.o
|
||||||
obj-$(CONFIG_MAX3100_SERIAL) += serial_max3100.o
|
obj-$(CONFIG_MAX3100_SERIAL) += serial_max3100.o
|
||||||
|
|
|
@ -5,17 +5,25 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <common.h>
|
#include <common.h>
|
||||||
|
#include <dm.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fdtdec.h>
|
||||||
#include <ns16550.h>
|
#include <ns16550.h>
|
||||||
|
#include <serial.h>
|
||||||
#include <watchdog.h>
|
#include <watchdog.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
|
DECLARE_GLOBAL_DATA_PTR;
|
||||||
|
|
||||||
#define UART_LCRVAL UART_LCR_8N1 /* 8 data, 1 stop, no parity */
|
#define UART_LCRVAL UART_LCR_8N1 /* 8 data, 1 stop, no parity */
|
||||||
#define UART_MCRVAL (UART_MCR_DTR | \
|
#define UART_MCRVAL (UART_MCR_DTR | \
|
||||||
UART_MCR_RTS) /* RTS/DTR */
|
UART_MCR_RTS) /* RTS/DTR */
|
||||||
#define UART_FCRVAL (UART_FCR_FIFO_EN | \
|
#define UART_FCRVAL (UART_FCR_FIFO_EN | \
|
||||||
UART_FCR_RXSR | \
|
UART_FCR_RXSR | \
|
||||||
UART_FCR_TXSR) /* Clear & enable FIFOs */
|
UART_FCR_TXSR) /* Clear & enable FIFOs */
|
||||||
|
|
||||||
|
#ifndef CONFIG_DM_SERIAL
|
||||||
#ifdef CONFIG_SYS_NS16550_PORT_MAPPED
|
#ifdef CONFIG_SYS_NS16550_PORT_MAPPED
|
||||||
#define serial_out(x, y) outb(x, (ulong)y)
|
#define serial_out(x, y) outb(x, (ulong)y)
|
||||||
#define serial_in(y) inb((ulong)y)
|
#define serial_in(y) inb((ulong)y)
|
||||||
|
@ -29,6 +37,7 @@
|
||||||
#define serial_out(x, y) writeb(x, y)
|
#define serial_out(x, y) writeb(x, y)
|
||||||
#define serial_in(y) readb(y)
|
#define serial_in(y) readb(y)
|
||||||
#endif
|
#endif
|
||||||
|
#endif /* !CONFIG_DM_SERIAL */
|
||||||
|
|
||||||
#if defined(CONFIG_SOC_KEYSTONE)
|
#if defined(CONFIG_SOC_KEYSTONE)
|
||||||
#define UART_REG_VAL_PWREMU_MGMT_UART_DISABLE 0
|
#define UART_REG_VAL_PWREMU_MGMT_UART_DISABLE 0
|
||||||
|
@ -45,6 +54,58 @@
|
||||||
#define CONFIG_SYS_NS16550_IER 0x00
|
#define CONFIG_SYS_NS16550_IER 0x00
|
||||||
#endif /* CONFIG_SYS_NS16550_IER */
|
#endif /* CONFIG_SYS_NS16550_IER */
|
||||||
|
|
||||||
|
#ifdef CONFIG_DM_SERIAL
|
||||||
|
static void ns16550_writeb(NS16550_t port, int offset, int value)
|
||||||
|
{
|
||||||
|
struct ns16550_platdata *plat = port->plat;
|
||||||
|
unsigned char *addr;
|
||||||
|
|
||||||
|
offset *= 1 << plat->reg_shift;
|
||||||
|
addr = plat->base + offset;
|
||||||
|
/*
|
||||||
|
* As far as we know it doesn't make sense to support selection of
|
||||||
|
* these options at run-time, so use the existing CONFIG options.
|
||||||
|
*/
|
||||||
|
#ifdef CONFIG_SYS_NS16550_PORT_MAPPED
|
||||||
|
outb(value, addr);
|
||||||
|
#elif defined(CONFIG_SYS_NS16550_MEM32) && !defined(CONFIG_SYS_BIG_ENDIAN)
|
||||||
|
out_le32(addr, value);
|
||||||
|
#elif defined(CONFIG_SYS_NS16550_MEM32) && defined(CONFIG_SYS_BIG_ENDIAN)
|
||||||
|
out_be32(addr, value);
|
||||||
|
#elif defined(CONFIG_SYS_BIG_ENDIAN)
|
||||||
|
writeb(value, addr + (1 << plat->reg_shift) - 1);
|
||||||
|
#else
|
||||||
|
writeb(value, addr);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ns16550_readb(NS16550_t port, int offset)
|
||||||
|
{
|
||||||
|
struct ns16550_platdata *plat = port->plat;
|
||||||
|
unsigned char *addr;
|
||||||
|
|
||||||
|
offset *= 1 << plat->reg_shift;
|
||||||
|
addr = plat->base + offset;
|
||||||
|
#ifdef CONFIG_SYS_NS16550_PORT_MAPPED
|
||||||
|
return inb(addr);
|
||||||
|
#elif defined(CONFIG_SYS_NS16550_MEM32) && !defined(CONFIG_SYS_BIG_ENDIAN)
|
||||||
|
return in_le32(addr);
|
||||||
|
#elif defined(CONFIG_SYS_NS16550_MEM32) && defined(CONFIG_SYS_BIG_ENDIAN)
|
||||||
|
return in_be32(addr);
|
||||||
|
#elif defined(CONFIG_SYS_BIG_ENDIAN)
|
||||||
|
return readb(addr + (1 << plat->reg_shift) - 1);
|
||||||
|
#else
|
||||||
|
return readb(addr);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We can clean these up once everything is moved to driver model */
|
||||||
|
#define serial_out(value, addr) \
|
||||||
|
ns16550_writeb(com_port, addr - (unsigned char *)com_port, value)
|
||||||
|
#define serial_in(addr) \
|
||||||
|
ns16550_readb(com_port, addr - (unsigned char *)com_port)
|
||||||
|
#endif
|
||||||
|
|
||||||
int ns16550_calc_divisor(NS16550_t port, int clock, int baudrate)
|
int ns16550_calc_divisor(NS16550_t port, int clock, int baudrate)
|
||||||
{
|
{
|
||||||
const unsigned int mode_x_div = 16;
|
const unsigned int mode_x_div = 16;
|
||||||
|
@ -79,7 +140,8 @@ void NS16550_init(NS16550_t com_port, int baud_divisor)
|
||||||
*/
|
*/
|
||||||
if ((serial_in(&com_port->lsr) & (UART_LSR_TEMT | UART_LSR_THRE))
|
if ((serial_in(&com_port->lsr) & (UART_LSR_TEMT | UART_LSR_THRE))
|
||||||
== UART_LSR_THRE) {
|
== UART_LSR_THRE) {
|
||||||
NS16550_setbrg(com_port, baud_divisor);
|
if (baud_divisor != -1)
|
||||||
|
NS16550_setbrg(com_port, baud_divisor);
|
||||||
serial_out(0, &com_port->mdr1);
|
serial_out(0, &com_port->mdr1);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -95,7 +157,8 @@ void NS16550_init(NS16550_t com_port, int baud_divisor)
|
||||||
NS16550_setbrg(com_port, 0);
|
NS16550_setbrg(com_port, 0);
|
||||||
serial_out(UART_MCRVAL, &com_port->mcr);
|
serial_out(UART_MCRVAL, &com_port->mcr);
|
||||||
serial_out(UART_FCRVAL, &com_port->fcr);
|
serial_out(UART_FCRVAL, &com_port->fcr);
|
||||||
NS16550_setbrg(com_port, baud_divisor);
|
if (baud_divisor != -1)
|
||||||
|
NS16550_setbrg(com_port, baud_divisor);
|
||||||
#if defined(CONFIG_OMAP) || \
|
#if defined(CONFIG_OMAP) || \
|
||||||
defined(CONFIG_AM33XX) || defined(CONFIG_SOC_DA8XX) || \
|
defined(CONFIG_AM33XX) || defined(CONFIG_SOC_DA8XX) || \
|
||||||
defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX)
|
defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX)
|
||||||
|
@ -154,3 +217,92 @@ int NS16550_tstc(NS16550_t com_port)
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_NS16550_MIN_FUNCTIONS */
|
#endif /* CONFIG_NS16550_MIN_FUNCTIONS */
|
||||||
|
|
||||||
|
#ifdef CONFIG_DM_SERIAL
|
||||||
|
static int ns16550_serial_putc(struct udevice *dev, const char ch)
|
||||||
|
{
|
||||||
|
struct NS16550 *const com_port = dev_get_priv(dev);
|
||||||
|
|
||||||
|
if (!(serial_in(&com_port->lsr) & UART_LSR_THRE))
|
||||||
|
return -EAGAIN;
|
||||||
|
serial_out(ch, &com_port->thr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call watchdog_reset() upon newline. This is done here in putc
|
||||||
|
* since the environment code uses a single puts() to print the complete
|
||||||
|
* environment upon "printenv". So we can't put this watchdog call
|
||||||
|
* in puts().
|
||||||
|
*/
|
||||||
|
if (ch == '\n')
|
||||||
|
WATCHDOG_RESET();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ns16550_serial_pending(struct udevice *dev, bool input)
|
||||||
|
{
|
||||||
|
struct NS16550 *const com_port = dev_get_priv(dev);
|
||||||
|
|
||||||
|
if (input)
|
||||||
|
return serial_in(&com_port->lsr) & UART_LSR_DR ? 1 : 0;
|
||||||
|
else
|
||||||
|
return serial_in(&com_port->lsr) & UART_LSR_THRE ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ns16550_serial_getc(struct udevice *dev)
|
||||||
|
{
|
||||||
|
struct NS16550 *const com_port = dev_get_priv(dev);
|
||||||
|
|
||||||
|
if (!serial_in(&com_port->lsr) & UART_LSR_DR)
|
||||||
|
return -EAGAIN;
|
||||||
|
|
||||||
|
return serial_in(&com_port->rbr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ns16550_serial_setbrg(struct udevice *dev, int baudrate)
|
||||||
|
{
|
||||||
|
struct NS16550 *const com_port = dev_get_priv(dev);
|
||||||
|
struct ns16550_platdata *plat = com_port->plat;
|
||||||
|
int clock_divisor;
|
||||||
|
|
||||||
|
clock_divisor = ns16550_calc_divisor(com_port, plat->clock, baudrate);
|
||||||
|
|
||||||
|
NS16550_setbrg(com_port, clock_divisor);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ns16550_serial_probe(struct udevice *dev)
|
||||||
|
{
|
||||||
|
struct NS16550 *const com_port = dev_get_priv(dev);
|
||||||
|
|
||||||
|
NS16550_init(com_port, -1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ns16550_serial_ofdata_to_platdata(struct udevice *dev)
|
||||||
|
{
|
||||||
|
struct NS16550 *const com_port = dev_get_priv(dev);
|
||||||
|
struct ns16550_platdata *plat = dev->platdata;
|
||||||
|
fdt_addr_t addr;
|
||||||
|
|
||||||
|
addr = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg");
|
||||||
|
if (addr == FDT_ADDR_T_NONE)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
plat->base = (unsigned char *)addr;
|
||||||
|
plat->reg_shift = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
|
||||||
|
"reg-shift", 1);
|
||||||
|
com_port->plat = plat;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct dm_serial_ops ns16550_serial_ops = {
|
||||||
|
.putc = ns16550_serial_putc,
|
||||||
|
.pending = ns16550_serial_pending,
|
||||||
|
.getc = ns16550_serial_getc,
|
||||||
|
.setbrg = ns16550_serial_setbrg,
|
||||||
|
};
|
||||||
|
#endif /* CONFIG_DM_SERIAL */
|
||||||
|
|
|
@ -23,6 +23,14 @@
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#ifdef CONFIG_DM_SERIAL
|
||||||
|
/*
|
||||||
|
* For driver model we always use one byte per register, and sort out the
|
||||||
|
* differences in the driver
|
||||||
|
*/
|
||||||
|
#define CONFIG_SYS_NS16550_REG_SIZE (-1)
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !defined(CONFIG_SYS_NS16550_REG_SIZE) || (CONFIG_SYS_NS16550_REG_SIZE == 0)
|
#if !defined(CONFIG_SYS_NS16550_REG_SIZE) || (CONFIG_SYS_NS16550_REG_SIZE == 0)
|
||||||
#error "Please define NS16550 registers size."
|
#error "Please define NS16550 registers size."
|
||||||
#elif defined(CONFIG_SYS_NS16550_MEM32)
|
#elif defined(CONFIG_SYS_NS16550_MEM32)
|
||||||
|
@ -37,6 +45,21 @@
|
||||||
unsigned char postpad_##x[-CONFIG_SYS_NS16550_REG_SIZE - 1];
|
unsigned char postpad_##x[-CONFIG_SYS_NS16550_REG_SIZE - 1];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct ns16550_platdata - information about a NS16550 port
|
||||||
|
*
|
||||||
|
* @base: Base register address
|
||||||
|
* @reg_shift: Shift size of registers (0=byte, 1=16bit, 2=32bit...)
|
||||||
|
* @clock: UART base clock speed in Hz
|
||||||
|
*/
|
||||||
|
struct ns16550_platdata {
|
||||||
|
unsigned char *base;
|
||||||
|
int reg_shift;
|
||||||
|
int clock;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct udevice;
|
||||||
|
|
||||||
struct NS16550 {
|
struct NS16550 {
|
||||||
UART_REG(rbr); /* 0 */
|
UART_REG(rbr); /* 0 */
|
||||||
UART_REG(ier); /* 1 */
|
UART_REG(ier); /* 1 */
|
||||||
|
@ -65,6 +88,9 @@ struct NS16550 {
|
||||||
UART_REG(scr); /* 10*/
|
UART_REG(scr); /* 10*/
|
||||||
UART_REG(ssr); /* 11*/
|
UART_REG(ssr); /* 11*/
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CONFIG_DM_SERIAL
|
||||||
|
struct ns16550_platdata *plat;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#define thr rbr
|
#define thr rbr
|
||||||
|
@ -183,3 +209,30 @@ void NS16550_reinit(NS16550_t com_port, int baud_divisor);
|
||||||
* @return baud rate divisor that should be used
|
* @return baud rate divisor that should be used
|
||||||
*/
|
*/
|
||||||
int ns16550_calc_divisor(NS16550_t port, int clock, int baudrate);
|
int ns16550_calc_divisor(NS16550_t port, int clock, int baudrate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ns16550_serial_ofdata_to_platdata() - convert DT to platform data
|
||||||
|
*
|
||||||
|
* Decode a device tree node for an ns16550 device. This includes the
|
||||||
|
* register base address and register shift properties. The caller must set
|
||||||
|
* up the clock frequency.
|
||||||
|
*
|
||||||
|
* @dev: dev to decode platform data for
|
||||||
|
* @return: 0 if OK, -EINVAL on error
|
||||||
|
*/
|
||||||
|
int ns16550_serial_ofdata_to_platdata(struct udevice *dev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ns16550_serial_probe() - probe a serial port
|
||||||
|
*
|
||||||
|
* This sets up the serial port ready for use, except for the baud rate
|
||||||
|
* @return 0, or -ve on error
|
||||||
|
*/
|
||||||
|
int ns16550_serial_probe(struct udevice *dev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct ns16550_serial_ops - ns16550 serial operations
|
||||||
|
*
|
||||||
|
* These should be used by the client driver for the driver's 'ops' member
|
||||||
|
*/
|
||||||
|
extern const struct dm_serial_ops ns16550_serial_ops;
|
||||||
|
|
Loading…
Add table
Reference in a new issue