diff mbox

[U-Boot,3/7] STiH410: Add STi serial driver

Message ID 1486054841-8982-4-git-send-email-patrice.chotard@st.com
State Superseded
Delegated to: Tom Rini
Headers show

Commit Message

Patrice CHOTARD Feb. 2, 2017, 5 p.m. UTC
From: Patrice Chotard <patrice.chotard@st.com>

This patch adds support to ASC (asynchronous serial controller)
driver, which is basically a standard serial driver. This IP
is common across other STMicroelectronics SoCs

Signed-off-by: Patrice Chotard <patrice.chotard@st.com>
---
 arch/arm/Kconfig                          |   2 +
 arch/arm/include/asm/arch-stih410/sti.h   |   6 +
 board/st/stih410-b2260/board.c            |  12 ++
 drivers/serial/Kconfig                    |   7 +
 drivers/serial/Makefile                   |   1 +
 drivers/serial/serial_sti_asc.c           | 219 ++++++++++++++++++++++++++++++
 include/configs/stih410-b2260.h           |   1 +
 include/dm/platform_data/serial_sti_asc.h |  17 +++
 8 files changed, 265 insertions(+)
 create mode 100644 drivers/serial/serial_sti_asc.c
 create mode 100644 include/dm/platform_data/serial_sti_asc.h

Comments

Simon Glass Feb. 10, 2017, 4:22 p.m. UTC | #1
Hi Patrice,

On 2 February 2017 at 10:00,  <patrice.chotard@st.com> wrote:
> From: Patrice Chotard <patrice.chotard@st.com>
>
> This patch adds support to ASC (asynchronous serial controller)
> driver, which is basically a standard serial driver. This IP
> is common across other STMicroelectronics SoCs
>
> Signed-off-by: Patrice Chotard <patrice.chotard@st.com>
> ---
>  arch/arm/Kconfig                          |   2 +
>  arch/arm/include/asm/arch-stih410/sti.h   |   6 +
>  board/st/stih410-b2260/board.c            |  12 ++
>  drivers/serial/Kconfig                    |   7 +
>  drivers/serial/Makefile                   |   1 +
>  drivers/serial/serial_sti_asc.c           | 219 ++++++++++++++++++++++++++++++
>  include/configs/stih410-b2260.h           |   1 +
>  include/dm/platform_data/serial_sti_asc.h |  17 +++
>  8 files changed, 265 insertions(+)
>  create mode 100644 drivers/serial/serial_sti_asc.c
>  create mode 100644 include/dm/platform_data/serial_sti_asc.h
>
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index 4aa5eb9..b91a5b7 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -985,6 +985,8 @@ config STM32
>  config ARCH_STI
>         bool "Support STMicrolectronics SoCs"
>         select CPU_V7
> +       select DM
> +       select DM_SERIAL
>         help
>           Support for STMicroelectronics STiH407/10 SoC family.
>           This SoC is used on Linaro 96Board STiH410-B2260
> diff --git a/arch/arm/include/asm/arch-stih410/sti.h b/arch/arm/include/asm/arch-stih410/sti.h
> index d35c4f0..f167560 100644
> --- a/arch/arm/include/asm/arch-stih410/sti.h
> +++ b/arch/arm/include/asm/arch-stih410/sti.h
> @@ -11,4 +11,10 @@
>  #define STI_A9_CONFIG_BASE                     0x08760000
>  #define STI_A9_GLOBAL_TIMER_BASE               (STI_A9_CONFIG_BASE + 0x0200)
>
> +/* STiH410 control registers */
> +#define STIH410_COMMS_BASE                     0x09800000
> +
> +/* ASC UART located in the main "COMMs" block */
> +#define STIH410_ASC1_BASE (STIH410_COMMS_BASE + 0x00031000)

Can you get the address of the serial port from the device tree
instead, e.g. with dev_get_addr_ptr()?

> +
>  #endif /* _STI_H_ */
> diff --git a/board/st/stih410-b2260/board.c b/board/st/stih410-b2260/board.c
> index 0c06bca..b2cfa7f 100644
> --- a/board/st/stih410-b2260/board.c
> +++ b/board/st/stih410-b2260/board.c
> @@ -7,6 +7,9 @@
>   */
>
>  #include <common.h>
> +#include <asm/arch/sti.h>
> +#include <dm/platdata.h>
> +#include <dm/platform_data/serial_sti_asc.h>
>
>  DECLARE_GLOBAL_DATA_PTR;
>
> @@ -26,3 +29,12 @@ int board_init(void)
>  {
>         return 0;
>  }
> +
> +static const struct sti_asc_serial_platdata serial_platdata = {
> +       .base = (struct sti_asc_uart *)STIH410_ASC1_BASE,
> +};
> +
> +U_BOOT_DEVICE(sti_asc) = {
> +       .name = "serial_sti_asc",
> +       .platdata = &serial_platdata,
> +};

Can you use DT instead of platform data? You have the code for it, so
why is this needed?

> diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
> index b11f3ff..7557632 100644
> --- a/drivers/serial/Kconfig
> +++ b/drivers/serial/Kconfig
> @@ -413,4 +413,11 @@ config PXA_SERIAL
>           If you have a machine based on a Marvell XScale PXA2xx CPU you
>           can enable its onboard serial ports by enabling this option.
>
> +config STI_ASC_SERIAL
> +       bool "STMicroelectronics on-chip UART"
> +       depends on DM_SERIAL && ARCH_STI
> +       help
> +         Select this to enable Asynchronous Serial Controller available
> +         on STiH410 SoC.

Anything more to say? Maximum baud rate, or what hardware features the
driver supports? E.g. it only seems to support a subset of baud rates.

> +
>  endmenu
> diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
> index 8430668..84a22ce 100644
> --- a/drivers/serial/Makefile
> +++ b/drivers/serial/Makefile
> @@ -41,6 +41,7 @@ obj-$(CONFIG_FSL_LINFLEXUART) += serial_linflexuart.o
>  obj-$(CONFIG_ARC_SERIAL) += serial_arc.o
>  obj-$(CONFIG_UNIPHIER_SERIAL) += serial_uniphier.o
>  obj-$(CONFIG_STM32_SERIAL) += serial_stm32.o
> +obj-$(CONFIG_STI_ASC_SERIAL) += serial_sti_asc.o
>  obj-$(CONFIG_PIC32_SERIAL) += serial_pic32.o
>  obj-$(CONFIG_STM32X7_SERIAL) += serial_stm32x7.o
>  obj-$(CONFIG_BCM283X_MU_SERIAL) += serial_bcm283x_mu.o
> diff --git a/drivers/serial/serial_sti_asc.c b/drivers/serial/serial_sti_asc.c
> new file mode 100644
> index 0000000..5279836
> --- /dev/null
> +++ b/drivers/serial/serial_sti_asc.c
> @@ -0,0 +1,219 @@
> +/*
> + * Support for Serial I/O using STMicroelectronics' on-chip ASC.
> + *
> + *  Copyright (c) 2017
> + *  Patrice Chotard <patrice.chotard@st.com>
> + *
> + * SPDX-License-Identifier:    GPL-2.0
> + */
> +
> +#include <common.h>
> +#include <dm.h>
> +#include <serial.h>
> +#include <asm/arch/sti.h>
> +#include <asm/io.h>

move that up one line

> +#include <dm/platform_data/serial_sti_asc.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +#define BAUDMODE       0x00001000
> +#define RXENABLE       0x00000100
> +#define RUN            0x00000080
> +#define MODE           0x00000001
> +#define MODE_8BIT      0x0001
> +#define STOP_1BIT      0x0008
> +#define PARITYODD      0x0020
> +
> +#define STA_TF         0x0200
> +#define STA_RBF                0x0001

You could use BIT() if you like

> +
> +struct sti_asc_uart {
> +       u32 baudrate;
> +       u32 txbuf;
> +       u32 rxbuf;
> +       u32 control;
> +       u32 inten;
> +       u32 status;
> +       u32 guardtime;
> +       u32 timeout;
> +       u32 txreset;
> +       u32 rxreset;
> +};
> +
> +/*---- Values for the BAUDRATE Register -----------------------*/

/* Values for the BAUDRATE Register */

> +#define PCLK                   (200ul * 1000000ul)
> +#define BAUDRATE_VAL_M0(bps)   (PCLK / (16 * (bps)))
> +#define BAUDRATE_VAL_M1(bps)   ((bps * (1 << 14)) + (1<<13)) / (PCLK/(1 << 6))
> +
> +/*
> + * MODE 0
> + *                       ICCLK
> + * ASCBaudRate =   ----------------
> + *                   baudrate * 16
> + *
> + * MODE 1
> + *                   baudrate * 16 * 2^16
> + * ASCBaudRate =   ------------------------
> + *                          ICCLK
> + *
> + * NOTE:
> + * Mode 1 should be used for baudrates of 19200, and above, as it
> + * has a lower deviation error than Mode 0 for higher frequencies.
> + * Mode 0 should be used for all baudrates below 19200.
> + */
> +
> +static int sti_asc_pending(struct udevice *dev, bool input)
> +{
> +       struct sti_asc_serial_platdata *plat = dev->platdata;
> +       struct sti_asc_uart *const uart = plat->base;
> +       unsigned long status;
> +
> +       status = readl(&uart->status);
> +       if (input)
> +               return status & STA_RBF;
> +       else
> +               return status & STA_TF;
> +}
> +
> +/* called to adjust baud-rate */
> +static int sti_asc_serial_setbrg(struct udevice *dev, int baudrate)
> +{
> +       struct sti_asc_serial_platdata *plat = dev->platdata;
> +       struct sti_asc_uart *const uart = plat->base;
> +

drop blank line

> +       unsigned long val;
> +       int t, mode = 1;
> +
> +       switch (baudrate) {
> +       case 9600:
> +               t = BAUDRATE_VAL_M0(9600);
> +               mode = 0;
> +               break;
> +       case 19200:
> +               t = BAUDRATE_VAL_M1(19200);
> +               break;
> +       case 38400:
> +               t = BAUDRATE_VAL_M1(38400);
> +               break;
> +       case 57600:
> +               t = BAUDRATE_VAL_M1(57600);
> +               break;
> +       default:
> +               printf("ASC: unsupported baud rate: %d, using 115200 instead.\n",
> +                      baudrate);

debug() ?

> +       case 115200:
> +               t = BAUDRATE_VAL_M1(115200);
> +               break;
> +       }
> +
> +       /* wait for end of current transmission */
> +       while (sti_asc_pending(dev, false))
> +               ;

Hmm this should not be here. We should be able to call
serial_pending() to check that the device is idle, then avoid the loop
here.

Maybe there is something needed in serial_uclass.c?

> +
> +       /* disable the baudrate generator */
> +       val = readl(&uart->control);
> +       writel((val & ~RUN), &uart->control);

Drop extra brackets

> +
> +       /* set baud generator reload value */
> +       writel(t, &uart->baudrate);
> +       /* reset the RX & TX buffers */
> +       writel(1, &uart->txreset);
> +       writel(1, &uart->rxreset);
> +
> +       /* set baud generator mode */
> +       if (mode)
> +               val |= BAUDMODE;
> +
> +       /* finally, write value and enable ASC */
> +       writel(val, &uart->control);
> +
> +       return 0;
> +}
> +
> +/* initialize the ASC */
> +static int sti_asc_serial_probe(struct udevice *dev)
> +{
> +       struct sti_asc_serial_platdata *plat = dev->platdata;
> +       struct sti_asc_uart *const uart = plat->base;
> +       unsigned long val;
> +
> +       sti_asc_serial_setbrg(dev, gd->baudrate);
> +
> +       /*
> +        * build up the value to be written to CONTROL
> +        * set character length, bit stop number, odd parity
> +        */
> +       val = RXENABLE | RUN | MODE_8BIT | STOP_1BIT | PARITYODD;
> +       writel(val, &uart->control);
> +
> +       return 0;
> +}
> +
> +/* blocking function, that returns next char */
> +static int sti_asc_serial_getc(struct udevice *dev)
> +{
> +       struct sti_asc_serial_platdata *plat = dev->platdata;
> +       struct sti_asc_uart *const uart = plat->base;
> +
> +       /* polling wait: for a char to be read */
> +       if (!sti_asc_pending(dev, true))
> +               return -EAGAIN;
> +
> +       return readl(&uart->rxbuf);
> +}
> +
> +/* write write out a single char */
> +static int sti_asc_serial_putc(struct udevice *dev, const char c)
> +{
> +       struct sti_asc_serial_platdata *plat = dev->platdata;
> +       struct sti_asc_uart *const uart = plat->base;
> +
> +       /* Stream-LF to CR+LF conversion */
> +       if (c == 10)
> +               sti_asc_serial_putc(dev, '\r');

This happens in the uclass, you don't need it here.

> +
> +       /* wait till safe to write next char */
> +       while (sti_asc_pending(dev, false))
> +               ;

if (sti_asc_pending(dev, false))
   return -EAGAIN

(no loops needed / wanted in drivers)

> +
> +       /* finally, write next char */
> +       writel(c, &uart->txbuf);
> +
> +       return 0;
> +}
> +
> +static int sti_ofdata_to_platdata(struct udevice *dev)
> +{
> +       struct sti_asc_serial_platdata *priv = dev_get_priv(dev);
> +       fdt_addr_t base;
> +
> +       base = dev_get_addr(dev);
> +       if (base == FDT_ADDR_T_NONE)
> +               return -EINVAL;
> +
> +       priv->base = (struct sti_asc_uart *)base;

Can I suggest the name 'regs' for the registers? Normally 'base' is
the base address. It's the same value, but regs is a pointer and base
is a ulong.

> +
> +       return 0;
> +}
> +
> +static const struct dm_serial_ops sti_asc_serial_ops = {
> +       .putc = sti_asc_serial_putc,
> +       .pending = sti_asc_pending,
> +       .getc = sti_asc_serial_getc,
> +       .setbrg = sti_asc_serial_setbrg,
> +};
> +
> +static const struct udevice_id sti_serial_of_match[] = {
> +       { .compatible = "st,asc" },
> +       { }
> +};
> +
> +U_BOOT_DRIVER(serial_sti_asc) = {
> +       .name = "serial_sti_asc",
> +       .id = UCLASS_SERIAL,
> +       .of_match = sti_serial_of_match,
> +       .ops = &sti_asc_serial_ops,
> +       .ofdata_to_platdata = sti_ofdata_to_platdata,
> +       .probe = sti_asc_serial_probe,
> +       .flags = DM_FLAG_PRE_RELOC,
> +};
> diff --git a/include/configs/stih410-b2260.h b/include/configs/stih410-b2260.h
> index fade7a1..4fea22a 100644
> --- a/include/configs/stih410-b2260.h
> +++ b/include/configs/stih410-b2260.h
> @@ -17,6 +17,7 @@
>  #define CONFIG_SYS_LOAD_ADDR           PHYS_SDRAM_1    /* default load addr */
>
>  #define CONFIG_BAUDRATE                        115200
> +#define CONFIG_SYS_BAUDRATE_TABLE       { 9600, 19200, 38400, 57600, 115200 }
>
>  #define CONFIG_SYS_HZ_CLOCK            1000000000      /* 1 GHz */
>
> diff --git a/include/dm/platform_data/serial_sti_asc.h b/include/dm/platform_data/serial_sti_asc.h
> new file mode 100644
> index 0000000..c8631f1
> --- /dev/null
> +++ b/include/dm/platform_data/serial_sti_asc.h
> @@ -0,0 +1,17 @@
> +/*
> + * (C) Copyright 2017
> + * Patrice Chotard <patrice.chotard@st.com>
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +
> +#ifndef __SERIAL_STI_ASC_H
> +#define __SERIAL_STI_ASC_H
> +
> +/* Information about a serial port */
> +struct sti_asc_serial_platdata {
> +       /* address of registers in physical memory */
> +       struct sti_asc_uart *base;
> +};
> +
> +#endif /* __SERIAL_STI_ASC_H */
> --
> 1.9.1
>

Regards,
Simon
Patrice CHOTARD Feb. 14, 2017, 12:24 p.m. UTC | #2
Hi Simon

On 02/10/2017 05:22 PM, Simon Glass wrote:
> Hi Patrice,
>
> On 2 February 2017 at 10:00,  <patrice.chotard@st.com> wrote:
>> From: Patrice Chotard <patrice.chotard@st.com>
>>
>> This patch adds support to ASC (asynchronous serial controller)
>> driver, which is basically a standard serial driver. This IP
>> is common across other STMicroelectronics SoCs
>>
>> Signed-off-by: Patrice Chotard <patrice.chotard@st.com>
>> ---
>>  arch/arm/Kconfig                          |   2 +
>>  arch/arm/include/asm/arch-stih410/sti.h   |   6 +
>>  board/st/stih410-b2260/board.c            |  12 ++
>>  drivers/serial/Kconfig                    |   7 +
>>  drivers/serial/Makefile                   |   1 +
>>  drivers/serial/serial_sti_asc.c           | 219 ++++++++++++++++++++++++++++++
>>  include/configs/stih410-b2260.h           |   1 +
>>  include/dm/platform_data/serial_sti_asc.h |  17 +++
>>  8 files changed, 265 insertions(+)
>>  create mode 100644 drivers/serial/serial_sti_asc.c
>>  create mode 100644 include/dm/platform_data/serial_sti_asc.h
>>
>> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
>> index 4aa5eb9..b91a5b7 100644
>> --- a/arch/arm/Kconfig
>> +++ b/arch/arm/Kconfig
>> @@ -985,6 +985,8 @@ config STM32
>>  config ARCH_STI
>>         bool "Support STMicrolectronics SoCs"
>>         select CPU_V7
>> +       select DM
>> +       select DM_SERIAL
>>         help
>>           Support for STMicroelectronics STiH407/10 SoC family.
>>           This SoC is used on Linaro 96Board STiH410-B2260
>> diff --git a/arch/arm/include/asm/arch-stih410/sti.h b/arch/arm/include/asm/arch-stih410/sti.h
>> index d35c4f0..f167560 100644
>> --- a/arch/arm/include/asm/arch-stih410/sti.h
>> +++ b/arch/arm/include/asm/arch-stih410/sti.h
>> @@ -11,4 +11,10 @@
>>  #define STI_A9_CONFIG_BASE                     0x08760000
>>  #define STI_A9_GLOBAL_TIMER_BASE               (STI_A9_CONFIG_BASE + 0x0200)
>>
>> +/* STiH410 control registers */
>> +#define STIH410_COMMS_BASE                     0x09800000
>> +
>> +/* ASC UART located in the main "COMMs" block */
>> +#define STIH410_ASC1_BASE (STIH410_COMMS_BASE + 0x00031000)
>
> Can you get the address of the serial port from the device tree
> instead, e.g. with dev_get_addr_ptr()?

Will be fixed

>
>> +
>>  #endif /* _STI_H_ */
>> diff --git a/board/st/stih410-b2260/board.c b/board/st/stih410-b2260/board.c
>> index 0c06bca..b2cfa7f 100644
>> --- a/board/st/stih410-b2260/board.c
>> +++ b/board/st/stih410-b2260/board.c
>> @@ -7,6 +7,9 @@
>>   */
>>
>>  #include <common.h>
>> +#include <asm/arch/sti.h>
>> +#include <dm/platdata.h>
>> +#include <dm/platform_data/serial_sti_asc.h>
>>
>>  DECLARE_GLOBAL_DATA_PTR;
>>
>> @@ -26,3 +29,12 @@ int board_init(void)
>>  {
>>         return 0;
>>  }
>> +
>> +static const struct sti_asc_serial_platdata serial_platdata = {
>> +       .base = (struct sti_asc_uart *)STIH410_ASC1_BASE,
>> +};
>> +
>> +U_BOOT_DEVICE(sti_asc) = {
>> +       .name = "serial_sti_asc",
>> +       .platdata = &serial_platdata,
>> +};
>
> Can you use DT instead of platform data? You have the code for it, so
> why is this needed?

Yes, i simply forgot to fully convert this driver to DT, i will fix this.

>
>> diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
>> index b11f3ff..7557632 100644
>> --- a/drivers/serial/Kconfig
>> +++ b/drivers/serial/Kconfig
>> @@ -413,4 +413,11 @@ config PXA_SERIAL
>>           If you have a machine based on a Marvell XScale PXA2xx CPU you
>>           can enable its onboard serial ports by enabling this option.
>>
>> +config STI_ASC_SERIAL
>> +       bool "STMicroelectronics on-chip UART"
>> +       depends on DM_SERIAL && ARCH_STI
>> +       help
>> +         Select this to enable Asynchronous Serial Controller available
>> +         on STiH410 SoC.
>
> Anything more to say? Maximum baud rate, or what hardware features the
> driver supports? E.g. it only seems to support a subset of baud rates.

Will be fixed

>
>> +
>>  endmenu
>> diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
>> index 8430668..84a22ce 100644
>> --- a/drivers/serial/Makefile
>> +++ b/drivers/serial/Makefile
>> @@ -41,6 +41,7 @@ obj-$(CONFIG_FSL_LINFLEXUART) += serial_linflexuart.o
>>  obj-$(CONFIG_ARC_SERIAL) += serial_arc.o
>>  obj-$(CONFIG_UNIPHIER_SERIAL) += serial_uniphier.o
>>  obj-$(CONFIG_STM32_SERIAL) += serial_stm32.o
>> +obj-$(CONFIG_STI_ASC_SERIAL) += serial_sti_asc.o
>>  obj-$(CONFIG_PIC32_SERIAL) += serial_pic32.o
>>  obj-$(CONFIG_STM32X7_SERIAL) += serial_stm32x7.o
>>  obj-$(CONFIG_BCM283X_MU_SERIAL) += serial_bcm283x_mu.o
>> diff --git a/drivers/serial/serial_sti_asc.c b/drivers/serial/serial_sti_asc.c
>> new file mode 100644
>> index 0000000..5279836
>> --- /dev/null
>> +++ b/drivers/serial/serial_sti_asc.c
>> @@ -0,0 +1,219 @@
>> +/*
>> + * Support for Serial I/O using STMicroelectronics' on-chip ASC.
>> + *
>> + *  Copyright (c) 2017
>> + *  Patrice Chotard <patrice.chotard@st.com>
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0
>> + */
>> +
>> +#include <common.h>
>> +#include <dm.h>
>> +#include <serial.h>
>> +#include <asm/arch/sti.h>
>> +#include <asm/io.h>
>
> move that up one line

ok

>
>> +#include <dm/platform_data/serial_sti_asc.h>
>> +
>> +DECLARE_GLOBAL_DATA_PTR;
>> +
>> +#define BAUDMODE       0x00001000
>> +#define RXENABLE       0x00000100
>> +#define RUN            0x00000080
>> +#define MODE           0x00000001
>> +#define MODE_8BIT      0x0001
>> +#define STOP_1BIT      0x0008
>> +#define PARITYODD      0x0020
>> +
>> +#define STA_TF         0x0200
>> +#define STA_RBF                0x0001
>
> You could use BIT() if you like

ok

>
>> +
>> +struct sti_asc_uart {
>> +       u32 baudrate;
>> +       u32 txbuf;
>> +       u32 rxbuf;
>> +       u32 control;
>> +       u32 inten;
>> +       u32 status;
>> +       u32 guardtime;
>> +       u32 timeout;
>> +       u32 txreset;
>> +       u32 rxreset;
>> +};
>> +
>> +/*---- Values for the BAUDRATE Register -----------------------*/
>
> /* Values for the BAUDRATE Register */

ok

>
>> +#define PCLK                   (200ul * 1000000ul)
>> +#define BAUDRATE_VAL_M0(bps)   (PCLK / (16 * (bps)))
>> +#define BAUDRATE_VAL_M1(bps)   ((bps * (1 << 14)) + (1<<13)) / (PCLK/(1 << 6))
>> +
>> +/*
>> + * MODE 0
>> + *                       ICCLK
>> + * ASCBaudRate =   ----------------
>> + *                   baudrate * 16
>> + *
>> + * MODE 1
>> + *                   baudrate * 16 * 2^16
>> + * ASCBaudRate =   ------------------------
>> + *                          ICCLK
>> + *
>> + * NOTE:
>> + * Mode 1 should be used for baudrates of 19200, and above, as it
>> + * has a lower deviation error than Mode 0 for higher frequencies.
>> + * Mode 0 should be used for all baudrates below 19200.
>> + */
>> +
>> +static int sti_asc_pending(struct udevice *dev, bool input)
>> +{
>> +       struct sti_asc_serial_platdata *plat = dev->platdata;
>> +       struct sti_asc_uart *const uart = plat->base;
>> +       unsigned long status;
>> +
>> +       status = readl(&uart->status);
>> +       if (input)
>> +               return status & STA_RBF;
>> +       else
>> +               return status & STA_TF;
>> +}
>> +
>> +/* called to adjust baud-rate */
>> +static int sti_asc_serial_setbrg(struct udevice *dev, int baudrate)
>> +{
>> +       struct sti_asc_serial_platdata *plat = dev->platdata;
>> +       struct sti_asc_uart *const uart = plat->base;
>> +
>
> drop blank line

ok

>
>> +       unsigned long val;
>> +       int t, mode = 1;
>> +
>> +       switch (baudrate) {
>> +       case 9600:
>> +               t = BAUDRATE_VAL_M0(9600);
>> +               mode = 0;
>> +               break;
>> +       case 19200:
>> +               t = BAUDRATE_VAL_M1(19200);
>> +               break;
>> +       case 38400:
>> +               t = BAUDRATE_VAL_M1(38400);
>> +               break;
>> +       case 57600:
>> +               t = BAUDRATE_VAL_M1(57600);
>> +               break;
>> +       default:
>> +               printf("ASC: unsupported baud rate: %d, using 115200 instead.\n",
>> +                      baudrate);
>
> debug() ?

ok

>
>> +       case 115200:
>> +               t = BAUDRATE_VAL_M1(115200);
>> +               break;
>> +       }
>> +
>> +       /* wait for end of current transmission */
>> +       while (sti_asc_pending(dev, false))
>> +               ;
>
> Hmm this should not be here. We should be able to call
> serial_pending() to check that the device is idle, then avoid the loop
> here.
>
> Maybe there is something needed in serial_uclass.c?

you are right, it's useless

>
>> +
>> +       /* disable the baudrate generator */
>> +       val = readl(&uart->control);
>> +       writel((val & ~RUN), &uart->control);
>
> Drop extra brackets

ok

>
>> +
>> +       /* set baud generator reload value */
>> +       writel(t, &uart->baudrate);
>> +       /* reset the RX & TX buffers */
>> +       writel(1, &uart->txreset);
>> +       writel(1, &uart->rxreset);
>> +
>> +       /* set baud generator mode */
>> +       if (mode)
>> +               val |= BAUDMODE;
>> +
>> +       /* finally, write value and enable ASC */
>> +       writel(val, &uart->control);
>> +
>> +       return 0;
>> +}
>> +
>> +/* initialize the ASC */
>> +static int sti_asc_serial_probe(struct udevice *dev)
>> +{
>> +       struct sti_asc_serial_platdata *plat = dev->platdata;
>> +       struct sti_asc_uart *const uart = plat->base;
>> +       unsigned long val;
>> +
>> +       sti_asc_serial_setbrg(dev, gd->baudrate);
>> +
>> +       /*
>> +        * build up the value to be written to CONTROL
>> +        * set character length, bit stop number, odd parity
>> +        */
>> +       val = RXENABLE | RUN | MODE_8BIT | STOP_1BIT | PARITYODD;
>> +       writel(val, &uart->control);
>> +
>> +       return 0;
>> +}
>> +
>> +/* blocking function, that returns next char */
>> +static int sti_asc_serial_getc(struct udevice *dev)
>> +{
>> +       struct sti_asc_serial_platdata *plat = dev->platdata;
>> +       struct sti_asc_uart *const uart = plat->base;
>> +
>> +       /* polling wait: for a char to be read */
>> +       if (!sti_asc_pending(dev, true))
>> +               return -EAGAIN;
>> +
>> +       return readl(&uart->rxbuf);
>> +}
>> +
>> +/* write write out a single char */
>> +static int sti_asc_serial_putc(struct udevice *dev, const char c)
>> +{
>> +       struct sti_asc_serial_platdata *plat = dev->platdata;
>> +       struct sti_asc_uart *const uart = plat->base;
>> +
>> +       /* Stream-LF to CR+LF conversion */
>> +       if (c == 10)
>> +               sti_asc_serial_putc(dev, '\r');
>
> This happens in the uclass, you don't need it here.

ok

>
>> +
>> +       /* wait till safe to write next char */
>> +       while (sti_asc_pending(dev, false))
>> +               ;
>
> if (sti_asc_pending(dev, false))
>    return -EAGAIN
>
> (no loops needed / wanted in drivers)

ok

>
>> +
>> +       /* finally, write next char */
>> +       writel(c, &uart->txbuf);
>> +
>> +       return 0;
>> +}
>> +
>> +static int sti_ofdata_to_platdata(struct udevice *dev)
>> +{
>> +       struct sti_asc_serial_platdata *priv = dev_get_priv(dev);
>> +       fdt_addr_t base;
>> +
>> +       base = dev_get_addr(dev);
>> +       if (base == FDT_ADDR_T_NONE)
>> +               return -EINVAL;
>> +
>> +       priv->base = (struct sti_asc_uart *)base;
>
> Can I suggest the name 'regs' for the registers? Normally 'base' is
> the base address. It's the same value, but regs is a pointer and base
> is a ulong.

ok will fix this

>
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct dm_serial_ops sti_asc_serial_ops = {
>> +       .putc = sti_asc_serial_putc,
>> +       .pending = sti_asc_pending,
>> +       .getc = sti_asc_serial_getc,
>> +       .setbrg = sti_asc_serial_setbrg,
>> +};
>> +
>> +static const struct udevice_id sti_serial_of_match[] = {
>> +       { .compatible = "st,asc" },
>> +       { }
>> +};
>> +
>> +U_BOOT_DRIVER(serial_sti_asc) = {
>> +       .name = "serial_sti_asc",
>> +       .id = UCLASS_SERIAL,
>> +       .of_match = sti_serial_of_match,
>> +       .ops = &sti_asc_serial_ops,
>> +       .ofdata_to_platdata = sti_ofdata_to_platdata,
>> +       .probe = sti_asc_serial_probe,
>> +       .flags = DM_FLAG_PRE_RELOC,
>> +};
>> diff --git a/include/configs/stih410-b2260.h b/include/configs/stih410-b2260.h
>> index fade7a1..4fea22a 100644
>> --- a/include/configs/stih410-b2260.h
>> +++ b/include/configs/stih410-b2260.h
>> @@ -17,6 +17,7 @@
>>  #define CONFIG_SYS_LOAD_ADDR           PHYS_SDRAM_1    /* default load addr */
>>
>>  #define CONFIG_BAUDRATE                        115200
>> +#define CONFIG_SYS_BAUDRATE_TABLE       { 9600, 19200, 38400, 57600, 115200 }
>>
>>  #define CONFIG_SYS_HZ_CLOCK            1000000000      /* 1 GHz */
>>
>> diff --git a/include/dm/platform_data/serial_sti_asc.h b/include/dm/platform_data/serial_sti_asc.h
>> new file mode 100644
>> index 0000000..c8631f1
>> --- /dev/null
>> +++ b/include/dm/platform_data/serial_sti_asc.h
>> @@ -0,0 +1,17 @@
>> +/*
>> + * (C) Copyright 2017
>> + * Patrice Chotard <patrice.chotard@st.com>
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0+
>> + */
>> +
>> +#ifndef __SERIAL_STI_ASC_H
>> +#define __SERIAL_STI_ASC_H
>> +
>> +/* Information about a serial port */
>> +struct sti_asc_serial_platdata {
>> +       /* address of registers in physical memory */
>> +       struct sti_asc_uart *base;
>> +};
>> +
>> +#endif /* __SERIAL_STI_ASC_H */
>> --
>> 1.9.1
>>
>
> Regards,
> Simon
>
diff mbox

Patch

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 4aa5eb9..b91a5b7 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -985,6 +985,8 @@  config STM32
 config ARCH_STI
 	bool "Support STMicrolectronics SoCs"
 	select CPU_V7
+	select DM
+	select DM_SERIAL
 	help
 	  Support for STMicroelectronics STiH407/10 SoC family.
 	  This SoC is used on Linaro 96Board STiH410-B2260
diff --git a/arch/arm/include/asm/arch-stih410/sti.h b/arch/arm/include/asm/arch-stih410/sti.h
index d35c4f0..f167560 100644
--- a/arch/arm/include/asm/arch-stih410/sti.h
+++ b/arch/arm/include/asm/arch-stih410/sti.h
@@ -11,4 +11,10 @@ 
 #define STI_A9_CONFIG_BASE			0x08760000
 #define STI_A9_GLOBAL_TIMER_BASE		(STI_A9_CONFIG_BASE + 0x0200)
 
+/* STiH410 control registers */
+#define STIH410_COMMS_BASE			0x09800000
+
+/* ASC UART located in the main "COMMs" block */
+#define STIH410_ASC1_BASE (STIH410_COMMS_BASE + 0x00031000)
+
 #endif /* _STI_H_ */
diff --git a/board/st/stih410-b2260/board.c b/board/st/stih410-b2260/board.c
index 0c06bca..b2cfa7f 100644
--- a/board/st/stih410-b2260/board.c
+++ b/board/st/stih410-b2260/board.c
@@ -7,6 +7,9 @@ 
  */
 
 #include <common.h>
+#include <asm/arch/sti.h>
+#include <dm/platdata.h>
+#include <dm/platform_data/serial_sti_asc.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -26,3 +29,12 @@  int board_init(void)
 {
 	return 0;
 }
+
+static const struct sti_asc_serial_platdata serial_platdata = {
+	.base = (struct sti_asc_uart *)STIH410_ASC1_BASE,
+};
+
+U_BOOT_DEVICE(sti_asc) = {
+	.name = "serial_sti_asc",
+	.platdata = &serial_platdata,
+};
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index b11f3ff..7557632 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -413,4 +413,11 @@  config PXA_SERIAL
 	  If you have a machine based on a Marvell XScale PXA2xx CPU you
 	  can enable its onboard serial ports by enabling this option.
 
+config STI_ASC_SERIAL
+	bool "STMicroelectronics on-chip UART"
+	depends on DM_SERIAL && ARCH_STI
+	help
+	  Select this to enable Asynchronous Serial Controller available
+	  on STiH410 SoC.
+
 endmenu
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 8430668..84a22ce 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -41,6 +41,7 @@  obj-$(CONFIG_FSL_LINFLEXUART) += serial_linflexuart.o
 obj-$(CONFIG_ARC_SERIAL) += serial_arc.o
 obj-$(CONFIG_UNIPHIER_SERIAL) += serial_uniphier.o
 obj-$(CONFIG_STM32_SERIAL) += serial_stm32.o
+obj-$(CONFIG_STI_ASC_SERIAL) += serial_sti_asc.o
 obj-$(CONFIG_PIC32_SERIAL) += serial_pic32.o
 obj-$(CONFIG_STM32X7_SERIAL) += serial_stm32x7.o
 obj-$(CONFIG_BCM283X_MU_SERIAL) += serial_bcm283x_mu.o
diff --git a/drivers/serial/serial_sti_asc.c b/drivers/serial/serial_sti_asc.c
new file mode 100644
index 0000000..5279836
--- /dev/null
+++ b/drivers/serial/serial_sti_asc.c
@@ -0,0 +1,219 @@ 
+/*
+ * Support for Serial I/O using STMicroelectronics' on-chip ASC.
+ *
+ *  Copyright (c) 2017
+ *  Patrice Chotard <patrice.chotard@st.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <serial.h>
+#include <asm/arch/sti.h>
+#include <asm/io.h>
+#include <dm/platform_data/serial_sti_asc.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define BAUDMODE	0x00001000
+#define RXENABLE	0x00000100
+#define RUN		0x00000080
+#define MODE		0x00000001
+#define MODE_8BIT	0x0001
+#define STOP_1BIT	0x0008
+#define PARITYODD	0x0020
+
+#define STA_TF		0x0200
+#define STA_RBF		0x0001
+
+struct sti_asc_uart {
+	u32 baudrate;
+	u32 txbuf;
+	u32 rxbuf;
+	u32 control;
+	u32 inten;
+	u32 status;
+	u32 guardtime;
+	u32 timeout;
+	u32 txreset;
+	u32 rxreset;
+};
+
+/*---- Values for the BAUDRATE Register -----------------------*/
+#define PCLK			(200ul * 1000000ul)
+#define BAUDRATE_VAL_M0(bps)	(PCLK / (16 * (bps)))
+#define BAUDRATE_VAL_M1(bps)	((bps * (1 << 14)) + (1<<13)) / (PCLK/(1 << 6))
+
+/*
+ * MODE 0
+ *                       ICCLK
+ * ASCBaudRate =   ----------------
+ *                   baudrate * 16
+ *
+ * MODE 1
+ *                   baudrate * 16 * 2^16
+ * ASCBaudRate =   ------------------------
+ *                          ICCLK
+ *
+ * NOTE:
+ * Mode 1 should be used for baudrates of 19200, and above, as it
+ * has a lower deviation error than Mode 0 for higher frequencies.
+ * Mode 0 should be used for all baudrates below 19200.
+ */
+
+static int sti_asc_pending(struct udevice *dev, bool input)
+{
+	struct sti_asc_serial_platdata *plat = dev->platdata;
+	struct sti_asc_uart *const uart = plat->base;
+	unsigned long status;
+
+	status = readl(&uart->status);
+	if (input)
+		return status & STA_RBF;
+	else
+		return status & STA_TF;
+}
+
+/* called to adjust baud-rate */
+static int sti_asc_serial_setbrg(struct udevice *dev, int baudrate)
+{
+	struct sti_asc_serial_platdata *plat = dev->platdata;
+	struct sti_asc_uart *const uart = plat->base;
+
+	unsigned long val;
+	int t, mode = 1;
+
+	switch (baudrate) {
+	case 9600:
+		t = BAUDRATE_VAL_M0(9600);
+		mode = 0;
+		break;
+	case 19200:
+		t = BAUDRATE_VAL_M1(19200);
+		break;
+	case 38400:
+		t = BAUDRATE_VAL_M1(38400);
+		break;
+	case 57600:
+		t = BAUDRATE_VAL_M1(57600);
+		break;
+	default:
+		printf("ASC: unsupported baud rate: %d, using 115200 instead.\n",
+		       baudrate);
+	case 115200:
+		t = BAUDRATE_VAL_M1(115200);
+		break;
+	}
+
+	/* wait for end of current transmission */
+	while (sti_asc_pending(dev, false))
+		;
+
+	/* disable the baudrate generator */
+	val = readl(&uart->control);
+	writel((val & ~RUN), &uart->control);
+
+	/* set baud generator reload value */
+	writel(t, &uart->baudrate);
+	/* reset the RX & TX buffers */
+	writel(1, &uart->txreset);
+	writel(1, &uart->rxreset);
+
+	/* set baud generator mode */
+	if (mode)
+		val |= BAUDMODE;
+
+	/* finally, write value and enable ASC */
+	writel(val, &uart->control);
+
+	return 0;
+}
+
+/* initialize the ASC */
+static int sti_asc_serial_probe(struct udevice *dev)
+{
+	struct sti_asc_serial_platdata *plat = dev->platdata;
+	struct sti_asc_uart *const uart = plat->base;
+	unsigned long val;
+
+	sti_asc_serial_setbrg(dev, gd->baudrate);
+
+	/*
+	 * build up the value to be written to CONTROL
+	 * set character length, bit stop number, odd parity
+	 */
+	val = RXENABLE | RUN | MODE_8BIT | STOP_1BIT | PARITYODD;
+	writel(val, &uart->control);
+
+	return 0;
+}
+
+/* blocking function, that returns next char */
+static int sti_asc_serial_getc(struct udevice *dev)
+{
+	struct sti_asc_serial_platdata *plat = dev->platdata;
+	struct sti_asc_uart *const uart = plat->base;
+
+	/* polling wait: for a char to be read */
+	if (!sti_asc_pending(dev, true))
+		return -EAGAIN;
+
+	return readl(&uart->rxbuf);
+}
+
+/* write write out a single char */
+static int sti_asc_serial_putc(struct udevice *dev, const char c)
+{
+	struct sti_asc_serial_platdata *plat = dev->platdata;
+	struct sti_asc_uart *const uart = plat->base;
+
+	/* Stream-LF to CR+LF conversion */
+	if (c == 10)
+		sti_asc_serial_putc(dev, '\r');
+
+	/* wait till safe to write next char */
+	while (sti_asc_pending(dev, false))
+		;
+
+	/* finally, write next char */
+	writel(c, &uart->txbuf);
+
+	return 0;
+}
+
+static int sti_ofdata_to_platdata(struct udevice *dev)
+{
+	struct sti_asc_serial_platdata *priv = dev_get_priv(dev);
+	fdt_addr_t base;
+
+	base = dev_get_addr(dev);
+	if (base == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	priv->base = (struct sti_asc_uart *)base;
+
+	return 0;
+}
+
+static const struct dm_serial_ops sti_asc_serial_ops = {
+	.putc = sti_asc_serial_putc,
+	.pending = sti_asc_pending,
+	.getc = sti_asc_serial_getc,
+	.setbrg = sti_asc_serial_setbrg,
+};
+
+static const struct udevice_id sti_serial_of_match[] = {
+	{ .compatible = "st,asc" },
+	{ }
+};
+
+U_BOOT_DRIVER(serial_sti_asc) = {
+	.name = "serial_sti_asc",
+	.id = UCLASS_SERIAL,
+	.of_match = sti_serial_of_match,
+	.ops = &sti_asc_serial_ops,
+	.ofdata_to_platdata = sti_ofdata_to_platdata,
+	.probe = sti_asc_serial_probe,
+	.flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/include/configs/stih410-b2260.h b/include/configs/stih410-b2260.h
index fade7a1..4fea22a 100644
--- a/include/configs/stih410-b2260.h
+++ b/include/configs/stih410-b2260.h
@@ -17,6 +17,7 @@ 
 #define CONFIG_SYS_LOAD_ADDR		PHYS_SDRAM_1	/* default load addr */
 
 #define CONFIG_BAUDRATE			115200
+#define CONFIG_SYS_BAUDRATE_TABLE       { 9600, 19200, 38400, 57600, 115200 }
 
 #define CONFIG_SYS_HZ_CLOCK		1000000000	/* 1 GHz */
 
diff --git a/include/dm/platform_data/serial_sti_asc.h b/include/dm/platform_data/serial_sti_asc.h
new file mode 100644
index 0000000..c8631f1
--- /dev/null
+++ b/include/dm/platform_data/serial_sti_asc.h
@@ -0,0 +1,17 @@ 
+/*
+ * (C) Copyright 2017
+ * Patrice Chotard <patrice.chotard@st.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __SERIAL_STI_ASC_H
+#define __SERIAL_STI_ASC_H
+
+/* Information about a serial port */
+struct sti_asc_serial_platdata {
+	/* address of registers in physical memory */
+	struct sti_asc_uart *base;
+};
+
+#endif /* __SERIAL_STI_ASC_H */