serial-uclass.c 6.5 KB
Newer Older
1 2 3 4 5 6 7 8
/*
 * Copyright (c) 2014 The Chromium OS Authors.
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <common.h>
#include <dm.h>
9
#include <environment.h>
10 11 12 13 14
#include <errno.h>
#include <fdtdec.h>
#include <os.h>
#include <serial.h>
#include <stdio_dev.h>
15
#include <watchdog.h>
16 17 18 19 20
#include <dm/lists.h>
#include <dm/device-internal.h>

DECLARE_GLOBAL_DATA_PTR;

21 22 23 24 25
/*
 * Table with supported baudrates (defined in config_xyz.h)
 */
static const unsigned long baudrate_table[] = CONFIG_SYS_BAUDRATE_TABLE;

26 27 28 29 30 31
#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)
{
32
	struct udevice *dev;
33 34
	int node;

35
	if (OF_CONTROL && gd->fdt_blob) {
36 37 38 39 40 41
		/* Check for a chosen console */
		node = fdtdec_get_chosen_node(gd->fdt_blob, "stdout-path");
		if (node < 0)
			node = fdt_path_offset(gd->fdt_blob, "console");
		if (!uclass_get_device_by_of_offset(UCLASS_SERIAL, node,
						    &dev)) {
42
			gd->cur_serial_dev = dev;
43
			return;
44
		}
45 46 47 48 49 50 51 52 53 54 55 56

		/*
		* 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, &dev)) {
			if (!device_probe(dev)) {
				gd->cur_serial_dev = dev;
				return;
			}
		}
57 58
	}
	if (!SPL_BUILD || !OF_CONTROL || !gd->fdt_blob) {
59 60 61 62 63 64 65 66
		/*
		* Try to use CONFIG_CONS_INDEX if available (it is numbered
		* from 1!).
		*
		* 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).
		*/
67 68 69 70 71
#ifdef CONFIG_CONS_INDEX
#define INDEX (CONFIG_CONS_INDEX - 1)
#else
#define INDEX 0
#endif
72 73
		if (!uclass_get_device_by_seq(UCLASS_SERIAL, INDEX, &dev) ||
		    !uclass_get_device(UCLASS_SERIAL, INDEX, &dev) ||
74
		    (!uclass_first_device(UCLASS_SERIAL, &dev) && dev)) {
75 76 77
			gd->cur_serial_dev = dev;
			return;
		}
78
#undef INDEX
79 80 81
	}

	panic_str("No serial driver found");
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
}

/* 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();
}

99
static void _serial_putc(struct udevice *dev, char ch)
100
{
M
Masahiro Yamada 已提交
101
	struct dm_serial_ops *ops = serial_get_ops(dev);
102 103 104
	int err;

	do {
M
Masahiro Yamada 已提交
105
		err = ops->putc(dev, ch);
106 107
	} while (err == -EAGAIN);
	if (ch == '\n')
108
		_serial_putc(dev, '\r');
109 110
}

111
static void _serial_puts(struct udevice *dev, const char *str)
112
{
113 114
	while (*str)
		_serial_putc(dev, *str++);
115 116
}

117
static int _serial_getc(struct udevice *dev)
118
{
119 120
	struct dm_serial_ops *ops = serial_get_ops(dev);
	int err;
121

122 123 124 125 126
	do {
		err = ops->getc(dev);
		if (err == -EAGAIN)
			WATCHDOG_RESET();
	} while (err == -EAGAIN);
127

128
	return err >= 0 ? err : 0;
129 130
}

131
static int _serial_tstc(struct udevice *dev)
132
{
133
	struct dm_serial_ops *ops = serial_get_ops(dev);
134 135

	if (ops->pending)
136
		return ops->pending(dev, true);
137 138 139 140

	return 1;
}

141
void serial_putc(char ch)
142
{
143
	_serial_putc(gd->cur_serial_dev, ch);
144
}
145

146 147
void serial_puts(const char *str)
{
148
	_serial_puts(gd->cur_serial_dev, str);
149 150
}

151 152
int serial_getc(void)
{
153
	return _serial_getc(gd->cur_serial_dev);
154 155 156 157
}

int serial_tstc(void)
{
158
	return _serial_tstc(gd->cur_serial_dev);
159 160 161 162
}

void serial_setbrg(void)
{
163
	struct dm_serial_ops *ops = serial_get_ops(gd->cur_serial_dev);
164 165

	if (ops->setbrg)
166
		ops->setbrg(gd->cur_serial_dev, gd->baudrate);
167 168
}

169 170 171 172
void serial_stdio_init(void)
{
}

173
#ifdef CONFIG_DM_STDIO
174
static void serial_stub_putc(struct stdio_dev *sdev, const char ch)
175
{
176
	_serial_putc(sdev->priv, ch);
177
}
178
#endif
179 180 181

void serial_stub_puts(struct stdio_dev *sdev, const char *str)
{
182
	_serial_puts(sdev->priv, str);
183 184 185 186
}

int serial_stub_getc(struct stdio_dev *sdev)
{
187
	return _serial_getc(sdev->priv);
188 189 190 191
}

int serial_stub_tstc(struct stdio_dev *sdev)
{
192
	return _serial_tstc(sdev->priv);
193 194
}

195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
/**
 * on_baudrate() - Update the actual baudrate when the env var changes
 *
 * This will check for a valid baudrate and only apply it if valid.
 */
static int on_baudrate(const char *name, const char *value, enum env_op op,
	int flags)
{
	int i;
	int baudrate;

	switch (op) {
	case env_op_create:
	case env_op_overwrite:
		/*
		 * Switch to new baudrate if new baudrate is supported
		 */
		baudrate = simple_strtoul(value, NULL, 10);

		/* Not actually changing */
		if (gd->baudrate == baudrate)
			return 0;

		for (i = 0; i < ARRAY_SIZE(baudrate_table); ++i) {
			if (baudrate == baudrate_table[i])
				break;
		}
		if (i == ARRAY_SIZE(baudrate_table)) {
			if ((flags & H_FORCE) == 0)
				printf("## Baudrate %d bps not supported\n",
				       baudrate);
			return 1;
		}
		if ((flags & H_INTERACTIVE) != 0) {
			printf("## Switch baudrate to %d bps and press ENTER ...\n",
			       baudrate);
			udelay(50000);
		}

		gd->baudrate = baudrate;

		serial_setbrg();

		udelay(50000);

		if ((flags & H_INTERACTIVE) != 0)
			while (1) {
				if (getc() == '\r')
					break;
			}

		return 0;
	case env_op_delete:
		printf("## Baudrate may not be deleted\n");
		return 1;
	default:
		return 0;
	}
}
U_BOOT_ENV_CALLBACK(baudrate, on_baudrate);

256 257 258
static int serial_post_probe(struct udevice *dev)
{
	struct dm_serial_ops *ops = serial_get_ops(dev);
259
#ifdef CONFIG_DM_STDIO
260
	struct serial_dev_priv *upriv = dev_get_uclass_priv(dev);
261 262
	struct stdio_dev sdev;
#endif
263 264
	int ret;

265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
#if defined(CONFIG_NEEDS_MANUAL_RELOC)
	if (ops->setbrg)
		ops->setbrg += gd->reloc_off;
	if (ops->getc)
		ops->getc += gd->reloc_off;
	if (ops->putc)
		ops->putc += gd->reloc_off;
	if (ops->pending)
		ops->pending += gd->reloc_off;
	if (ops->clear)
		ops->clear += gd->reloc_off;
#if CONFIG_POST & CONFIG_SYS_POST_UART
	if (ops->loop)
		ops->loop += gd->reloc_off
#endif
#endif
281 282 283 284 285 286 287
	/* Set the baud rate */
	if (ops->setbrg) {
		ret = ops->setbrg(dev, gd->baudrate);
		if (ret)
			return ret;
	}

288
#ifdef CONFIG_DM_STDIO
289 290 291 292 293 294 295 296 297 298 299 300
	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);
301
#endif
302 303 304 305 306 307
	return 0;
}

static int serial_pre_remove(struct udevice *dev)
{
#ifdef CONFIG_SYS_STDIO_DEREGISTER
308
	struct serial_dev_priv *upriv = dev_get_uclass_priv(dev);
309

310
	if (stdio_deregister_dev(upriv->sdev, 0))
311 312 313 314 315 316 317 318 319
		return -EPERM;
#endif

	return 0;
}

UCLASS_DRIVER(serial) = {
	.id		= UCLASS_SERIAL,
	.name		= "serial",
320
	.flags		= DM_UC_FLAG_SEQ_ALIAS,
321 322 323 324
	.post_probe	= serial_post_probe,
	.pre_remove	= serial_pre_remove,
	.per_device_auto_alloc_size = sizeof(struct serial_dev_priv),
};