[U-Boot] [PATCH v3 2/4] mips: ath79: add serial driver for ar933x SOC

Signed-off-by: Wills Wang wills.wang@live.com ---
Changes in v3: None Changes in v2: None
drivers/serial/Makefile | 1 + drivers/serial/serial_ar933x.c | 274 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 275 insertions(+) create mode 100644 drivers/serial/serial_ar933x.c
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index dd87147..9a7ad89 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -17,6 +17,7 @@ endif
obj-$(CONFIG_ALTERA_UART) += altera_uart.o obj-$(CONFIG_ALTERA_JTAG_UART) += altera_jtag_uart.o +obj-$(CONFIG_AR933X_SERIAL) += serial_ar933x.o obj-$(CONFIG_ARM_DCC) += arm_dcc.o obj-$(CONFIG_ATMEL_USART) += atmel_usart.o obj-$(CONFIG_EFI_APP) += serial_efi.o diff --git a/drivers/serial/serial_ar933x.c b/drivers/serial/serial_ar933x.c new file mode 100644 index 0000000..6c0d726 --- /dev/null +++ b/drivers/serial/serial_ar933x.c @@ -0,0 +1,274 @@ +/* + * (C) Copyright 2015 + * Wills Wang, wills.wang@live.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <serial.h> +#include <dm.h> +#include <errno.h> +#include <asm/io.h> +#include <asm/addrspace.h> +#include <asm/types.h> +#include <asm/arch/ar71xx_regs.h> +#include <asm/arch/ar933x_uart.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct ar933x_serial_baudrate{ + u32 baudrate; + u32 scale; + u32 step; +}; + +const struct ar933x_serial_baudrate baudrate_table_40mhz[] = { +/* baudrate, scale, step */ + {600, 255, 503}, + {1200, 249, 983}, + {2400, 167, 1321}, + {4800, 87, 1384}, + {9600, 45, 1447}, + {14400, 53, 2548}, + {19200, 22, 1447}, + {28800, 26, 2548}, + {38400, 28, 3649}, + {56000, 7, 1468}, + {57600, 34, 6606}, + {115200, 28, 10947}, + {128000, 6, 2936}, + {153600, 18, 9563}, + {230400, 16, 12834}, + {250000, 4, 4096}, + {256000, 6, 5872}, + {460800, 7, 12079}, + {576000, 4, 9437}, + {921600, 3, 12079}, + {1000000, 2, 9830}, + {1152000, 2, 11324}, + {1500000, 0, 4915}, + {2000000, 0, 6553}, + }; + +const struct ar933x_serial_baudrate baudrate_table_25mhz[] = { +/* baudrate, scale, step */ + {600, 255, 805}, + {1200, 209, 1321}, + {2400, 104, 1321}, + {4800, 54, 1384}, + {9600, 78, 3976}, + {14400, 98, 7474}, + {19200, 55, 5637}, + {28800, 77, 11777}, + {38400, 36, 7449}, + {56000, 4, 1468}, + {57600, 35, 10871}, + {115200, 20, 12683}, + {128000, 11, 8053}, + {153600, 9, 8053}, + {230400, 9, 12079}, + {250000, 6, 9175}, + {256000, 5, 8053}, + {460800, 4, 12079}, + {576000, 3, 12079}, + {921600, 1, 9663}, + {1000000, 1, 10485}, + {1152000, 1, 12079}, + {1500000, 0, 7864}, + {2000000, 0, 10485}, +}; + +static inline u32 ar933x_read(u32 base, u32 offset) +{ + return readl(KSEG1ADDR(base + offset)); +} + +static inline void ar933x_write(u32 base, u32 offset, u32 val) +{ + writel(val, KSEG1ADDR(base + offset)); +} + +static int ar933x_serial_init(void) +{ + u32 val; + + /* + * Set GPIO10 (UART_SO) as output and enable UART, + * BIT(15) in GPIO_FUNCTION_1 register must be written with 1 + */ + val = ar933x_read(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_OE); + val |= BIT(10); + ar933x_write(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_OE, val); + + val = ar933x_read(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_FUNC); + val |= (AR933X_GPIO_FUNC_UART_EN | BIT(15)); + ar933x_write(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_FUNC, val); + + /* + * UART controller configuration: + * - no DMA + * - no interrupt + * - DCE mode + * - no flow control + * - set RX ready oride + * - set TX ready oride + */ + val = AR933X_UART_CS_TX_READY_ORIDE | AR933X_UART_CS_RX_READY_ORIDE + | (AR933X_UART_CS_IF_MODE_DCE << AR933X_UART_CS_IF_MODE_S); + ar933x_write(AR933X_UART_BASE, AR933X_UART_CS_REG, val); + return 0; +} + +#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_setbrg(struct udevice *dev, int baudrate) +{ +#else +static void ar933x_serial_setbrg(void) +{ + int baudrate = gd->baudrate; +#endif + u32 val, scale, step; + const struct ar933x_serial_baudrate *baudrate_table; + int i, baudrate_table_size; + + val = ar933x_read(AR71XX_RESET_BASE, AR933X_RESET_REG_BOOTSTRAP); + if (val & AR933X_BOOTSTRAP_REF_CLK_40) { + baudrate_table = baudrate_table_40mhz; + baudrate_table_size = ARRAY_SIZE(baudrate_table_40mhz); + scale = (40000000 / (16 * baudrate)) - 1; + step = 8192; + } else { + baudrate_table = baudrate_table_25mhz; + baudrate_table_size = ARRAY_SIZE(baudrate_table_25mhz); + scale = (25000000 / (16 * baudrate)) - 1; + step = 8192; + } + + for (i = 0; i < baudrate_table_size; i++) { + if (baudrate_table[i].baudrate == gd->baudrate) { + scale = baudrate_table[i].scale; + step = baudrate_table[i].step; + } + } + + val = ((scale & AR933X_UART_CLOCK_SCALE_M) + << AR933X_UART_CLOCK_SCALE_S); + val |= ((step & AR933X_UART_CLOCK_STEP_M) + << AR933X_UART_CLOCK_STEP_S); + ar933x_write(AR933X_UART_BASE, AR933X_UART_CLOCK_REG, val); +#ifdef CONFIG_DM_SERIAL + return 0; +#endif +} + +#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_putc(struct udevice *dev, const char c) +#else +static void ar933x_serial_putc(const char c) +#endif +{ + u32 data; + + if (c == '\n') +#ifdef CONFIG_DM_SERIAL + ar933x_serial_putc(dev, '\r'); +#else + ar933x_serial_putc('\r'); +#endif + do { + data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG); + } while (!(data & AR933X_UART_DATA_TX_CSR)); + + data = (u32)c | AR933X_UART_DATA_TX_CSR; + ar933x_write(AR933X_UART_BASE, AR933X_UART_DATA_REG, data); +#ifdef CONFIG_DM_SERIAL + return 0; +#endif +} + +#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_getc(struct udevice *dev) +#else +static int ar933x_serial_getc(void) +#endif +{ + u32 data; + + do { + data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG); + } while (!(data & AR933X_UART_DATA_RX_CSR)); + + data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG); + ar933x_write(AR933X_UART_BASE, AR933X_UART_DATA_REG, AR933X_UART_DATA_RX_CSR); + return data & AR933X_UART_DATA_TX_RX_MASK; +} + +#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_pending(struct udevice *dev, bool input) +{ + u32 data; + + data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG); + if (input) + return (data & AR933X_UART_DATA_RX_CSR) ? 1 : 0; + else + return (data & AR933X_UART_DATA_TX_CSR) ? 0 : 1; +} + +static int ar933x_serial_probe(struct udevice *dev) +{ + ar933x_serial_init(); + return 0; +} + +static const struct dm_serial_ops ar933x_serial_ops = { + .putc = ar933x_serial_putc, + .pending = ar933x_serial_pending, + .getc = ar933x_serial_getc, + .setbrg = ar933x_serial_setbrg, +}; + +static const struct udevice_id ar933x_serial_ids[] = { + { .compatible = "ath79,ar933x-uart" }, + { } +}; + +U_BOOT_DRIVER(serial_ar933x) = { + .name = "serial_ar933x", + .id = UCLASS_SERIAL, + .of_match = ar933x_serial_ids, + .probe = ar933x_serial_probe, + .ops = &ar933x_serial_ops, + .flags = DM_FLAG_PRE_RELOC, +}; +#else +static int ar933x_serial_tstc(void) +{ + u32 data; + + data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG); + return (data & AR933X_UART_DATA_RX_CSR) ? 1 : 0; +} + +static struct serial_device ar933x_serial_drv = { + .name = "ar933x_serial", + .start = ar933x_serial_init, + .stop = NULL, + .setbrg = ar933x_serial_setbrg, + .putc = ar933x_serial_putc, + .puts = default_serial_puts, + .getc = ar933x_serial_getc, + .tstc = ar933x_serial_tstc, +}; + +void ar933x_serial_initialize(void) +{ + serial_register(&ar933x_serial_drv); +} + +__weak struct serial_device *default_serial_console(void) +{ + return &ar933x_serial_drv; +} +#endif

Hi Wills,
Is there any reason that you need to support pre-DM serial driver? It should be safe to support DM only. You may add debug_uart support which might be helpful during debug. The ops for DM serial is different to pre-DM though the name look similar. Please see the comments below.
I worked on mips before I moved to nios very long ago (20 yr). And nios2 mostly followed mips.
I understand there is not device tree control for u-boot on mips yet. But I added it to nios2 recently. It might be worthy if you could add it to mips. You may find the dts binding on Linux kernel. And please add dts binding to doc/ and you might copy them from Linux kernel.
+#include <common.h> +#include <serial.h> +#include <dm.h> +#include <errno.h> +#include <asm/io.h> +#include <asm/addrspace.h> +#include <asm/types.h> +#include <asm/arch/ar71xx_regs.h> +#include <asm/arch/ar933x_uart.h>
Please sort the sequence of header files inclusion.
+DECLARE_GLOBAL_DATA_PTR;
+struct ar933x_serial_baudrate{
- u32 baudrate;
- u32 scale;
- u32 step;
+};
+const struct ar933x_serial_baudrate baudrate_table_40mhz[] = { +/* baudrate, scale, step */
- {600, 255, 503},
- {1200, 249, 983},
- {2400, 167, 1321},
- {4800, 87, 1384},
- {9600, 45, 1447},
- {14400, 53, 2548},
- {19200, 22, 1447},
- {28800, 26, 2548},
- {38400, 28, 3649},
- {56000, 7, 1468},
- {57600, 34, 6606},
- {115200, 28, 10947},
- {128000, 6, 2936},
- {153600, 18, 9563},
- {230400, 16, 12834},
- {250000, 4, 4096},
- {256000, 6, 5872},
- {460800, 7, 12079},
- {576000, 4, 9437},
- {921600, 3, 12079},
- {1000000, 2, 9830},
- {1152000, 2, 11324},
- {1500000, 0, 4915},
- {2000000, 0, 6553},
- };
+const struct ar933x_serial_baudrate baudrate_table_25mhz[] = { +/* baudrate, scale, step */
- {600, 255, 805},
- {1200, 209, 1321},
- {2400, 104, 1321},
- {4800, 54, 1384},
- {9600, 78, 3976},
- {14400, 98, 7474},
- {19200, 55, 5637},
- {28800, 77, 11777},
- {38400, 36, 7449},
- {56000, 4, 1468},
- {57600, 35, 10871},
- {115200, 20, 12683},
- {128000, 11, 8053},
- {153600, 9, 8053},
- {230400, 9, 12079},
- {250000, 6, 9175},
- {256000, 5, 8053},
- {460800, 4, 12079},
- {576000, 3, 12079},
- {921600, 1, 9663},
- {1000000, 1, 10485},
- {1152000, 1, 12079},
- {1500000, 0, 7864},
- {2000000, 0, 10485},
+};
The requirement of u-boot is much simpler than Linux kernel. For the uart header, you should include only the macros that the driver really used. Or you can add it to the driver directly without the additional header file. The same rule applies to the baudrate tables. You need only some practical rates, not every possible one.
+static inline u32 ar933x_read(u32 base, u32 offset) +{
- return readl(KSEG1ADDR(base + offset));
+}
+static inline void ar933x_write(u32 base, u32 offset, u32 val) +{
- writel(val, KSEG1ADDR(base + offset));
+}
For the KSEG1ADDR mapping, which Marek also mentioned, I would suggest rework map_physmem() in asm/io.h on mips to map K1 kernel space, which is very similar to ioremap() on Linux kernel. So that you don't need to map it for every IO.
plat->regs = map_physmem(dev_get_addr(dev), sizeof(...), MAP_NOCACHE);
+static int ar933x_serial_init(void) +{
- u32 val;
- /*
* Set GPIO10 (UART_SO) as output and enable UART,
* BIT(15) in GPIO_FUNCTION_1 register must be written with 1
*/
- val = ar933x_read(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_OE);
- val |= BIT(10);
- ar933x_write(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_OE, val);
- val = ar933x_read(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_FUNC);
- val |= (AR933X_GPIO_FUNC_UART_EN | BIT(15));
- ar933x_write(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_FUNC, val);
These might go to the pinctrl driver or board initialization.
- /*
* UART controller configuration:
* - no DMA
* - no interrupt
* - DCE mode
* - no flow control
* - set RX ready oride
* - set TX ready oride
*/
- val = AR933X_UART_CS_TX_READY_ORIDE | AR933X_UART_CS_RX_READY_ORIDE
| (AR933X_UART_CS_IF_MODE_DCE << AR933X_UART_CS_IF_MODE_S);
- ar933x_write(AR933X_UART_BASE, AR933X_UART_CS_REG, val);
- return 0;
+}
+#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_setbrg(struct udevice *dev, int baudrate) +{ +#else +static void ar933x_serial_setbrg(void) +{
- int baudrate = gd->baudrate;
+#endif
- u32 val, scale, step;
- const struct ar933x_serial_baudrate *baudrate_table;
- int i, baudrate_table_size;
- val = ar933x_read(AR71XX_RESET_BASE, AR933X_RESET_REG_BOOTSTRAP);
This can go to some clock rate in board info during board initialization.
- if (val & AR933X_BOOTSTRAP_REF_CLK_40) {
baudrate_table = baudrate_table_40mhz;
baudrate_table_size = ARRAY_SIZE(baudrate_table_40mhz);
scale = (40000000 / (16 * baudrate)) - 1;
step = 8192;
- } else {
baudrate_table = baudrate_table_25mhz;
baudrate_table_size = ARRAY_SIZE(baudrate_table_25mhz);
scale = (25000000 / (16 * baudrate)) - 1;
step = 8192;
- }
- for (i = 0; i < baudrate_table_size; i++) {
if (baudrate_table[i].baudrate == gd->baudrate) {
scale = baudrate_table[i].scale;
step = baudrate_table[i].step;
}
- }
What happens if not found?
- val = ((scale & AR933X_UART_CLOCK_SCALE_M)
<< AR933X_UART_CLOCK_SCALE_S);
The outer parenthesis is not needed.
- val |= ((step & AR933X_UART_CLOCK_STEP_M)
<< AR933X_UART_CLOCK_STEP_S);
- ar933x_write(AR933X_UART_BASE, AR933X_UART_CLOCK_REG, val);
+#ifdef CONFIG_DM_SERIAL
- return 0;
+#endif +}
+#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_putc(struct udevice *dev, const char c) +#else +static void ar933x_serial_putc(const char c) +#endif +{
- u32 data;
- if (c == '\n')
+#ifdef CONFIG_DM_SERIAL
ar933x_serial_putc(dev, '\r');
Not needed for DM serial.
+#else
ar933x_serial_putc('\r');
+#endif
- do {
data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG);
- } while (!(data & AR933X_UART_DATA_TX_CSR));
Return -EAGAIN if not ready for DM serial.
- data = (u32)c | AR933X_UART_DATA_TX_CSR;
- ar933x_write(AR933X_UART_BASE, AR933X_UART_DATA_REG, data);
+#ifdef CONFIG_DM_SERIAL
- return 0;
+#endif +}
+#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_getc(struct udevice *dev) +#else +static int ar933x_serial_getc(void) +#endif +{
- u32 data;
- do {
data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG);
- } while (!(data & AR933X_UART_DATA_RX_CSR));
Return -EAGAIN if no data available for DM serial.
- data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG);
- ar933x_write(AR933X_UART_BASE, AR933X_UART_DATA_REG, AR933X_UART_DATA_RX_CSR);
- return data & AR933X_UART_DATA_TX_RX_MASK;
+}
+#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_pending(struct udevice *dev, bool input) +{
- u32 data;
- data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG);
- if (input)
return (data & AR933X_UART_DATA_RX_CSR) ? 1 : 0;
- else
return (data & AR933X_UART_DATA_TX_CSR) ? 0 : 1;
+}
+static int ar933x_serial_probe(struct udevice *dev) +{
- ar933x_serial_init();
- return 0;
+}
+static const struct dm_serial_ops ar933x_serial_ops = {
- .putc = ar933x_serial_putc,
- .pending = ar933x_serial_pending,
- .getc = ar933x_serial_getc,
- .setbrg = ar933x_serial_setbrg,
+};
+static const struct udevice_id ar933x_serial_ids[] = {
- { .compatible = "ath79,ar933x-uart" },
- { }
+};
+U_BOOT_DRIVER(serial_ar933x) = {
- .name = "serial_ar933x",
- .id = UCLASS_SERIAL,
- .of_match = ar933x_serial_ids,
- .probe = ar933x_serial_probe,
- .ops = &ar933x_serial_ops,
- .flags = DM_FLAG_PRE_RELOC,
+};
I understand there might be only a single UART on the ar9331. But it might be better to bind the reg address with U_BOOT_DEVICE() and platform data, or better dts.
Thanks.
Best regards, Thomas

On 12/25/2015 10:39 AM, Thomas Chou wrote:
Hi Wills,
Is there any reason that you need to support pre-DM serial driver? It should be safe to support DM only. You may add debug_uart support which might be helpful during debug. The ops for DM serial is different to pre-DM though the name look similar. Please see the comments below.
I will remove the code about pre-DM serial driver.
I worked on mips before I moved to nios very long ago (20 yr). And nios2 mostly followed mips.
I understand there is not device tree control for u-boot on mips yet. But I added it to nios2 recently. It might be worthy if you could add it to mips. You may find the dts binding on Linux kernel. And please add dts binding to doc/ and you might copy them from Linux kernel.
I has taken a long time to add base device tree for this patch, but i can't get the driver serial number if don't specify explicitly "aliases" section in dts file. but It seems to not need for ARM device.
+#include <common.h> +#include <serial.h> +#include <dm.h> +#include <errno.h> +#include <asm/io.h> +#include <asm/addrspace.h> +#include <asm/types.h> +#include <asm/arch/ar71xx_regs.h> +#include <asm/arch/ar933x_uart.h>
Please sort the sequence of header files inclusion.
+DECLARE_GLOBAL_DATA_PTR;
+struct ar933x_serial_baudrate{
- u32 baudrate;
- u32 scale;
- u32 step;
+};
+const struct ar933x_serial_baudrate baudrate_table_40mhz[] = { +/* baudrate, scale, step */
- {600, 255, 503},
- {1200, 249, 983},
- {2400, 167, 1321},
- {4800, 87, 1384},
- {9600, 45, 1447},
- {14400, 53, 2548},
- {19200, 22, 1447},
- {28800, 26, 2548},
- {38400, 28, 3649},
- {56000, 7, 1468},
- {57600, 34, 6606},
- {115200, 28, 10947},
- {128000, 6, 2936},
- {153600, 18, 9563},
- {230400, 16, 12834},
- {250000, 4, 4096},
- {256000, 6, 5872},
- {460800, 7, 12079},
- {576000, 4, 9437},
- {921600, 3, 12079},
- {1000000, 2, 9830},
- {1152000, 2, 11324},
- {1500000, 0, 4915},
- {2000000, 0, 6553},
- };
+const struct ar933x_serial_baudrate baudrate_table_25mhz[] = { +/* baudrate, scale, step */
- {600, 255, 805},
- {1200, 209, 1321},
- {2400, 104, 1321},
- {4800, 54, 1384},
- {9600, 78, 3976},
- {14400, 98, 7474},
- {19200, 55, 5637},
- {28800, 77, 11777},
- {38400, 36, 7449},
- {56000, 4, 1468},
- {57600, 35, 10871},
- {115200, 20, 12683},
- {128000, 11, 8053},
- {153600, 9, 8053},
- {230400, 9, 12079},
- {250000, 6, 9175},
- {256000, 5, 8053},
- {460800, 4, 12079},
- {576000, 3, 12079},
- {921600, 1, 9663},
- {1000000, 1, 10485},
- {1152000, 1, 12079},
- {1500000, 0, 7864},
- {2000000, 0, 10485},
+};
The requirement of u-boot is much simpler than Linux kernel. For the uart header, you should include only the macros that the driver really used. Or you can add it to the driver directly without the additional header file. The same rule applies to the baudrate tables. You need only some practical rates, not every possible one.
+static inline u32 ar933x_read(u32 base, u32 offset) +{
- return readl(KSEG1ADDR(base + offset));
+}
+static inline void ar933x_write(u32 base, u32 offset, u32 val) +{
- writel(val, KSEG1ADDR(base + offset));
+}
For the KSEG1ADDR mapping, which Marek also mentioned, I would suggest rework map_physmem() in asm/io.h on mips to map K1 kernel space, which is very similar to ioremap() on Linux kernel. So that you don't need to map it for every IO.
plat->regs = map_physmem(dev_get_addr(dev), sizeof(...), MAP_NOCACHE);
In MIPS, function "map_physmem" is empty.
+static int ar933x_serial_init(void) +{
- u32 val;
- /*
* Set GPIO10 (UART_SO) as output and enable UART,
* BIT(15) in GPIO_FUNCTION_1 register must be written with 1
*/
- val = ar933x_read(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_OE);
- val |= BIT(10);
- ar933x_write(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_OE, val);
- val = ar933x_read(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_FUNC);
- val |= (AR933X_GPIO_FUNC_UART_EN | BIT(15));
- ar933x_write(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_FUNC, val);
These might go to the pinctrl driver or board initialization.
- /*
* UART controller configuration:
* - no DMA
* - no interrupt
* - DCE mode
* - no flow control
* - set RX ready oride
* - set TX ready oride
*/
- val = AR933X_UART_CS_TX_READY_ORIDE | AR933X_UART_CS_RX_READY_ORIDE
| (AR933X_UART_CS_IF_MODE_DCE << AR933X_UART_CS_IF_MODE_S);
- ar933x_write(AR933X_UART_BASE, AR933X_UART_CS_REG, val);
- return 0;
+}
+#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_setbrg(struct udevice *dev, int baudrate) +{ +#else +static void ar933x_serial_setbrg(void) +{
- int baudrate = gd->baudrate;
+#endif
- u32 val, scale, step;
- const struct ar933x_serial_baudrate *baudrate_table;
- int i, baudrate_table_size;
- val = ar933x_read(AR71XX_RESET_BASE, AR933X_RESET_REG_BOOTSTRAP);
This can go to some clock rate in board info during board initialization.
- if (val & AR933X_BOOTSTRAP_REF_CLK_40) {
baudrate_table = baudrate_table_40mhz;
baudrate_table_size = ARRAY_SIZE(baudrate_table_40mhz);
scale = (40000000 / (16 * baudrate)) - 1;
step = 8192;
- } else {
baudrate_table = baudrate_table_25mhz;
baudrate_table_size = ARRAY_SIZE(baudrate_table_25mhz);
scale = (25000000 / (16 * baudrate)) - 1;
step = 8192;
- }
- for (i = 0; i < baudrate_table_size; i++) {
if (baudrate_table[i].baudrate == gd->baudrate) {
scale = baudrate_table[i].scale;
step = baudrate_table[i].step;
}
- }
What happens if not found?
"scale" and "step" was initialized by "if...else" statement above.
- val = ((scale & AR933X_UART_CLOCK_SCALE_M)
<< AR933X_UART_CLOCK_SCALE_S);
The outer parenthesis is not needed.
- val |= ((step & AR933X_UART_CLOCK_STEP_M)
<< AR933X_UART_CLOCK_STEP_S);
- ar933x_write(AR933X_UART_BASE, AR933X_UART_CLOCK_REG, val);
+#ifdef CONFIG_DM_SERIAL
- return 0;
+#endif +}
+#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_putc(struct udevice *dev, const char c) +#else +static void ar933x_serial_putc(const char c) +#endif +{
- u32 data;
- if (c == '\n')
+#ifdef CONFIG_DM_SERIAL
ar933x_serial_putc(dev, '\r');
Not needed for DM serial.
+#else
ar933x_serial_putc('\r');
+#endif
- do {
data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG);
- } while (!(data & AR933X_UART_DATA_TX_CSR));
Return -EAGAIN if not ready for DM serial.
- data = (u32)c | AR933X_UART_DATA_TX_CSR;
- ar933x_write(AR933X_UART_BASE, AR933X_UART_DATA_REG, data);
+#ifdef CONFIG_DM_SERIAL
- return 0;
+#endif +}
+#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_getc(struct udevice *dev) +#else +static int ar933x_serial_getc(void) +#endif +{
- u32 data;
- do {
data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG);
- } while (!(data & AR933X_UART_DATA_RX_CSR));
Return -EAGAIN if no data available for DM serial.
- data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG);
- ar933x_write(AR933X_UART_BASE, AR933X_UART_DATA_REG,
AR933X_UART_DATA_RX_CSR);
- return data & AR933X_UART_DATA_TX_RX_MASK;
+}
+#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_pending(struct udevice *dev, bool input) +{
- u32 data;
- data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG);
- if (input)
return (data & AR933X_UART_DATA_RX_CSR) ? 1 : 0;
- else
return (data & AR933X_UART_DATA_TX_CSR) ? 0 : 1;
+}
+static int ar933x_serial_probe(struct udevice *dev) +{
- ar933x_serial_init();
- return 0;
+}
+static const struct dm_serial_ops ar933x_serial_ops = {
- .putc = ar933x_serial_putc,
- .pending = ar933x_serial_pending,
- .getc = ar933x_serial_getc,
- .setbrg = ar933x_serial_setbrg,
+};
+static const struct udevice_id ar933x_serial_ids[] = {
- { .compatible = "ath79,ar933x-uart" },
- { }
+};
+U_BOOT_DRIVER(serial_ar933x) = {
- .name = "serial_ar933x",
- .id = UCLASS_SERIAL,
- .of_match = ar933x_serial_ids,
- .probe = ar933x_serial_probe,
- .ops = &ar933x_serial_ops,
- .flags = DM_FLAG_PRE_RELOC,
+};
I understand there might be only a single UART on the ar9331. But it might be better to bind the reg address with U_BOOT_DEVICE() and platform data, or better dts.
The pinctrl and clock drvier is not ready, there is no way to operate clock and gpio resister if use address from device tree.
Thanks.
Best regards, Thomas

On 2015年12月25日 14:05, Wills Wang wrote:
On 12/25/2015 10:39 AM, Thomas Chou wrote:
Hi Wills,
Is there any reason that you need to support pre-DM serial driver? It should be safe to support DM only. You may add debug_uart support which might be helpful during debug. The ops for DM serial is different to pre-DM though the name look similar. Please see the comments below.
I will remove the code about pre-DM serial driver.
I worked on mips before I moved to nios very long ago (20 yr). And nios2 mostly followed mips.
I understand there is not device tree control for u-boot on mips yet. But I added it to nios2 recently. It might be worthy if you could add it to mips. You may find the dts binding on Linux kernel. And please add dts binding to doc/ and you might copy them from Linux kernel.
I has taken a long time to add base device tree for this patch, but i can't get the driver serial number if don't specify explicitly "aliases" section in dts file. but It seems to not need for ARM device.
You will need to add "stdout-path=..." property with your serial path to the chosen node, like this, chosen { stdout-path = &uart; };
+#include <common.h> +#include <serial.h> +#include <dm.h> +#include <errno.h> +#include <asm/io.h> +#include <asm/addrspace.h> +#include <asm/types.h> +#include <asm/arch/ar71xx_regs.h> +#include <asm/arch/ar933x_uart.h>
Please sort the sequence of header files inclusion.
+DECLARE_GLOBAL_DATA_PTR;
+struct ar933x_serial_baudrate{
- u32 baudrate;
- u32 scale;
- u32 step;
+};
+const struct ar933x_serial_baudrate baudrate_table_40mhz[] = { +/* baudrate, scale, step */
- {600, 255, 503},
- {1200, 249, 983},
- {2400, 167, 1321},
- {4800, 87, 1384},
- {9600, 45, 1447},
- {14400, 53, 2548},
- {19200, 22, 1447},
- {28800, 26, 2548},
- {38400, 28, 3649},
- {56000, 7, 1468},
- {57600, 34, 6606},
- {115200, 28, 10947},
- {128000, 6, 2936},
- {153600, 18, 9563},
- {230400, 16, 12834},
- {250000, 4, 4096},
- {256000, 6, 5872},
- {460800, 7, 12079},
- {576000, 4, 9437},
- {921600, 3, 12079},
- {1000000, 2, 9830},
- {1152000, 2, 11324},
- {1500000, 0, 4915},
- {2000000, 0, 6553},
- };
+const struct ar933x_serial_baudrate baudrate_table_25mhz[] = { +/* baudrate, scale, step */
- {600, 255, 805},
- {1200, 209, 1321},
- {2400, 104, 1321},
- {4800, 54, 1384},
- {9600, 78, 3976},
- {14400, 98, 7474},
- {19200, 55, 5637},
- {28800, 77, 11777},
- {38400, 36, 7449},
- {56000, 4, 1468},
- {57600, 35, 10871},
- {115200, 20, 12683},
- {128000, 11, 8053},
- {153600, 9, 8053},
- {230400, 9, 12079},
- {250000, 6, 9175},
- {256000, 5, 8053},
- {460800, 4, 12079},
- {576000, 3, 12079},
- {921600, 1, 9663},
- {1000000, 1, 10485},
- {1152000, 1, 12079},
- {1500000, 0, 7864},
- {2000000, 0, 10485},
+};
The requirement of u-boot is much simpler than Linux kernel. For the uart header, you should include only the macros that the driver really used. Or you can add it to the driver directly without the additional header file. The same rule applies to the baudrate tables. You need only some practical rates, not every possible one.
+static inline u32 ar933x_read(u32 base, u32 offset) +{
- return readl(KSEG1ADDR(base + offset));
+}
+static inline void ar933x_write(u32 base, u32 offset, u32 val) +{
- writel(val, KSEG1ADDR(base + offset));
+}
For the KSEG1ADDR mapping, which Marek also mentioned, I would suggest rework map_physmem() in asm/io.h on mips to map K1 kernel space, which is very similar to ioremap() on Linux kernel. So that you don't need to map it for every IO.
plat->regs = map_physmem(dev_get_addr(dev), sizeof(...), MAP_NOCACHE);
In MIPS, function "map_physmem" is empty.
Yes. But you may improve it to do the real work. For example, not tested,
#define MAP_NOCACHE 1 #define MAP_WRCOMBINE 0 #define MAP_WRBACK 0 #define MAP_WRTHROUGH 0
static inline void * map_physmem(phys_addr_t paddr, unsigned long len, unsigned long flags) { if (flags) return (void *)KSEG1ADDR(paddr); else return (void *)KSEG0ADDR(paddr); }
+static int ar933x_serial_init(void) +{
- u32 val;
- /*
* Set GPIO10 (UART_SO) as output and enable UART,
* BIT(15) in GPIO_FUNCTION_1 register must be written with 1
*/
- val = ar933x_read(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_OE);
- val |= BIT(10);
- ar933x_write(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_OE, val);
- val = ar933x_read(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_FUNC);
- val |= (AR933X_GPIO_FUNC_UART_EN | BIT(15));
- ar933x_write(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_FUNC, val);
These might go to the pinctrl driver or board initialization.
- /*
* UART controller configuration:
* - no DMA
* - no interrupt
* - DCE mode
* - no flow control
* - set RX ready oride
* - set TX ready oride
*/
- val = AR933X_UART_CS_TX_READY_ORIDE | AR933X_UART_CS_RX_READY_ORIDE
| (AR933X_UART_CS_IF_MODE_DCE << AR933X_UART_CS_IF_MODE_S);
- ar933x_write(AR933X_UART_BASE, AR933X_UART_CS_REG, val);
- return 0;
+}
+#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_setbrg(struct udevice *dev, int baudrate) +{ +#else +static void ar933x_serial_setbrg(void) +{
- int baudrate = gd->baudrate;
+#endif
- u32 val, scale, step;
- const struct ar933x_serial_baudrate *baudrate_table;
- int i, baudrate_table_size;
- val = ar933x_read(AR71XX_RESET_BASE, AR933X_RESET_REG_BOOTSTRAP);
This can go to some clock rate in board info during board initialization.
- if (val & AR933X_BOOTSTRAP_REF_CLK_40) {
baudrate_table = baudrate_table_40mhz;
baudrate_table_size = ARRAY_SIZE(baudrate_table_40mhz);
scale = (40000000 / (16 * baudrate)) - 1;
step = 8192;
- } else {
baudrate_table = baudrate_table_25mhz;
baudrate_table_size = ARRAY_SIZE(baudrate_table_25mhz);
scale = (25000000 / (16 * baudrate)) - 1;
step = 8192;
- }
- for (i = 0; i < baudrate_table_size; i++) {
if (baudrate_table[i].baudrate == gd->baudrate) {
scale = baudrate_table[i].scale;
step = baudrate_table[i].step;
}
- }
What happens if not found?
"scale" and "step" was initialized by "if...else" statement above.
- val = ((scale & AR933X_UART_CLOCK_SCALE_M)
<< AR933X_UART_CLOCK_SCALE_S);
The outer parenthesis is not needed.
- val |= ((step & AR933X_UART_CLOCK_STEP_M)
<< AR933X_UART_CLOCK_STEP_S);
- ar933x_write(AR933X_UART_BASE, AR933X_UART_CLOCK_REG, val);
+#ifdef CONFIG_DM_SERIAL
- return 0;
+#endif +}
+#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_putc(struct udevice *dev, const char c) +#else +static void ar933x_serial_putc(const char c) +#endif +{
- u32 data;
- if (c == '\n')
+#ifdef CONFIG_DM_SERIAL
ar933x_serial_putc(dev, '\r');
Not needed for DM serial.
+#else
ar933x_serial_putc('\r');
+#endif
- do {
data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG);
- } while (!(data & AR933X_UART_DATA_TX_CSR));
Return -EAGAIN if not ready for DM serial.
- data = (u32)c | AR933X_UART_DATA_TX_CSR;
- ar933x_write(AR933X_UART_BASE, AR933X_UART_DATA_REG, data);
+#ifdef CONFIG_DM_SERIAL
- return 0;
+#endif +}
+#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_getc(struct udevice *dev) +#else +static int ar933x_serial_getc(void) +#endif +{
- u32 data;
- do {
data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG);
- } while (!(data & AR933X_UART_DATA_RX_CSR));
Return -EAGAIN if no data available for DM serial.
- data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG);
- ar933x_write(AR933X_UART_BASE, AR933X_UART_DATA_REG,
AR933X_UART_DATA_RX_CSR);
- return data & AR933X_UART_DATA_TX_RX_MASK;
+}
+#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_pending(struct udevice *dev, bool input) +{
- u32 data;
- data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG);
- if (input)
return (data & AR933X_UART_DATA_RX_CSR) ? 1 : 0;
- else
return (data & AR933X_UART_DATA_TX_CSR) ? 0 : 1;
+}
+static int ar933x_serial_probe(struct udevice *dev) +{
- ar933x_serial_init();
- return 0;
+}
+static const struct dm_serial_ops ar933x_serial_ops = {
- .putc = ar933x_serial_putc,
- .pending = ar933x_serial_pending,
- .getc = ar933x_serial_getc,
- .setbrg = ar933x_serial_setbrg,
+};
+static const struct udevice_id ar933x_serial_ids[] = {
- { .compatible = "ath79,ar933x-uart" },
- { }
+};
+U_BOOT_DRIVER(serial_ar933x) = {
- .name = "serial_ar933x",
- .id = UCLASS_SERIAL,
- .of_match = ar933x_serial_ids,
- .probe = ar933x_serial_probe,
- .ops = &ar933x_serial_ops,
- .flags = DM_FLAG_PRE_RELOC,
+};
I understand there might be only a single UART on the ar9331. But it might be better to bind the reg address with U_BOOT_DEVICE() and platform data, or better dts.
The pinctrl and clock drvier is not ready, there is no way to operate clock and gpio resister if use address from device tree.
You may add a legacy get_serial_clock() temporarily. And put the reset/pin configuration under mach-ath79. You may improve them with pinctrl and clock drivers.
Best regards, Thomas

Hi Wills,
There is code to set baud rate in Linux kernel,
drivers/tty/serial/ar933x_uart.c
Best regards, Thomas

Hi WIlls,
On 24 December 2015 at 04:22, Wills Wang wills.wang@live.com wrote:
Signed-off-by: Wills Wang wills.wang@live.com
Changes in v3: None Changes in v2: None
drivers/serial/Makefile | 1 + drivers/serial/serial_ar933x.c | 274 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 275 insertions(+) create mode 100644 drivers/serial/serial_ar933x.c
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index dd87147..9a7ad89 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -17,6 +17,7 @@ endif
obj-$(CONFIG_ALTERA_UART) += altera_uart.o obj-$(CONFIG_ALTERA_JTAG_UART) += altera_jtag_uart.o +obj-$(CONFIG_AR933X_SERIAL) += serial_ar933x.o obj-$(CONFIG_ARM_DCC) += arm_dcc.o obj-$(CONFIG_ATMEL_USART) += atmel_usart.o obj-$(CONFIG_EFI_APP) += serial_efi.o diff --git a/drivers/serial/serial_ar933x.c b/drivers/serial/serial_ar933x.c new file mode 100644 index 0000000..6c0d726 --- /dev/null +++ b/drivers/serial/serial_ar933x.c @@ -0,0 +1,274 @@ +/*
- (C) Copyright 2015
- Wills Wang, wills.wang@live.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <serial.h> +#include <dm.h> +#include <errno.h> +#include <asm/io.h> +#include <asm/addrspace.h> +#include <asm/types.h> +#include <asm/arch/ar71xx_regs.h> +#include <asm/arch/ar933x_uart.h>
+DECLARE_GLOBAL_DATA_PTR;
+struct ar933x_serial_baudrate{
u32 baudrate;
u32 scale;
u32 step;
+};
+const struct ar933x_serial_baudrate baudrate_table_40mhz[] = { +/* baudrate, scale, step */
{600, 255, 503},
{1200, 249, 983},
{2400, 167, 1321},
{4800, 87, 1384},
{9600, 45, 1447},
{14400, 53, 2548},
{19200, 22, 1447},
{28800, 26, 2548},
{38400, 28, 3649},
{56000, 7, 1468},
{57600, 34, 6606},
{115200, 28, 10947},
{128000, 6, 2936},
{153600, 18, 9563},
{230400, 16, 12834},
{250000, 4, 4096},
{256000, 6, 5872},
{460800, 7, 12079},
{576000, 4, 9437},
{921600, 3, 12079},
{1000000, 2, 9830},
{1152000, 2, 11324},
{1500000, 0, 4915},
{2000000, 0, 6553},
- };
+const struct ar933x_serial_baudrate baudrate_table_25mhz[] = { +/* baudrate, scale, step */
{600, 255, 805},
{1200, 209, 1321},
{2400, 104, 1321},
{4800, 54, 1384},
{9600, 78, 3976},
{14400, 98, 7474},
{19200, 55, 5637},
{28800, 77, 11777},
{38400, 36, 7449},
{56000, 4, 1468},
{57600, 35, 10871},
{115200, 20, 12683},
{128000, 11, 8053},
{153600, 9, 8053},
{230400, 9, 12079},
{250000, 6, 9175},
{256000, 5, 8053},
{460800, 4, 12079},
{576000, 3, 12079},
{921600, 1, 9663},
{1000000, 1, 10485},
{1152000, 1, 12079},
{1500000, 0, 7864},
{2000000, 0, 10485},
+};
+static inline u32 ar933x_read(u32 base, u32 offset) +{
return readl(KSEG1ADDR(base + offset));
+}
+static inline void ar933x_write(u32 base, u32 offset, u32 val) +{
writel(val, KSEG1ADDR(base + offset));
+}
+static int ar933x_serial_init(void) +{
u32 val;
/*
* Set GPIO10 (UART_SO) as output and enable UART,
* BIT(15) in GPIO_FUNCTION_1 register must be written with 1
*/
val = ar933x_read(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_OE);
val |= BIT(10);
ar933x_write(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_OE, val);
val = ar933x_read(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_FUNC);
val |= (AR933X_GPIO_FUNC_UART_EN | BIT(15));
ar933x_write(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_FUNC, val);
/*
* UART controller configuration:
* - no DMA
* - no interrupt
* - DCE mode
* - no flow control
* - set RX ready oride
* - set TX ready oride
*/
val = AR933X_UART_CS_TX_READY_ORIDE | AR933X_UART_CS_RX_READY_ORIDE
| (AR933X_UART_CS_IF_MODE_DCE << AR933X_UART_CS_IF_MODE_S);
ar933x_write(AR933X_UART_BASE, AR933X_UART_CS_REG, val);
return 0;
+}
+#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_setbrg(struct udevice *dev, int baudrate) +{ +#else +static void ar933x_serial_setbrg(void) +{
int baudrate = gd->baudrate;
+#endif
u32 val, scale, step;
const struct ar933x_serial_baudrate *baudrate_table;
int i, baudrate_table_size;
val = ar933x_read(AR71XX_RESET_BASE, AR933X_RESET_REG_BOOTSTRAP);
if (val & AR933X_BOOTSTRAP_REF_CLK_40) {
baudrate_table = baudrate_table_40mhz;
baudrate_table_size = ARRAY_SIZE(baudrate_table_40mhz);
scale = (40000000 / (16 * baudrate)) - 1;
step = 8192;
} else {
baudrate_table = baudrate_table_25mhz;
baudrate_table_size = ARRAY_SIZE(baudrate_table_25mhz);
scale = (25000000 / (16 * baudrate)) - 1;
step = 8192;
}
for (i = 0; i < baudrate_table_size; i++) {
if (baudrate_table[i].baudrate == gd->baudrate) {
scale = baudrate_table[i].scale;
step = baudrate_table[i].step;
}
}
val = ((scale & AR933X_UART_CLOCK_SCALE_M)
<< AR933X_UART_CLOCK_SCALE_S);
val |= ((step & AR933X_UART_CLOCK_STEP_M)
<< AR933X_UART_CLOCK_STEP_S);
ar933x_write(AR933X_UART_BASE, AR933X_UART_CLOCK_REG, val);
+#ifdef CONFIG_DM_SERIAL
return 0;
+#endif +}
+#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_putc(struct udevice *dev, const char c) +#else +static void ar933x_serial_putc(const char c) +#endif +{
u32 data;
if (c == '\n')
+#ifdef CONFIG_DM_SERIAL
ar933x_serial_putc(dev, '\r');
With driver model the uclass does this for you.
+#else
ar933x_serial_putc('\r');
+#endif
do {
data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG);
} while (!(data & AR933X_UART_DATA_TX_CSR));
You should not loop here - the uclass does it. Just return -EAGAIN.
data = (u32)c | AR933X_UART_DATA_TX_CSR;
ar933x_write(AR933X_UART_BASE, AR933X_UART_DATA_REG, data);
+#ifdef CONFIG_DM_SERIAL
return 0;
+#endif +}
+#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_getc(struct udevice *dev) +#else +static int ar933x_serial_getc(void) +#endif +{
u32 data;
do {
data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG);
} while (!(data & AR933X_UART_DATA_RX_CSR));
You should not loop here - the uclass does it. Just return -EAGAIN.
data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG);
ar933x_write(AR933X_UART_BASE, AR933X_UART_DATA_REG, AR933X_UART_DATA_RX_CSR);
return data & AR933X_UART_DATA_TX_RX_MASK;
+}
+#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_pending(struct udevice *dev, bool input) +{
u32 data;
data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG);
if (input)
return (data & AR933X_UART_DATA_RX_CSR) ? 1 : 0;
else
return (data & AR933X_UART_DATA_TX_CSR) ? 0 : 1;
+}
+static int ar933x_serial_probe(struct udevice *dev) +{
ar933x_serial_init();
return 0;
+}
+static const struct dm_serial_ops ar933x_serial_ops = {
.putc = ar933x_serial_putc,
.pending = ar933x_serial_pending,
.getc = ar933x_serial_getc,
.setbrg = ar933x_serial_setbrg,
+};
+static const struct udevice_id ar933x_serial_ids[] = {
{ .compatible = "ath79,ar933x-uart" },
{ }
+};
+U_BOOT_DRIVER(serial_ar933x) = {
.name = "serial_ar933x",
.id = UCLASS_SERIAL,
.of_match = ar933x_serial_ids,
.probe = ar933x_serial_probe,
.ops = &ar933x_serial_ops,
.flags = DM_FLAG_PRE_RELOC,
+}; +#else +static int ar933x_serial_tstc(void) +{
u32 data;
data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG);
return (data & AR933X_UART_DATA_RX_CSR) ? 1 : 0;
+}
+static struct serial_device ar933x_serial_drv = {
.name = "ar933x_serial",
.start = ar933x_serial_init,
.stop = NULL,
.setbrg = ar933x_serial_setbrg,
.putc = ar933x_serial_putc,
.puts = default_serial_puts,
.getc = ar933x_serial_getc,
.tstc = ar933x_serial_tstc,
+};
+void ar933x_serial_initialize(void) +{
serial_register(&ar933x_serial_drv);
+}
+__weak struct serial_device *default_serial_console(void) +{
return &ar933x_serial_drv;
+}
+#endif
1.9.1
Regards, SImon

On 01/06/2016 08:25 AM, Simon Glass wrote:
Hi WIlls,
On 24 December 2015 at 04:22, Wills Wang wills.wang@live.com wrote:
Signed-off-by: Wills Wang wills.wang@live.com
Changes in v3: None Changes in v2: None
drivers/serial/Makefile | 1 + drivers/serial/serial_ar933x.c | 274 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 275 insertions(+) create mode 100644 drivers/serial/serial_ar933x.c
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index dd87147..9a7ad89 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -17,6 +17,7 @@ endif
obj-$(CONFIG_ALTERA_UART) += altera_uart.o obj-$(CONFIG_ALTERA_JTAG_UART) += altera_jtag_uart.o +obj-$(CONFIG_AR933X_SERIAL) += serial_ar933x.o obj-$(CONFIG_ARM_DCC) += arm_dcc.o obj-$(CONFIG_ATMEL_USART) += atmel_usart.o obj-$(CONFIG_EFI_APP) += serial_efi.o diff --git a/drivers/serial/serial_ar933x.c b/drivers/serial/serial_ar933x.c new file mode 100644 index 0000000..6c0d726 --- /dev/null +++ b/drivers/serial/serial_ar933x.c @@ -0,0 +1,274 @@ +/*
- (C) Copyright 2015
- Wills Wang, wills.wang@live.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <serial.h> +#include <dm.h> +#include <errno.h> +#include <asm/io.h> +#include <asm/addrspace.h> +#include <asm/types.h> +#include <asm/arch/ar71xx_regs.h> +#include <asm/arch/ar933x_uart.h>
+DECLARE_GLOBAL_DATA_PTR;
+struct ar933x_serial_baudrate{
u32 baudrate;
u32 scale;
u32 step;
+};
+const struct ar933x_serial_baudrate baudrate_table_40mhz[] = { +/* baudrate, scale, step */
{600, 255, 503},
{1200, 249, 983},
{2400, 167, 1321},
{4800, 87, 1384},
{9600, 45, 1447},
{14400, 53, 2548},
{19200, 22, 1447},
{28800, 26, 2548},
{38400, 28, 3649},
{56000, 7, 1468},
{57600, 34, 6606},
{115200, 28, 10947},
{128000, 6, 2936},
{153600, 18, 9563},
{230400, 16, 12834},
{250000, 4, 4096},
{256000, 6, 5872},
{460800, 7, 12079},
{576000, 4, 9437},
{921600, 3, 12079},
{1000000, 2, 9830},
{1152000, 2, 11324},
{1500000, 0, 4915},
{2000000, 0, 6553},
- };
+const struct ar933x_serial_baudrate baudrate_table_25mhz[] = { +/* baudrate, scale, step */
{600, 255, 805},
{1200, 209, 1321},
{2400, 104, 1321},
{4800, 54, 1384},
{9600, 78, 3976},
{14400, 98, 7474},
{19200, 55, 5637},
{28800, 77, 11777},
{38400, 36, 7449},
{56000, 4, 1468},
{57600, 35, 10871},
{115200, 20, 12683},
{128000, 11, 8053},
{153600, 9, 8053},
{230400, 9, 12079},
{250000, 6, 9175},
{256000, 5, 8053},
{460800, 4, 12079},
{576000, 3, 12079},
{921600, 1, 9663},
{1000000, 1, 10485},
{1152000, 1, 12079},
{1500000, 0, 7864},
{2000000, 0, 10485},
+};
+static inline u32 ar933x_read(u32 base, u32 offset) +{
return readl(KSEG1ADDR(base + offset));
+}
+static inline void ar933x_write(u32 base, u32 offset, u32 val) +{
writel(val, KSEG1ADDR(base + offset));
+}
+static int ar933x_serial_init(void) +{
u32 val;
/*
* Set GPIO10 (UART_SO) as output and enable UART,
* BIT(15) in GPIO_FUNCTION_1 register must be written with 1
*/
val = ar933x_read(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_OE);
val |= BIT(10);
ar933x_write(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_OE, val);
val = ar933x_read(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_FUNC);
val |= (AR933X_GPIO_FUNC_UART_EN | BIT(15));
ar933x_write(AR71XX_GPIO_BASE, AR71XX_GPIO_REG_FUNC, val);
/*
* UART controller configuration:
* - no DMA
* - no interrupt
* - DCE mode
* - no flow control
* - set RX ready oride
* - set TX ready oride
*/
val = AR933X_UART_CS_TX_READY_ORIDE | AR933X_UART_CS_RX_READY_ORIDE
| (AR933X_UART_CS_IF_MODE_DCE << AR933X_UART_CS_IF_MODE_S);
ar933x_write(AR933X_UART_BASE, AR933X_UART_CS_REG, val);
return 0;
+}
+#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_setbrg(struct udevice *dev, int baudrate) +{ +#else +static void ar933x_serial_setbrg(void) +{
int baudrate = gd->baudrate;
+#endif
u32 val, scale, step;
const struct ar933x_serial_baudrate *baudrate_table;
int i, baudrate_table_size;
val = ar933x_read(AR71XX_RESET_BASE, AR933X_RESET_REG_BOOTSTRAP);
if (val & AR933X_BOOTSTRAP_REF_CLK_40) {
baudrate_table = baudrate_table_40mhz;
baudrate_table_size = ARRAY_SIZE(baudrate_table_40mhz);
scale = (40000000 / (16 * baudrate)) - 1;
step = 8192;
} else {
baudrate_table = baudrate_table_25mhz;
baudrate_table_size = ARRAY_SIZE(baudrate_table_25mhz);
scale = (25000000 / (16 * baudrate)) - 1;
step = 8192;
}
for (i = 0; i < baudrate_table_size; i++) {
if (baudrate_table[i].baudrate == gd->baudrate) {
scale = baudrate_table[i].scale;
step = baudrate_table[i].step;
}
}
val = ((scale & AR933X_UART_CLOCK_SCALE_M)
<< AR933X_UART_CLOCK_SCALE_S);
val |= ((step & AR933X_UART_CLOCK_STEP_M)
<< AR933X_UART_CLOCK_STEP_S);
ar933x_write(AR933X_UART_BASE, AR933X_UART_CLOCK_REG, val);
+#ifdef CONFIG_DM_SERIAL
return 0;
+#endif +}
+#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_putc(struct udevice *dev, const char c) +#else +static void ar933x_serial_putc(const char c) +#endif +{
u32 data;
if (c == '\n')
+#ifdef CONFIG_DM_SERIAL
ar933x_serial_putc(dev, '\r');
With driver model the uclass does this for you.
+#else
ar933x_serial_putc('\r');
+#endif
do {
data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG);
} while (!(data & AR933X_UART_DATA_TX_CSR));
You should not loop here - the uclass does it. Just return -EAGAIN.
data = (u32)c | AR933X_UART_DATA_TX_CSR;
ar933x_write(AR933X_UART_BASE, AR933X_UART_DATA_REG, data);
+#ifdef CONFIG_DM_SERIAL
return 0;
+#endif +}
+#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_getc(struct udevice *dev) +#else +static int ar933x_serial_getc(void) +#endif +{
u32 data;
do {
data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG);
} while (!(data & AR933X_UART_DATA_RX_CSR));
You should not loop here - the uclass does it. Just return -EAGAIN.
data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG);
ar933x_write(AR933X_UART_BASE, AR933X_UART_DATA_REG, AR933X_UART_DATA_RX_CSR);
return data & AR933X_UART_DATA_TX_RX_MASK;
+}
+#ifdef CONFIG_DM_SERIAL +static int ar933x_serial_pending(struct udevice *dev, bool input) +{
u32 data;
data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG);
if (input)
return (data & AR933X_UART_DATA_RX_CSR) ? 1 : 0;
else
return (data & AR933X_UART_DATA_TX_CSR) ? 0 : 1;
+}
+static int ar933x_serial_probe(struct udevice *dev) +{
ar933x_serial_init();
return 0;
+}
+static const struct dm_serial_ops ar933x_serial_ops = {
.putc = ar933x_serial_putc,
.pending = ar933x_serial_pending,
.getc = ar933x_serial_getc,
.setbrg = ar933x_serial_setbrg,
+};
+static const struct udevice_id ar933x_serial_ids[] = {
{ .compatible = "ath79,ar933x-uart" },
{ }
+};
+U_BOOT_DRIVER(serial_ar933x) = {
.name = "serial_ar933x",
.id = UCLASS_SERIAL,
.of_match = ar933x_serial_ids,
.probe = ar933x_serial_probe,
.ops = &ar933x_serial_ops,
.flags = DM_FLAG_PRE_RELOC,
+}; +#else +static int ar933x_serial_tstc(void) +{
u32 data;
data = ar933x_read(AR933X_UART_BASE, AR933X_UART_DATA_REG);
return (data & AR933X_UART_DATA_RX_CSR) ? 1 : 0;
+}
+static struct serial_device ar933x_serial_drv = {
.name = "ar933x_serial",
.start = ar933x_serial_init,
.stop = NULL,
.setbrg = ar933x_serial_setbrg,
.putc = ar933x_serial_putc,
.puts = default_serial_puts,
.getc = ar933x_serial_getc,
.tstc = ar933x_serial_tstc,
+};
+void ar933x_serial_initialize(void) +{
serial_register(&ar933x_serial_drv);
+}
+__weak struct serial_device *default_serial_console(void) +{
return &ar933x_serial_drv;
+}
+#endif
1.9.1
Regards, SImon
Ok. I have corrected them in the latest V6 patch. Please help me to review the V6 patch.
participants (3)
-
Simon Glass
-
Thomas Chou
-
Wills Wang