mirror of
https://github.com/Fishwaldo/u-boot.git
synced 2025-03-21 06:31:31 +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
|
||||
else
|
||||
obj-y += serial.o
|
||||
obj-$(CONFIG_SYS_NS16550_SERIAL) += serial_ns16550.o
|
||||
endif
|
||||
|
||||
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_SYS_NS16550) += ns16550.o
|
||||
obj-$(CONFIG_S5P) += serial_s5p.o
|
||||
obj-$(CONFIG_SYS_NS16550_SERIAL) += serial_ns16550.o
|
||||
obj-$(CONFIG_IMX_SERIAL) += serial_imx.o
|
||||
obj-$(CONFIG_KS8695_SERIAL) += serial_ks8695.o
|
||||
obj-$(CONFIG_MAX3100_SERIAL) += serial_max3100.o
|
||||
|
|
|
@ -5,17 +5,25 @@
|
|||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <ns16550.h>
|
||||
#include <serial.h>
|
||||
#include <watchdog.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define UART_LCRVAL UART_LCR_8N1 /* 8 data, 1 stop, no parity */
|
||||
#define UART_MCRVAL (UART_MCR_DTR | \
|
||||
UART_MCR_RTS) /* RTS/DTR */
|
||||
#define UART_FCRVAL (UART_FCR_FIFO_EN | \
|
||||
UART_FCR_RXSR | \
|
||||
UART_FCR_TXSR) /* Clear & enable FIFOs */
|
||||
|
||||
#ifndef CONFIG_DM_SERIAL
|
||||
#ifdef CONFIG_SYS_NS16550_PORT_MAPPED
|
||||
#define serial_out(x, y) outb(x, (ulong)y)
|
||||
#define serial_in(y) inb((ulong)y)
|
||||
|
@ -29,6 +37,7 @@
|
|||
#define serial_out(x, y) writeb(x, y)
|
||||
#define serial_in(y) readb(y)
|
||||
#endif
|
||||
#endif /* !CONFIG_DM_SERIAL */
|
||||
|
||||
#if defined(CONFIG_SOC_KEYSTONE)
|
||||
#define UART_REG_VAL_PWREMU_MGMT_UART_DISABLE 0
|
||||
|
@ -45,6 +54,58 @@
|
|||
#define CONFIG_SYS_NS16550_IER 0x00
|
||||
#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)
|
||||
{
|
||||
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))
|
||||
== 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);
|
||||
}
|
||||
#endif
|
||||
|
@ -95,7 +157,8 @@ void NS16550_init(NS16550_t com_port, int baud_divisor)
|
|||
NS16550_setbrg(com_port, 0);
|
||||
serial_out(UART_MCRVAL, &com_port->mcr);
|
||||
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) || \
|
||||
defined(CONFIG_AM33XX) || defined(CONFIG_SOC_DA8XX) || \
|
||||
defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX)
|
||||
|
@ -154,3 +217,92 @@ int NS16550_tstc(NS16550_t com_port)
|
|||
}
|
||||
|
||||
#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>
|
||||
|
||||
#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)
|
||||
#error "Please define NS16550 registers size."
|
||||
#elif defined(CONFIG_SYS_NS16550_MEM32)
|
||||
|
@ -37,6 +45,21 @@
|
|||
unsigned char postpad_##x[-CONFIG_SYS_NS16550_REG_SIZE - 1];
|
||||
#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 {
|
||||
UART_REG(rbr); /* 0 */
|
||||
UART_REG(ier); /* 1 */
|
||||
|
@ -65,6 +88,9 @@ struct NS16550 {
|
|||
UART_REG(scr); /* 10*/
|
||||
UART_REG(ssr); /* 11*/
|
||||
#endif
|
||||
#ifdef CONFIG_DM_SERIAL
|
||||
struct ns16550_platdata *plat;
|
||||
#endif
|
||||
};
|
||||
|
||||
#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
|
||||
*/
|
||||
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