mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2026-06-13 15:03:58 +03:00
Merge branch 'master' of git://git.denx.de/u-boot-dm
This commit is contained in:
@@ -118,7 +118,8 @@ static int driver_check_compatible(const void *blob, int offset,
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int lists_bind_fdt(struct udevice *parent, const void *blob, int offset)
|
||||
int lists_bind_fdt(struct udevice *parent, const void *blob, int offset,
|
||||
struct udevice **devp)
|
||||
{
|
||||
struct driver *driver = ll_entry_start(struct driver, driver);
|
||||
const int n_ents = ll_entry_count(struct driver, driver);
|
||||
@@ -130,6 +131,8 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset)
|
||||
int ret = 0;
|
||||
|
||||
dm_dbg("bind node %s\n", fdt_get_name(blob, offset, NULL));
|
||||
if (devp)
|
||||
*devp = NULL;
|
||||
for (entry = driver; entry != driver + n_ents; entry++) {
|
||||
ret = driver_check_compatible(blob, offset, entry->of_match);
|
||||
name = fdt_get_name(blob, offset, NULL);
|
||||
@@ -149,10 +152,11 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset)
|
||||
ret = device_bind(parent, entry, name, NULL, offset, &dev);
|
||||
if (ret) {
|
||||
dm_warn("Error binding driver '%s'\n", entry->name);
|
||||
if (!result || ret != -ENOENT)
|
||||
result = ret;
|
||||
return ret;
|
||||
} else {
|
||||
found = true;
|
||||
if (devp)
|
||||
*devp = dev;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ int dm_scan_fdt_node(struct udevice *parent, const void *blob, int offset,
|
||||
if (pre_reloc_only &&
|
||||
!fdt_getprop(blob, offset, "u-boot,dm-pre-reloc", NULL))
|
||||
continue;
|
||||
err = lists_bind_fdt(parent, blob, offset);
|
||||
err = lists_bind_fdt(parent, blob, offset, NULL);
|
||||
if (err && !ret)
|
||||
ret = err;
|
||||
}
|
||||
|
||||
@@ -12,10 +12,17 @@
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <malloc.h>
|
||||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/bitops.h>
|
||||
#include <asm/arch/tegra.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <dm/device-internal.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
enum {
|
||||
TEGRA_CMD_INFO,
|
||||
@@ -24,14 +31,18 @@ enum {
|
||||
TEGRA_CMD_INPUT,
|
||||
};
|
||||
|
||||
static struct gpio_names {
|
||||
char name[GPIO_NAME_SIZE];
|
||||
} gpio_names[MAX_NUM_GPIOS];
|
||||
struct tegra_gpio_platdata {
|
||||
struct gpio_ctlr_bank *bank;
|
||||
const char *port_name; /* Name of port, e.g. "B" */
|
||||
int base_gpio; /* Port number for this port (0, 1,.., n-1) */
|
||||
};
|
||||
|
||||
static char *get_name(int i)
|
||||
{
|
||||
return *gpio_names[i].name ? gpio_names[i].name : "UNKNOWN";
|
||||
}
|
||||
/* Information about each port at run-time */
|
||||
struct tegra_port_info {
|
||||
char label[TEGRA_GPIOS_PER_PORT][GPIO_NAME_SIZE];
|
||||
struct gpio_ctlr_bank *bank;
|
||||
int base_gpio; /* Port number for this port (0, 1,.., n-1) */
|
||||
};
|
||||
|
||||
/* Return config of pin 'gpio' as GPIO (1) or SFPIO (0) */
|
||||
static int get_config(unsigned gpio)
|
||||
@@ -121,38 +132,72 @@ static void set_level(unsigned gpio, int high)
|
||||
writel(u, &bank->gpio_out[GPIO_PORT(gpio)]);
|
||||
}
|
||||
|
||||
static int check_reserved(struct udevice *dev, unsigned offset,
|
||||
const char *func)
|
||||
{
|
||||
struct tegra_port_info *state = dev_get_priv(dev);
|
||||
struct gpio_dev_priv *uc_priv = dev->uclass_priv;
|
||||
|
||||
if (!*state->label[offset]) {
|
||||
printf("tegra_gpio: %s: error: gpio %s%d not reserved\n",
|
||||
func, uc_priv->bank_name, offset);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set GPIO pin 'gpio' as an output, with polarity 'value' */
|
||||
int tegra_spl_gpio_direction_output(int gpio, int value)
|
||||
{
|
||||
/* Configure as a GPIO */
|
||||
set_config(gpio, 1);
|
||||
|
||||
/* Configure GPIO output value. */
|
||||
set_level(gpio, value);
|
||||
|
||||
/* Configure GPIO direction as output. */
|
||||
set_direction(gpio, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic_GPIO primitives.
|
||||
*/
|
||||
|
||||
int gpio_request(unsigned gpio, const char *label)
|
||||
static int tegra_gpio_request(struct udevice *dev, unsigned offset,
|
||||
const char *label)
|
||||
{
|
||||
if (gpio >= MAX_NUM_GPIOS)
|
||||
return -1;
|
||||
struct tegra_port_info *state = dev_get_priv(dev);
|
||||
|
||||
if (label != NULL) {
|
||||
strncpy(gpio_names[gpio].name, label, GPIO_NAME_SIZE);
|
||||
gpio_names[gpio].name[GPIO_NAME_SIZE - 1] = '\0';
|
||||
}
|
||||
if (*state->label[offset])
|
||||
return -EBUSY;
|
||||
|
||||
strncpy(state->label[offset], label, GPIO_NAME_SIZE);
|
||||
state->label[offset][GPIO_NAME_SIZE - 1] = '\0';
|
||||
|
||||
/* Configure as a GPIO */
|
||||
set_config(gpio, 1);
|
||||
set_config(state->base_gpio + offset, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpio_free(unsigned gpio)
|
||||
static int tegra_gpio_free(struct udevice *dev, unsigned offset)
|
||||
{
|
||||
if (gpio >= MAX_NUM_GPIOS)
|
||||
return -1;
|
||||
struct tegra_port_info *state = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = check_reserved(dev, offset, __func__);
|
||||
if (ret)
|
||||
return ret;
|
||||
state->label[offset][0] = '\0';
|
||||
|
||||
gpio_names[gpio].name[0] = '\0';
|
||||
/* Do not configure as input or change pin mux here */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* read GPIO OUT value of pin 'gpio' */
|
||||
static int gpio_get_output_value(unsigned gpio)
|
||||
static int tegra_gpio_get_output_value(unsigned gpio)
|
||||
{
|
||||
struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
|
||||
struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)];
|
||||
@@ -166,24 +211,34 @@ static int gpio_get_output_value(unsigned gpio)
|
||||
return (val >> GPIO_BIT(gpio)) & 1;
|
||||
}
|
||||
|
||||
|
||||
/* set GPIO pin 'gpio' as an input */
|
||||
int gpio_direction_input(unsigned gpio)
|
||||
static int tegra_gpio_direction_input(struct udevice *dev, unsigned offset)
|
||||
{
|
||||
debug("gpio_direction_input: pin = %d (port %d:bit %d)\n",
|
||||
gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio));
|
||||
struct tegra_port_info *state = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = check_reserved(dev, offset, __func__);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Configure GPIO direction as input. */
|
||||
set_direction(gpio, 0);
|
||||
set_direction(state->base_gpio + offset, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set GPIO pin 'gpio' as an output, with polarity 'value' */
|
||||
int gpio_direction_output(unsigned gpio, int value)
|
||||
static int tegra_gpio_direction_output(struct udevice *dev, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
debug("gpio_direction_output: pin = %d (port %d:bit %d) = %s\n",
|
||||
gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio),
|
||||
value ? "HIGH" : "LOW");
|
||||
struct tegra_port_info *state = dev_get_priv(dev);
|
||||
int gpio = state->base_gpio + offset;
|
||||
int ret;
|
||||
|
||||
ret = check_reserved(dev, offset, __func__);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Configure GPIO output value. */
|
||||
set_level(gpio, value);
|
||||
@@ -195,25 +250,38 @@ int gpio_direction_output(unsigned gpio, int value)
|
||||
}
|
||||
|
||||
/* read GPIO IN value of pin 'gpio' */
|
||||
int gpio_get_value(unsigned gpio)
|
||||
static int tegra_gpio_get_value(struct udevice *dev, unsigned offset)
|
||||
{
|
||||
struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
|
||||
struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)];
|
||||
struct tegra_port_info *state = dev_get_priv(dev);
|
||||
int gpio = state->base_gpio + offset;
|
||||
int ret;
|
||||
int val;
|
||||
|
||||
debug("gpio_get_value: pin = %d (port %d:bit %d)\n",
|
||||
gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio));
|
||||
ret = check_reserved(dev, offset, __func__);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = readl(&bank->gpio_in[GPIO_PORT(gpio)]);
|
||||
debug("%s: pin = %d (port %d:bit %d)\n", __func__,
|
||||
gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio));
|
||||
|
||||
val = readl(&state->bank->gpio_in[GPIO_PORT(gpio)]);
|
||||
|
||||
return (val >> GPIO_BIT(gpio)) & 1;
|
||||
}
|
||||
|
||||
/* write GPIO OUT value to pin 'gpio' */
|
||||
int gpio_set_value(unsigned gpio, int value)
|
||||
static int tegra_gpio_set_value(struct udevice *dev, unsigned offset, int value)
|
||||
{
|
||||
struct tegra_port_info *state = dev_get_priv(dev);
|
||||
int gpio = state->base_gpio + offset;
|
||||
int ret;
|
||||
|
||||
ret = check_reserved(dev, offset, __func__);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
debug("gpio_set_value: pin = %d (port %d:bit %d), value = %d\n",
|
||||
gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio), value);
|
||||
gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio), value);
|
||||
|
||||
/* Configure GPIO output value. */
|
||||
set_level(gpio, value);
|
||||
@@ -241,26 +309,175 @@ void gpio_config_table(const struct tegra_gpio_config *config, int len)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Display Tegra GPIO information
|
||||
*/
|
||||
void gpio_info(void)
|
||||
static int tegra_gpio_get_function(struct udevice *dev, unsigned offset)
|
||||
{
|
||||
unsigned c;
|
||||
int type;
|
||||
struct tegra_port_info *state = dev_get_priv(dev);
|
||||
int gpio = state->base_gpio + offset;
|
||||
|
||||
for (c = 0; c < MAX_NUM_GPIOS; c++) {
|
||||
type = get_config(c); /* GPIO, not SFPIO */
|
||||
if (type) {
|
||||
printf("GPIO_%d:\t%s is an %s, ", c,
|
||||
get_name(c),
|
||||
get_direction(c) ? "OUTPUT" : "INPUT");
|
||||
if (get_direction(c))
|
||||
printf("value = %d", gpio_get_output_value(c));
|
||||
else
|
||||
printf("value = %d", gpio_get_value(c));
|
||||
printf("\n");
|
||||
} else
|
||||
continue;
|
||||
}
|
||||
if (!*state->label[offset])
|
||||
return GPIOF_UNUSED;
|
||||
if (!get_config(gpio))
|
||||
return GPIOF_FUNC;
|
||||
else if (get_direction(gpio))
|
||||
return GPIOF_OUTPUT;
|
||||
else
|
||||
return GPIOF_INPUT;
|
||||
}
|
||||
|
||||
static int tegra_gpio_get_state(struct udevice *dev, unsigned int offset,
|
||||
char *buf, int bufsize)
|
||||
{
|
||||
struct gpio_dev_priv *uc_priv = dev->uclass_priv;
|
||||
struct tegra_port_info *state = dev_get_priv(dev);
|
||||
int gpio = state->base_gpio + offset;
|
||||
const char *label;
|
||||
int is_output;
|
||||
int is_gpio;
|
||||
int size;
|
||||
|
||||
label = state->label[offset];
|
||||
is_gpio = get_config(gpio); /* GPIO, not SFPIO */
|
||||
size = snprintf(buf, bufsize, "%s%d: ",
|
||||
uc_priv->bank_name ? uc_priv->bank_name : "", offset);
|
||||
buf += size;
|
||||
bufsize -= size;
|
||||
if (is_gpio) {
|
||||
is_output = get_direction(gpio);
|
||||
|
||||
snprintf(buf, bufsize, "%s: %d [%c]%s%s",
|
||||
is_output ? "out" : " in",
|
||||
is_output ?
|
||||
tegra_gpio_get_output_value(gpio) :
|
||||
tegra_gpio_get_value(dev, offset),
|
||||
*label ? 'x' : ' ',
|
||||
*label ? " " : "",
|
||||
label);
|
||||
} else {
|
||||
snprintf(buf, bufsize, "sfpio");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_gpio_ops gpio_tegra_ops = {
|
||||
.request = tegra_gpio_request,
|
||||
.free = tegra_gpio_free,
|
||||
.direction_input = tegra_gpio_direction_input,
|
||||
.direction_output = tegra_gpio_direction_output,
|
||||
.get_value = tegra_gpio_get_value,
|
||||
.set_value = tegra_gpio_set_value,
|
||||
.get_function = tegra_gpio_get_function,
|
||||
.get_state = tegra_gpio_get_state,
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the name of a GPIO port
|
||||
*
|
||||
* GPIOs are named A, B, C, ..., Z, AA, BB, CC, ...
|
||||
*
|
||||
* @base_port: Base port number (0, 1..n-1)
|
||||
* @return allocated string containing the name
|
||||
*/
|
||||
static char *gpio_port_name(int base_port)
|
||||
{
|
||||
char *name, *s;
|
||||
|
||||
name = malloc(3);
|
||||
if (name) {
|
||||
s = name;
|
||||
*s++ = 'A' + (base_port % 26);
|
||||
if (base_port >= 26)
|
||||
*s++ = *name;
|
||||
*s = '\0';
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
static const struct udevice_id tegra_gpio_ids[] = {
|
||||
{ .compatible = "nvidia,tegra30-gpio" },
|
||||
{ .compatible = "nvidia,tegra20-gpio" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static int gpio_tegra_probe(struct udevice *dev)
|
||||
{
|
||||
struct gpio_dev_priv *uc_priv = dev->uclass_priv;
|
||||
struct tegra_port_info *priv = dev->priv;
|
||||
struct tegra_gpio_platdata *plat = dev->platdata;
|
||||
|
||||
/* Only child devices have ports */
|
||||
if (!plat)
|
||||
return 0;
|
||||
|
||||
priv->bank = plat->bank;
|
||||
priv->base_gpio = plat->base_gpio;
|
||||
|
||||
uc_priv->gpio_count = TEGRA_GPIOS_PER_PORT;
|
||||
uc_priv->bank_name = plat->port_name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* We have a top-level GPIO device with no actual GPIOs. It has a child
|
||||
* device for each Tegra port.
|
||||
*/
|
||||
static int gpio_tegra_bind(struct udevice *parent)
|
||||
{
|
||||
struct tegra_gpio_platdata *plat = parent->platdata;
|
||||
struct gpio_ctlr *ctlr;
|
||||
int bank_count;
|
||||
int bank;
|
||||
int ret;
|
||||
int len;
|
||||
|
||||
/* If this is a child device, there is nothing to do here */
|
||||
if (plat)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* This driver does not make use of interrupts, other than to figure
|
||||
* out the number of GPIO banks
|
||||
*/
|
||||
if (!fdt_getprop(gd->fdt_blob, parent->of_offset, "interrupts", &len))
|
||||
return -EINVAL;
|
||||
bank_count = len / 3 / sizeof(u32);
|
||||
ctlr = (struct gpio_ctlr *)fdtdec_get_addr(gd->fdt_blob,
|
||||
parent->of_offset, "reg");
|
||||
for (bank = 0; bank < bank_count; bank++) {
|
||||
int port;
|
||||
|
||||
for (port = 0; port < TEGRA_PORTS_PER_BANK; port++) {
|
||||
struct tegra_gpio_platdata *plat;
|
||||
struct udevice *dev;
|
||||
int base_port;
|
||||
|
||||
plat = calloc(1, sizeof(*plat));
|
||||
if (!plat)
|
||||
return -ENOMEM;
|
||||
plat->bank = &ctlr->gpio_bank[bank];
|
||||
base_port = bank * TEGRA_PORTS_PER_BANK + port;
|
||||
plat->base_gpio = TEGRA_GPIOS_PER_PORT * base_port;
|
||||
plat->port_name = gpio_port_name(base_port);
|
||||
|
||||
ret = device_bind(parent, parent->driver,
|
||||
plat->port_name, plat, -1, &dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
dev->of_offset = parent->of_offset;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(gpio_tegra) = {
|
||||
.name = "gpio_tegra",
|
||||
.id = UCLASS_GPIO,
|
||||
.of_match = tegra_gpio_ids,
|
||||
.bind = gpio_tegra_bind,
|
||||
.probe = gpio_tegra_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct tegra_port_info),
|
||||
.ops = &gpio_tegra_ops,
|
||||
};
|
||||
|
||||
@@ -5,7 +5,12 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
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
|
||||
obj-$(CONFIG_ALTERA_JTAG_UART) += altera_jtag_uart.o
|
||||
@@ -16,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
|
||||
@@ -34,6 +38,7 @@ obj-$(CONFIG_BFIN_SERIAL) += serial_bfin.o
|
||||
obj-$(CONFIG_FSL_LPUART) += serial_lpuart.o
|
||||
obj-$(CONFIG_MXS_AUART) += mxs_auart.o
|
||||
obj-$(CONFIG_ARC_SERIAL) += serial_arc.o
|
||||
obj-$(CONFIG_TEGRA_SERIAL) += serial_tegra.o
|
||||
|
||||
ifndef CONFIG_SPL_BUILD
|
||||
obj-$(CONFIG_USB_TTY) += usbtty.o
|
||||
|
||||
@@ -4,18 +4,26 @@
|
||||
* modified to use CONFIG_SYS_ISA_MEM and new defines
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#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,82 @@
|
||||
#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;
|
||||
|
||||
#ifdef CONFIG_OMAP1510
|
||||
/* If can't cleanly clock 115200 set div to 1 */
|
||||
if ((clock == 12000000) && (baudrate == 115200)) {
|
||||
port->osc_12m_sel = OSC_12M_SEL; /* enable 6.5 * divisor */
|
||||
return 1; /* return 1 for base divisor */
|
||||
}
|
||||
port->osc_12m_sel = 0; /* clear if previsouly set */
|
||||
#endif
|
||||
|
||||
return DIV_ROUND_CLOSEST(clock, mode_x_div * baudrate);
|
||||
}
|
||||
|
||||
static void NS16550_setbrg(NS16550_t com_port, int baud_divisor)
|
||||
{
|
||||
serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr);
|
||||
serial_out(baud_divisor & 0xff, &com_port->dll);
|
||||
serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm);
|
||||
serial_out(UART_LCRVAL, &com_port->lcr);
|
||||
}
|
||||
|
||||
void NS16550_init(NS16550_t com_port, int baud_divisor)
|
||||
{
|
||||
#if (defined(CONFIG_SPL_BUILD) && defined(CONFIG_OMAP34XX))
|
||||
@@ -55,10 +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) {
|
||||
serial_out(UART_LCR_DLAB, &com_port->lcr);
|
||||
serial_out(baud_divisor & 0xff, &com_port->dll);
|
||||
serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm);
|
||||
serial_out(UART_LCRVAL, &com_port->lcr);
|
||||
if (baud_divisor != -1)
|
||||
NS16550_setbrg(com_port, baud_divisor);
|
||||
serial_out(0, &com_port->mdr1);
|
||||
}
|
||||
#endif
|
||||
@@ -71,16 +154,11 @@ void NS16550_init(NS16550_t com_port, int baud_divisor)
|
||||
defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX)
|
||||
serial_out(0x7, &com_port->mdr1); /* mode select reset TL16C750*/
|
||||
#endif
|
||||
serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr);
|
||||
serial_out(0, &com_port->dll);
|
||||
serial_out(0, &com_port->dlm);
|
||||
serial_out(UART_LCRVAL, &com_port->lcr);
|
||||
NS16550_setbrg(com_port, 0);
|
||||
serial_out(UART_MCRVAL, &com_port->mcr);
|
||||
serial_out(UART_FCRVAL, &com_port->fcr);
|
||||
serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr);
|
||||
serial_out(baud_divisor & 0xff, &com_port->dll);
|
||||
serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm);
|
||||
serial_out(UART_LCRVAL, &com_port->lcr);
|
||||
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)
|
||||
@@ -97,16 +175,10 @@ void NS16550_init(NS16550_t com_port, int baud_divisor)
|
||||
void NS16550_reinit(NS16550_t com_port, int baud_divisor)
|
||||
{
|
||||
serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier);
|
||||
serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr);
|
||||
serial_out(0, &com_port->dll);
|
||||
serial_out(0, &com_port->dlm);
|
||||
serial_out(UART_LCRVAL, &com_port->lcr);
|
||||
NS16550_setbrg(com_port, 0);
|
||||
serial_out(UART_MCRVAL, &com_port->mcr);
|
||||
serial_out(UART_FCRVAL, &com_port->fcr);
|
||||
serial_out(UART_LCR_BKSE, &com_port->lcr);
|
||||
serial_out(baud_divisor & 0xff, &com_port->dll);
|
||||
serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm);
|
||||
serial_out(UART_LCRVAL, &com_port->lcr);
|
||||
NS16550_setbrg(com_port, baud_divisor);
|
||||
}
|
||||
#endif /* CONFIG_NS16550_MIN_FUNCTIONS */
|
||||
|
||||
@@ -145,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 */
|
||||
|
||||
@@ -11,12 +11,16 @@
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <fdtdec.h>
|
||||
#include <lcd.h>
|
||||
#include <os.h>
|
||||
#include <serial.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <asm/state.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/*
|
||||
*
|
||||
* serial_buf: A buffer that holds keyboard characters for the
|
||||
@@ -30,27 +34,69 @@ static char serial_buf[16];
|
||||
static unsigned int serial_buf_write;
|
||||
static unsigned int serial_buf_read;
|
||||
|
||||
static int sandbox_serial_init(void)
|
||||
struct sandbox_serial_platdata {
|
||||
int colour; /* Text colour to use for output, -1 for none */
|
||||
};
|
||||
|
||||
struct sandbox_serial_priv {
|
||||
bool start_of_line;
|
||||
};
|
||||
|
||||
/**
|
||||
* output_ansi_colour() - Output an ANSI colour code
|
||||
*
|
||||
* @colour: Colour to output (0-7)
|
||||
*/
|
||||
static void output_ansi_colour(int colour)
|
||||
{
|
||||
char ansi_code[] = "\x1b[1;3Xm";
|
||||
|
||||
ansi_code[5] = '0' + colour;
|
||||
os_write(1, ansi_code, sizeof(ansi_code) - 1);
|
||||
}
|
||||
|
||||
static void output_ansi_reset(void)
|
||||
{
|
||||
os_write(1, "\x1b[0m", 4);
|
||||
}
|
||||
|
||||
static int sandbox_serial_probe(struct udevice *dev)
|
||||
{
|
||||
struct sandbox_state *state = state_get_current();
|
||||
struct sandbox_serial_priv *priv = dev_get_priv(dev);
|
||||
|
||||
if (state->term_raw != STATE_TERM_COOKED)
|
||||
os_tty_raw(0, state->term_raw == STATE_TERM_RAW_WITH_SIGS);
|
||||
priv->start_of_line = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sandbox_serial_setbrg(void)
|
||||
static int sandbox_serial_remove(struct udevice *dev)
|
||||
{
|
||||
struct sandbox_serial_platdata *plat = dev->platdata;
|
||||
|
||||
if (plat->colour != -1)
|
||||
output_ansi_reset();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sandbox_serial_putc(const char ch)
|
||||
static int sandbox_serial_putc(struct udevice *dev, const char ch)
|
||||
{
|
||||
struct sandbox_serial_priv *priv = dev_get_priv(dev);
|
||||
struct sandbox_serial_platdata *plat = dev->platdata;
|
||||
|
||||
if (priv->start_of_line && plat->colour != -1) {
|
||||
priv->start_of_line = false;
|
||||
output_ansi_colour(plat->colour);
|
||||
}
|
||||
|
||||
os_write(1, &ch, 1);
|
||||
}
|
||||
if (ch == '\n')
|
||||
priv->start_of_line = true;
|
||||
|
||||
static void sandbox_serial_puts(const char *str)
|
||||
{
|
||||
os_write(1, str, strlen(str));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int increment_buffer_index(unsigned int index)
|
||||
@@ -58,12 +104,15 @@ static unsigned int increment_buffer_index(unsigned int index)
|
||||
return (index + 1) % ARRAY_SIZE(serial_buf);
|
||||
}
|
||||
|
||||
static int sandbox_serial_tstc(void)
|
||||
static int sandbox_serial_pending(struct udevice *dev, bool input)
|
||||
{
|
||||
const unsigned int next_index =
|
||||
increment_buffer_index(serial_buf_write);
|
||||
ssize_t count;
|
||||
|
||||
if (!input)
|
||||
return 0;
|
||||
|
||||
os_usleep(100);
|
||||
#ifdef CONFIG_LCD
|
||||
lcd_sync();
|
||||
@@ -74,38 +123,77 @@ static int sandbox_serial_tstc(void)
|
||||
count = os_read_no_block(0, &serial_buf[serial_buf_write], 1);
|
||||
if (count == 1)
|
||||
serial_buf_write = next_index;
|
||||
|
||||
return serial_buf_write != serial_buf_read;
|
||||
}
|
||||
|
||||
static int sandbox_serial_getc(void)
|
||||
static int sandbox_serial_getc(struct udevice *dev)
|
||||
{
|
||||
int result;
|
||||
|
||||
while (!sandbox_serial_tstc())
|
||||
; /* buffer empty */
|
||||
if (!sandbox_serial_pending(dev, true))
|
||||
return -EAGAIN; /* buffer empty */
|
||||
|
||||
result = serial_buf[serial_buf_read];
|
||||
serial_buf_read = increment_buffer_index(serial_buf_read);
|
||||
return result;
|
||||
}
|
||||
|
||||
static struct serial_device sandbox_serial_drv = {
|
||||
.name = "sandbox_serial",
|
||||
.start = sandbox_serial_init,
|
||||
.stop = NULL,
|
||||
.setbrg = sandbox_serial_setbrg,
|
||||
.putc = sandbox_serial_putc,
|
||||
.puts = sandbox_serial_puts,
|
||||
.getc = sandbox_serial_getc,
|
||||
.tstc = sandbox_serial_tstc,
|
||||
static const char * const ansi_colour[] = {
|
||||
"black", "red", "green", "yellow", "blue", "megenta", "cyan",
|
||||
"white",
|
||||
};
|
||||
|
||||
void sandbox_serial_initialize(void)
|
||||
static int sandbox_serial_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
serial_register(&sandbox_serial_drv);
|
||||
struct sandbox_serial_platdata *plat = dev->platdata;
|
||||
const char *colour;
|
||||
int i;
|
||||
|
||||
plat->colour = -1;
|
||||
colour = fdt_getprop(gd->fdt_blob, dev->of_offset,
|
||||
"sandbox,text-colour", NULL);
|
||||
if (colour) {
|
||||
for (i = 0; i < ARRAY_SIZE(ansi_colour); i++) {
|
||||
if (!strcmp(colour, ansi_colour[i])) {
|
||||
plat->colour = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__weak struct serial_device *default_serial_console(void)
|
||||
{
|
||||
return &sandbox_serial_drv;
|
||||
}
|
||||
static const struct dm_serial_ops sandbox_serial_ops = {
|
||||
.putc = sandbox_serial_putc,
|
||||
.pending = sandbox_serial_pending,
|
||||
.getc = sandbox_serial_getc,
|
||||
};
|
||||
|
||||
static const struct udevice_id sandbox_serial_ids[] = {
|
||||
{ .compatible = "sandbox,serial" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(serial_sandbox) = {
|
||||
.name = "serial_sandbox",
|
||||
.id = UCLASS_SERIAL,
|
||||
.of_match = sandbox_serial_ids,
|
||||
.ofdata_to_platdata = sandbox_serial_ofdata_to_platdata,
|
||||
.platdata_auto_alloc_size = sizeof(struct sandbox_serial_platdata),
|
||||
.priv_auto_alloc_size = sizeof(struct sandbox_serial_priv),
|
||||
.probe = sandbox_serial_probe,
|
||||
.remove = sandbox_serial_remove,
|
||||
.ops = &sandbox_serial_ops,
|
||||
.flags = DM_FLAG_PRE_RELOC,
|
||||
};
|
||||
|
||||
static const struct sandbox_serial_platdata platdata_non_fdt = {
|
||||
.colour = -1,
|
||||
};
|
||||
|
||||
U_BOOT_DEVICE(serial_sandbox_non_fdt) = {
|
||||
.name = "serial_sandbox",
|
||||
.platdata = &platdata_non_fdt,
|
||||
};
|
||||
|
||||
213
drivers/serial/serial-uclass.c
Normal file
213
drivers/serial/serial-uclass.c
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The Chromium OS Authors.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <os.h>
|
||||
#include <serial.h>
|
||||
#include <stdio_dev.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/device-internal.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/* The currently-selected console serial device */
|
||||
struct udevice *cur_dev __attribute__ ((section(".data")));
|
||||
|
||||
#ifndef CONFIG_SYS_MALLOC_F_LEN
|
||||
#error "Serial is required before relocation - define CONFIG_SYS_MALLOC_F_LEN to make this work"
|
||||
#endif
|
||||
|
||||
static void serial_find_console_or_panic(void)
|
||||
{
|
||||
int node;
|
||||
|
||||
/* Check for a chosen console */
|
||||
node = fdtdec_get_chosen_node(gd->fdt_blob, "stdout-path");
|
||||
if (node < 0)
|
||||
node = fdtdec_get_alias_node(gd->fdt_blob, "console");
|
||||
if (!uclass_get_device_by_of_offset(UCLASS_SERIAL, node, &cur_dev))
|
||||
return;
|
||||
|
||||
/*
|
||||
* If the console is not marked to be bound before relocation, bind
|
||||
* it anyway.
|
||||
*/
|
||||
if (node > 0 &&
|
||||
!lists_bind_fdt(gd->dm_root, gd->fdt_blob, node, &cur_dev)) {
|
||||
if (!device_probe(cur_dev))
|
||||
return;
|
||||
cur_dev = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Failing that, get the device with sequence number 0, or in extremis
|
||||
* just the first serial device we can find. But we insist on having
|
||||
* a console (even if it is silent).
|
||||
*/
|
||||
if (uclass_get_device_by_seq(UCLASS_SERIAL, 0, &cur_dev) &&
|
||||
(uclass_first_device(UCLASS_SERIAL, &cur_dev) || !cur_dev))
|
||||
panic("No serial driver found");
|
||||
}
|
||||
|
||||
/* Called prior to relocation */
|
||||
int serial_init(void)
|
||||
{
|
||||
serial_find_console_or_panic();
|
||||
gd->flags |= GD_FLG_SERIAL_READY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called after relocation */
|
||||
void serial_initialize(void)
|
||||
{
|
||||
serial_find_console_or_panic();
|
||||
}
|
||||
|
||||
void serial_putc(char ch)
|
||||
{
|
||||
struct dm_serial_ops *ops = serial_get_ops(cur_dev);
|
||||
int err;
|
||||
|
||||
do {
|
||||
err = ops->putc(cur_dev, ch);
|
||||
} while (err == -EAGAIN);
|
||||
if (ch == '\n')
|
||||
serial_putc('\r');
|
||||
}
|
||||
|
||||
void serial_setbrg(void)
|
||||
{
|
||||
struct dm_serial_ops *ops = serial_get_ops(cur_dev);
|
||||
|
||||
if (ops->setbrg)
|
||||
ops->setbrg(cur_dev, gd->baudrate);
|
||||
}
|
||||
|
||||
void serial_puts(const char *str)
|
||||
{
|
||||
while (*str)
|
||||
serial_putc(*str++);
|
||||
}
|
||||
|
||||
int serial_tstc(void)
|
||||
{
|
||||
struct dm_serial_ops *ops = serial_get_ops(cur_dev);
|
||||
|
||||
if (ops->pending)
|
||||
return ops->pending(cur_dev, true);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int serial_getc(void)
|
||||
{
|
||||
struct dm_serial_ops *ops = serial_get_ops(cur_dev);
|
||||
int err;
|
||||
|
||||
do {
|
||||
err = ops->getc(cur_dev);
|
||||
} while (err == -EAGAIN);
|
||||
|
||||
return err >= 0 ? err : 0;
|
||||
}
|
||||
|
||||
void serial_stdio_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
void serial_stub_putc(struct stdio_dev *sdev, const char ch)
|
||||
{
|
||||
struct udevice *dev = sdev->priv;
|
||||
struct dm_serial_ops *ops = serial_get_ops(dev);
|
||||
|
||||
ops->putc(dev, ch);
|
||||
}
|
||||
|
||||
void serial_stub_puts(struct stdio_dev *sdev, const char *str)
|
||||
{
|
||||
while (*str)
|
||||
serial_stub_putc(sdev, *str++);
|
||||
}
|
||||
|
||||
int serial_stub_getc(struct stdio_dev *sdev)
|
||||
{
|
||||
struct udevice *dev = sdev->priv;
|
||||
struct dm_serial_ops *ops = serial_get_ops(dev);
|
||||
|
||||
int err;
|
||||
|
||||
do {
|
||||
err = ops->getc(dev);
|
||||
} while (err == -EAGAIN);
|
||||
|
||||
return err >= 0 ? err : 0;
|
||||
}
|
||||
|
||||
int serial_stub_tstc(struct stdio_dev *sdev)
|
||||
{
|
||||
struct udevice *dev = sdev->priv;
|
||||
struct dm_serial_ops *ops = serial_get_ops(dev);
|
||||
|
||||
if (ops->pending)
|
||||
return ops->pending(dev, true);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int serial_post_probe(struct udevice *dev)
|
||||
{
|
||||
struct stdio_dev sdev;
|
||||
struct dm_serial_ops *ops = serial_get_ops(dev);
|
||||
struct serial_dev_priv *upriv = dev->uclass_priv;
|
||||
int ret;
|
||||
|
||||
/* Set the baud rate */
|
||||
if (ops->setbrg) {
|
||||
ret = ops->setbrg(dev, gd->baudrate);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!(gd->flags & GD_FLG_RELOC))
|
||||
return 0;
|
||||
|
||||
memset(&sdev, '\0', sizeof(sdev));
|
||||
|
||||
strncpy(sdev.name, dev->name, sizeof(sdev.name));
|
||||
sdev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT;
|
||||
sdev.priv = dev;
|
||||
sdev.putc = serial_stub_putc;
|
||||
sdev.puts = serial_stub_puts;
|
||||
sdev.getc = serial_stub_getc;
|
||||
sdev.tstc = serial_stub_tstc;
|
||||
stdio_register_dev(&sdev, &upriv->sdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serial_pre_remove(struct udevice *dev)
|
||||
{
|
||||
#ifdef CONFIG_SYS_STDIO_DEREGISTER
|
||||
struct serial_dev_priv *upriv = dev->uclass_priv;
|
||||
|
||||
if (stdio_deregister_dev(upriv->sdev))
|
||||
return -EPERM;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(serial) = {
|
||||
.id = UCLASS_SERIAL,
|
||||
.name = "serial",
|
||||
.post_probe = serial_post_probe,
|
||||
.pre_remove = serial_pre_remove,
|
||||
.per_device_auto_alloc_size = sizeof(struct serial_dev_priv),
|
||||
};
|
||||
@@ -320,6 +320,7 @@ void serial_stdio_init(void)
|
||||
dev.puts = serial_stub_puts;
|
||||
dev.getc = serial_stub_getc;
|
||||
dev.tstc = serial_stub_tstc;
|
||||
dev.priv = s;
|
||||
|
||||
stdio_register(&dev);
|
||||
|
||||
|
||||
@@ -81,7 +81,8 @@ static NS16550_t serial_ports[6] = {
|
||||
static int eserial##port##_init(void) \
|
||||
{ \
|
||||
int clock_divisor; \
|
||||
clock_divisor = calc_divisor(serial_ports[port-1]); \
|
||||
clock_divisor = ns16550_calc_divisor(serial_ports[port-1], \
|
||||
CONFIG_SYS_NS16550_CLK, gd->baudrate); \
|
||||
NS16550_init(serial_ports[port-1], clock_divisor); \
|
||||
return 0 ; \
|
||||
} \
|
||||
@@ -118,14 +119,6 @@ static NS16550_t serial_ports[6] = {
|
||||
.puts = eserial##port##_puts, \
|
||||
}
|
||||
|
||||
static int calc_divisor (NS16550_t port)
|
||||
{
|
||||
const unsigned int mode_x_div = 16;
|
||||
|
||||
return DIV_ROUND_CLOSEST(CONFIG_SYS_NS16550_CLK,
|
||||
mode_x_div * gd->baudrate);
|
||||
}
|
||||
|
||||
void
|
||||
_serial_putc(const char c,const int port)
|
||||
{
|
||||
@@ -167,7 +160,8 @@ _serial_setbrg (const int port)
|
||||
{
|
||||
int clock_divisor;
|
||||
|
||||
clock_divisor = calc_divisor(PORT);
|
||||
clock_divisor = ns16550_calc_divisor(PORT, CONFIG_SYS_NS16550_CLK,
|
||||
gd->baudrate);
|
||||
NS16550_reinit(PORT, clock_divisor);
|
||||
}
|
||||
|
||||
|
||||
38
drivers/serial/serial_tegra.c
Normal file
38
drivers/serial/serial_tegra.c
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Google, Inc
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <ns16550.h>
|
||||
#include <serial.h>
|
||||
|
||||
static const struct udevice_id tegra_serial_ids[] = {
|
||||
{ .compatible = "nvidia,tegra20-uart" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static int tegra_serial_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct ns16550_platdata *plat = dev_get_platdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = ns16550_serial_ofdata_to_platdata(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
plat->clock = V_NS16550_CLK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
U_BOOT_DRIVER(serial_ns16550) = {
|
||||
.name = "serial_tegra20",
|
||||
.id = UCLASS_SERIAL,
|
||||
.of_match = tegra_serial_ids,
|
||||
.ofdata_to_platdata = tegra_serial_ofdata_to_platdata,
|
||||
.platdata_auto_alloc_size = sizeof(struct ns16550_platdata),
|
||||
.priv_auto_alloc_size = sizeof(struct NS16550),
|
||||
.probe = ns16550_serial_probe,
|
||||
.ops = &ns16550_serial_ops,
|
||||
};
|
||||
Reference in New Issue
Block a user