Serial devices support simple byte input/output and a few operations to find out whether data is available. Add a basic uclass for serial devices to be used by drivers that are converted to driver model. Signed-off-by: Simon Glass <sjg@chromium.org>master
parent
1f359e3611
commit
57d92753d4
@ -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), |
||||
}; |
Loading…
Reference in new issue