dm: exynos: Move serial to driver model
Change the Exynos serial driver to work with driver model and switch over all relevant boards to use it. Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
		
							parent
							
								
									9208fffebc
								
							
						
					
					
						commit
						73e256c2ac
					
				|  | @ -9,6 +9,8 @@ | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #include <common.h> | #include <common.h> | ||||||
|  | #include <dm.h> | ||||||
|  | #include <errno.h> | ||||||
| #include <fdtdec.h> | #include <fdtdec.h> | ||||||
| #include <linux/compiler.h> | #include <linux/compiler.h> | ||||||
| #include <asm/io.h> | #include <asm/io.h> | ||||||
|  | @ -18,26 +20,18 @@ | ||||||
| 
 | 
 | ||||||
| DECLARE_GLOBAL_DATA_PTR; | DECLARE_GLOBAL_DATA_PTR; | ||||||
| 
 | 
 | ||||||
| #define RX_FIFO_COUNT_MASK	0xff | #define RX_FIFO_COUNT_SHIFT	0 | ||||||
| #define RX_FIFO_FULL_MASK	(1 << 8) | #define RX_FIFO_COUNT_MASK	(0xff << RX_FIFO_COUNT_SHIFT) | ||||||
| #define TX_FIFO_FULL_MASK	(1 << 24) | #define RX_FIFO_FULL		(1 << 8) | ||||||
|  | #define TX_FIFO_COUNT_SHIFT	16 | ||||||
|  | #define TX_FIFO_COUNT_MASK	(0xff << TX_FIFO_COUNT_SHIFT) | ||||||
|  | #define TX_FIFO_FULL		(1 << 24) | ||||||
| 
 | 
 | ||||||
| /* Information about a serial port */ | /* Information about a serial port */ | ||||||
| struct fdt_serial { | struct s5p_serial_platdata { | ||||||
| 	u32 base_addr;  /* address of registers in physical memory */ | 	struct s5p_uart *reg;  /* address of registers in physical memory */ | ||||||
| 	u8 port_id;     /* uart port number */ | 	u8 port_id;     /* uart port number */ | ||||||
| 	u8 enabled;     /* 1 if enabled, 0 if disabled */ | }; | ||||||
| } config __attribute__ ((section(".data"))); |  | ||||||
| 
 |  | ||||||
| static inline struct s5p_uart *s5p_get_base_uart(int dev_index) |  | ||||||
| { |  | ||||||
| #ifdef CONFIG_OF_CONTROL |  | ||||||
| 	return (struct s5p_uart *)(config.base_addr); |  | ||||||
| #else |  | ||||||
| 	u32 offset = dev_index * sizeof(struct s5p_uart); |  | ||||||
| 	return (struct s5p_uart *)(samsung_get_base_uart() + offset); |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  * The coefficient, used to calculate the baudrate on S5P UARTs is |  * The coefficient, used to calculate the baudrate on S5P UARTs is | ||||||
|  | @ -65,23 +59,13 @@ static const int udivslot[] = { | ||||||
| 	0xffdf, | 	0xffdf, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static void serial_setbrg_dev(const int dev_index) | int s5p_serial_setbrg(struct udevice *dev, int baudrate) | ||||||
| { | { | ||||||
| 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index); | 	struct s5p_serial_platdata *plat = dev->platdata; | ||||||
| 	u32 uclk = get_uart_clk(dev_index); | 	struct s5p_uart *const uart = plat->reg; | ||||||
| 	u32 baudrate = gd->baudrate; | 	u32 uclk = get_uart_clk(plat->port_id); | ||||||
| 	u32 val; | 	u32 val; | ||||||
| 
 | 
 | ||||||
| #if defined(CONFIG_SILENT_CONSOLE) && \ |  | ||||||
| 		defined(CONFIG_OF_CONTROL) && \ |  | ||||||
| 		!defined(CONFIG_SPL_BUILD) |  | ||||||
| 	if (fdtdec_get_config_int(gd->fdt_blob, "silent_console", 0)) |  | ||||||
| 		gd->flags |= GD_FLG_SILENT; |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| 	if (!config.enabled) |  | ||||||
| 		return; |  | ||||||
| 
 |  | ||||||
| 	val = uclk / baudrate; | 	val = uclk / baudrate; | ||||||
| 
 | 
 | ||||||
| 	writel(val / 16 - 1, &uart->ubrdiv); | 	writel(val / 16 - 1, &uart->ubrdiv); | ||||||
|  | @ -90,15 +74,14 @@ static void serial_setbrg_dev(const int dev_index) | ||||||
| 		writew(udivslot[val % 16], &uart->rest.slot); | 		writew(udivslot[val % 16], &uart->rest.slot); | ||||||
| 	else | 	else | ||||||
| 		writeb(val % 16, &uart->rest.value); | 		writeb(val % 16, &uart->rest.value); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 | static int s5p_serial_probe(struct udevice *dev) | ||||||
|  * Initialise the serial port with the given baudrate. The settings |  | ||||||
|  * are always 8 data bits, no parity, 1 stop bit, no start bits. |  | ||||||
|  */ |  | ||||||
| static int serial_init_dev(const int dev_index) |  | ||||||
| { | { | ||||||
| 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index); | 	struct s5p_serial_platdata *plat = dev->platdata; | ||||||
|  | 	struct s5p_uart *const uart = plat->reg; | ||||||
| 
 | 
 | ||||||
| 	/* enable FIFOs, auto clear Rx FIFO */ | 	/* enable FIFOs, auto clear Rx FIFO */ | ||||||
| 	writel(0x3, &uart->ufcon); | 	writel(0x3, &uart->ufcon); | ||||||
|  | @ -108,14 +91,11 @@ static int serial_init_dev(const int dev_index) | ||||||
| 	/* No interrupts, no DMA, pure polling */ | 	/* No interrupts, no DMA, pure polling */ | ||||||
| 	writel(0x245, &uart->ucon); | 	writel(0x245, &uart->ucon); | ||||||
| 
 | 
 | ||||||
| 	serial_setbrg_dev(dev_index); |  | ||||||
| 
 |  | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int serial_err_check(const int dev_index, int op) | static int serial_err_check(const struct s5p_uart *const uart, int op) | ||||||
| { | { | ||||||
| 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index); |  | ||||||
| 	unsigned int mask; | 	unsigned int mask; | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
|  | @ -133,169 +113,78 @@ static int serial_err_check(const int dev_index, int op) | ||||||
| 	return readl(&uart->uerstat) & mask; | 	return readl(&uart->uerstat) & mask; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 | static int s5p_serial_getc(struct udevice *dev) | ||||||
|  * Read a single byte from the serial port. Returns 1 on success, 0 |  | ||||||
|  * otherwise. When the function is succesfull, the character read is |  | ||||||
|  * written into its argument c. |  | ||||||
|  */ |  | ||||||
| static int serial_getc_dev(const int dev_index) |  | ||||||
| { | { | ||||||
| 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index); | 	struct s5p_serial_platdata *plat = dev->platdata; | ||||||
|  | 	struct s5p_uart *const uart = plat->reg; | ||||||
| 
 | 
 | ||||||
| 	if (!config.enabled) | 	if (!(readl(&uart->ufstat) & RX_FIFO_COUNT_MASK)) | ||||||
| 		return 0; | 		return -EAGAIN; | ||||||
| 
 |  | ||||||
| 	/* wait for character to arrive */ |  | ||||||
| 	while (!(readl(&uart->ufstat) & (RX_FIFO_COUNT_MASK | |  | ||||||
| 					 RX_FIFO_FULL_MASK))) { |  | ||||||
| 		if (serial_err_check(dev_index, 0)) |  | ||||||
| 			return 0; |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
|  | 	serial_err_check(uart, 0); | ||||||
| 	return (int)(readb(&uart->urxh) & 0xff); | 	return (int)(readb(&uart->urxh) & 0xff); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 | static int s5p_serial_putc(struct udevice *dev, const char ch) | ||||||
|  * Output a single byte to the serial port. |  | ||||||
|  */ |  | ||||||
| static void serial_putc_dev(const char c, const int dev_index) |  | ||||||
| { | { | ||||||
| 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index); | 	struct s5p_serial_platdata *plat = dev->platdata; | ||||||
|  | 	struct s5p_uart *const uart = plat->reg; | ||||||
| 
 | 
 | ||||||
| 	if (!config.enabled) | 	if (readl(&uart->ufstat) & TX_FIFO_FULL) | ||||||
| 		return; | 		return -EAGAIN; | ||||||
| 
 | 
 | ||||||
| 	/* wait for room in the tx FIFO */ | 	writeb(ch, &uart->utxh); | ||||||
| 	while ((readl(&uart->ufstat) & TX_FIFO_FULL_MASK)) { | 	serial_err_check(uart, 1); | ||||||
| 		if (serial_err_check(dev_index, 1)) |  | ||||||
| 			return; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	writeb(c, &uart->utxh); |  | ||||||
| 
 |  | ||||||
| 	/* If \n, also do \r */ |  | ||||||
| 	if (c == '\n') |  | ||||||
| 		serial_putc('\r'); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /*
 |  | ||||||
|  * Test whether a character is in the RX buffer |  | ||||||
|  */ |  | ||||||
| static int serial_tstc_dev(const int dev_index) |  | ||||||
| { |  | ||||||
| 	struct s5p_uart *const uart = s5p_get_base_uart(dev_index); |  | ||||||
| 
 |  | ||||||
| 	if (!config.enabled) |  | ||||||
| 		return 0; |  | ||||||
| 
 |  | ||||||
| 	return (int)(readl(&uart->utrstat) & 0x1); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void serial_puts_dev(const char *s, const int dev_index) |  | ||||||
| { |  | ||||||
| 	while (*s) |  | ||||||
| 		serial_putc_dev(*s++, dev_index); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* Multi serial device functions */ |  | ||||||
| #define DECLARE_S5P_SERIAL_FUNCTIONS(port) \ |  | ||||||
| static int s5p_serial##port##_init(void) { return serial_init_dev(port); } \ |  | ||||||
| static void s5p_serial##port##_setbrg(void) { serial_setbrg_dev(port); } \ |  | ||||||
| static int s5p_serial##port##_getc(void) { return serial_getc_dev(port); } \ |  | ||||||
| static int s5p_serial##port##_tstc(void) { return serial_tstc_dev(port); } \ |  | ||||||
| static void s5p_serial##port##_putc(const char c) { serial_putc_dev(c, port); } \ |  | ||||||
| static void s5p_serial##port##_puts(const char *s) { serial_puts_dev(s, port); } |  | ||||||
| 
 |  | ||||||
| #define INIT_S5P_SERIAL_STRUCTURE(port, __name) {	\ |  | ||||||
| 	.name	= __name,				\ |  | ||||||
| 	.start	= s5p_serial##port##_init,		\ |  | ||||||
| 	.stop	= NULL,					\ |  | ||||||
| 	.setbrg	= s5p_serial##port##_setbrg,		\ |  | ||||||
| 	.getc	= s5p_serial##port##_getc,		\ |  | ||||||
| 	.tstc	= s5p_serial##port##_tstc,		\ |  | ||||||
| 	.putc	= s5p_serial##port##_putc,		\ |  | ||||||
| 	.puts	= s5p_serial##port##_puts,		\ |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| DECLARE_S5P_SERIAL_FUNCTIONS(0); |  | ||||||
| struct serial_device s5p_serial0_device = |  | ||||||
| 	INIT_S5P_SERIAL_STRUCTURE(0, "s5pser0"); |  | ||||||
| DECLARE_S5P_SERIAL_FUNCTIONS(1); |  | ||||||
| struct serial_device s5p_serial1_device = |  | ||||||
| 	INIT_S5P_SERIAL_STRUCTURE(1, "s5pser1"); |  | ||||||
| DECLARE_S5P_SERIAL_FUNCTIONS(2); |  | ||||||
| struct serial_device s5p_serial2_device = |  | ||||||
| 	INIT_S5P_SERIAL_STRUCTURE(2, "s5pser2"); |  | ||||||
| DECLARE_S5P_SERIAL_FUNCTIONS(3); |  | ||||||
| struct serial_device s5p_serial3_device = |  | ||||||
| 	INIT_S5P_SERIAL_STRUCTURE(3, "s5pser3"); |  | ||||||
| 
 |  | ||||||
| #ifdef CONFIG_OF_CONTROL |  | ||||||
| int fdtdec_decode_console(int *index, struct fdt_serial *uart) |  | ||||||
| { |  | ||||||
| 	const void *blob = gd->fdt_blob; |  | ||||||
| 	int node; |  | ||||||
| 
 |  | ||||||
| 	node = fdt_path_offset(blob, "console"); |  | ||||||
| 	if (node < 0) |  | ||||||
| 		return node; |  | ||||||
| 
 |  | ||||||
| 	uart->base_addr = fdtdec_get_addr(blob, node, "reg"); |  | ||||||
| 	if (uart->base_addr == FDT_ADDR_T_NONE) |  | ||||||
| 		return -FDT_ERR_NOTFOUND; |  | ||||||
| 
 |  | ||||||
| 	uart->port_id = fdtdec_get_int(blob, node, "id", -1); |  | ||||||
| 	uart->enabled = fdtdec_get_is_enabled(blob, node); |  | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| #endif |  | ||||||
| 
 | 
 | ||||||
| __weak struct serial_device *default_serial_console(void) | static int s5p_serial_pending(struct udevice *dev, bool input) | ||||||
| { | { | ||||||
| #ifdef CONFIG_OF_CONTROL | 	struct s5p_serial_platdata *plat = dev->platdata; | ||||||
| 	int index = 0; | 	struct s5p_uart *const uart = plat->reg; | ||||||
|  | 	uint32_t ufstat = readl(&uart->ufstat); | ||||||
| 
 | 
 | ||||||
| 	if ((!config.base_addr) && (fdtdec_decode_console(&index, &config))) { | 	if (input) | ||||||
| 		debug("Cannot decode default console node\n"); | 		return (ufstat & RX_FIFO_COUNT_MASK) >> RX_FIFO_COUNT_SHIFT; | ||||||
| 		return NULL; | 	else | ||||||
|  | 		return (ufstat & TX_FIFO_COUNT_MASK) >> TX_FIFO_COUNT_SHIFT; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 	switch (config.port_id) { | static int s5p_serial_ofdata_to_platdata(struct udevice *dev) | ||||||
| 	case 0: |  | ||||||
| 		return &s5p_serial0_device; |  | ||||||
| 	case 1: |  | ||||||
| 		return &s5p_serial1_device; |  | ||||||
| 	case 2: |  | ||||||
| 		return &s5p_serial2_device; |  | ||||||
| 	case 3: |  | ||||||
| 		return &s5p_serial3_device; |  | ||||||
| 	default: |  | ||||||
| 		debug("Unknown config.port_id: %d", config.port_id); |  | ||||||
| 		break; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return NULL; |  | ||||||
| #else |  | ||||||
| 	config.enabled = 1; |  | ||||||
| #if defined(CONFIG_SERIAL0) |  | ||||||
| 	return &s5p_serial0_device; |  | ||||||
| #elif defined(CONFIG_SERIAL1) |  | ||||||
| 	return &s5p_serial1_device; |  | ||||||
| #elif defined(CONFIG_SERIAL2) |  | ||||||
| 	return &s5p_serial2_device; |  | ||||||
| #elif defined(CONFIG_SERIAL3) |  | ||||||
| 	return &s5p_serial3_device; |  | ||||||
| #else |  | ||||||
| #error "CONFIG_SERIAL? missing." |  | ||||||
| #endif |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void s5p_serial_initialize(void) |  | ||||||
| { | { | ||||||
| 	serial_register(&s5p_serial0_device); | 	struct s5p_serial_platdata *plat = dev->platdata; | ||||||
| 	serial_register(&s5p_serial1_device); | 	fdt_addr_t addr; | ||||||
| 	serial_register(&s5p_serial2_device); | 
 | ||||||
| 	serial_register(&s5p_serial3_device); | 	addr = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg"); | ||||||
|  | 	if (addr == FDT_ADDR_T_NONE) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	plat->reg = (struct s5p_uart *)addr; | ||||||
|  | 	plat->port_id = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "id", -1); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | static const struct dm_serial_ops s5p_serial_ops = { | ||||||
|  | 	.putc = s5p_serial_putc, | ||||||
|  | 	.pending = s5p_serial_pending, | ||||||
|  | 	.getc = s5p_serial_getc, | ||||||
|  | 	.setbrg = s5p_serial_setbrg, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const struct udevice_id s5p_serial_ids[] = { | ||||||
|  | 	{ .compatible = "samsung,exynos4210-uart" }, | ||||||
|  | 	{ } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | U_BOOT_DRIVER(serial_s5p) = { | ||||||
|  | 	.name	= "serial_s5p", | ||||||
|  | 	.id	= UCLASS_SERIAL, | ||||||
|  | 	.of_match = s5p_serial_ids, | ||||||
|  | 	.ofdata_to_platdata = s5p_serial_ofdata_to_platdata, | ||||||
|  | 	.platdata_auto_alloc_size = sizeof(struct s5p_serial_platdata), | ||||||
|  | 	.probe = s5p_serial_probe, | ||||||
|  | 	.ops	= &s5p_serial_ops, | ||||||
|  | 	.flags = DM_FLAG_PRE_RELOC, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | @ -20,6 +20,7 @@ | ||||||
| #define CONFIG_DM | #define CONFIG_DM | ||||||
| #define CONFIG_CMD_DM | #define CONFIG_CMD_DM | ||||||
| #define CONFIG_DM_GPIO | #define CONFIG_DM_GPIO | ||||||
|  | #define CONFIG_DM_SERIAL | ||||||
| 
 | 
 | ||||||
| #define CONFIG_ARCH_CPU_INIT | #define CONFIG_ARCH_CPU_INIT | ||||||
| #define CONFIG_DISPLAY_CPUINFO | #define CONFIG_DISPLAY_CPUINFO | ||||||
|  |  | ||||||
|  | @ -292,5 +292,6 @@ | ||||||
| #define CONFIG_DM | #define CONFIG_DM | ||||||
| #define CONFIG_CMD_DM | #define CONFIG_CMD_DM | ||||||
| #define CONFIG_DM_GPIO | #define CONFIG_DM_GPIO | ||||||
|  | #define CONFIG_DM_SERIAL | ||||||
| 
 | 
 | ||||||
| #endif	/* __CONFIG_H */ | #endif	/* __CONFIG_H */ | ||||||
|  |  | ||||||
|  | @ -227,5 +227,6 @@ | ||||||
| #define CONFIG_DM | #define CONFIG_DM | ||||||
| #define CONFIG_CMD_DM | #define CONFIG_CMD_DM | ||||||
| #define CONFIG_DM_GPIO | #define CONFIG_DM_GPIO | ||||||
|  | #define CONFIG_DM_SERIAL | ||||||
| 
 | 
 | ||||||
| #endif	/* __CONFIG_H */ | #endif	/* __CONFIG_H */ | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue