summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mtd/nand/raw/Kconfig1
-rw-r--r--drivers/pinctrl/uniphier/pinctrl-uniphier-core.c3
-rw-r--r--drivers/pinctrl/uniphier/pinctrl-uniphier-pxs3.c20
-rw-r--r--drivers/rtc/Kconfig9
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/ht1380.c329
-rw-r--r--drivers/serial/Kconfig9
-rw-r--r--drivers/serial/Makefile1
-rw-r--r--drivers/serial/serial_arc.c8
-rw-r--r--drivers/serial/serial_lpuart.c39
-rw-r--r--drivers/serial/serial_mpc8xx.c15
-rw-r--r--drivers/serial/serial_mvebu_a3700.c8
-rw-r--r--drivers/serial/serial_s5p4418_pl011.c94
13 files changed, 494 insertions, 43 deletions
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index 1e4e5c92067..f8b79e54561 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -197,6 +197,7 @@ config NAND_DENALI
config NAND_DENALI_DT
bool "Support Denali NAND controller as a DT device"
select NAND_DENALI
+ select SPL_SYS_NAND_SELF_INIT
depends on OF_CONTROL && DM_MTD
help
Enable the driver for NAND flash on platforms using a Denali NAND
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c
index 631bb1f963b..bdca3f2f715 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c
@@ -33,6 +33,9 @@ static int uniphier_pinctrl_get_pins_count(struct udevice *dev)
const struct uniphier_pinctrl_pin *pins = priv->socdata->pins;
int pins_count = priv->socdata->pins_count;
+ if (WARN_ON(!pins_count))
+ return 0; /* no table of pins */
+
/*
* We do not list all pins in the pin table to save memory footprint.
* Report the max pin number + 1 to fake the framework.
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-pxs3.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-pxs3.c
index 1fc7bdb5c80..8a8f1269bb5 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-pxs3.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-pxs3.c
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * Copyright (C) 2017 Socionext Inc.
+ * Copyright (C) 2017-2021 Socionext Inc.
* Author: Masahiro Yamada <yamada.masahiro@socionext.com>
+ * Author: Dai Okamura <dai.okamura@socionext.com>
*/
#include <common.h>
@@ -10,6 +11,21 @@
#include "pinctrl-uniphier.h"
+static const struct uniphier_pinctrl_pin uniphier_pxs3_pins[] = {
+ UNIPHIER_PINCTRL_PIN(62, "RGMII0_TXCLK", 28, UNIPHIER_PIN_DRV_2BIT),
+ UNIPHIER_PINCTRL_PIN(63, "RGMII0_TXD0", 29, UNIPHIER_PIN_DRV_2BIT),
+ UNIPHIER_PINCTRL_PIN(64, "RGMII0_TXD1", 30, UNIPHIER_PIN_DRV_2BIT),
+ UNIPHIER_PINCTRL_PIN(65, "RGMII0_TXD2", 31, UNIPHIER_PIN_DRV_2BIT),
+ UNIPHIER_PINCTRL_PIN(66, "RGMII0_TXD3", 32, UNIPHIER_PIN_DRV_2BIT),
+ UNIPHIER_PINCTRL_PIN(67, "RGMII0_TXCTL", 33, UNIPHIER_PIN_DRV_2BIT),
+ UNIPHIER_PINCTRL_PIN(78, "RGMII1_TXCLK", 44, UNIPHIER_PIN_DRV_2BIT),
+ UNIPHIER_PINCTRL_PIN(79, "RGMII1_TXD0", 45, UNIPHIER_PIN_DRV_2BIT),
+ UNIPHIER_PINCTRL_PIN(80, "RGMII1_TXD1", 46, UNIPHIER_PIN_DRV_2BIT),
+ UNIPHIER_PINCTRL_PIN(81, "RGMII1_TXD2", 47, UNIPHIER_PIN_DRV_2BIT),
+ UNIPHIER_PINCTRL_PIN(82, "RGMII1_TXD3", 48, UNIPHIER_PIN_DRV_2BIT),
+ UNIPHIER_PINCTRL_PIN(83, "RGMII1_TXCTL", 49, UNIPHIER_PIN_DRV_2BIT),
+};
+
static const unsigned emmc_pins[] = {31, 32, 33, 34, 35, 36, 37, 38};
static const int emmc_muxvals[] = {0, 0, 0, 0, 0, 0, 0, 0};
static const unsigned emmc_dat8_pins[] = {39, 40, 41, 42};
@@ -121,6 +137,8 @@ static const char * const uniphier_pxs3_functions[] = {
};
static struct uniphier_pinctrl_socdata uniphier_pxs3_pinctrl_socdata = {
+ .pins = uniphier_pxs3_pins,
+ .pins_count = ARRAY_SIZE(uniphier_pxs3_pins),
.groups = uniphier_pxs3_groups,
.groups_count = ARRAY_SIZE(uniphier_pxs3_groups),
.functions = uniphier_pxs3_functions,
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index b6987225698..35b6ed4d7c7 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -260,4 +260,13 @@ config RTC_ZYNQMP
Say "yes" here to support the on chip real time clock
present on Xilinx ZynqMP SoC.
+config RTC_HT1380
+ bool "Enable Holtek HT1380/HT1381 RTC driver"
+ depends on DM_RTC && DM_GPIO
+ help
+ Say "yes" here to get support for Holtek HT1380/HT1381
+ Serial Timekeeper IC which provides seconds, minutes, hours,
+ day of the week, date, month and year information. It is to be
+ connected via 3 GPIO pins which work as reset, clock, and data.
+
endmenu
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 2089086551d..acfd130bbc9 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_RTC_DS3231) += ds3231.o
obj-$(CONFIG_RTC_DS3232) += ds3232.o
obj-$(CONFIG_RTC_EMULATION) += emul_rtc.o
obj-$(CONFIG_RTC_FTRTC010) += ftrtc010.o
+obj-$(CONFIG_RTC_HT1380) += ht1380.o
obj-$(CONFIG_SANDBOX) += i2c_rtc_emul.o
obj-$(CONFIG_RTC_IMXDI) += imxdi.o
obj-$(CONFIG_RTC_ISL1208) += isl1208.o
diff --git a/drivers/rtc/ht1380.c b/drivers/rtc/ht1380.c
new file mode 100644
index 00000000000..85fcee3e71e
--- /dev/null
+++ b/drivers/rtc/ht1380.c
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Holtek HT1380/HT1381 Serial Timekeeper Chip
+ *
+ * Communication with the chip is vendor-specific.
+ * It is done via 3 GPIO pins: reset, clock, and data.
+ * Describe in .dts this way:
+ *
+ * rtc {
+ * compatible = "holtek,ht1380";
+ * rst-gpios = <&gpio 19 GPIO_ACTIVE_LOW>;
+ * clk-gpios = <&gpio 20 GPIO_ACTIVE_HIGH>;
+ * dat-gpios = <&gpio 21 GPIO_ACTIVE_HIGH>;
+ * };
+ *
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <rtc.h>
+#include <bcd.h>
+#include <asm/gpio.h>
+#include <linux/delay.h>
+
+struct ht1380_priv {
+ struct gpio_desc rst_desc;
+ struct gpio_desc clk_desc;
+ struct gpio_desc dat_desc;
+};
+
+enum registers {
+ SEC,
+ MIN,
+ HOUR,
+ MDAY,
+ MONTH,
+ WDAY,
+ YEAR,
+ WP,
+ N_REGS
+};
+
+enum hour_mode {
+ AMPM_MODE = 0x80, /* RTC is in AM/PM mode */
+ PM_NOW = 0x20, /* set if PM, clear if AM */
+};
+
+static const int BURST = 0xbe;
+static const int READ = 1;
+
+static void ht1380_half_period_delay(void)
+{
+ /*
+ * Delay for half a period. 1 us complies with the 500 KHz maximum
+ * input serial clock limit given by the datasheet.
+ */
+ udelay(1);
+}
+
+static int ht1380_send_byte(struct ht1380_priv *priv, int byte)
+{
+ int ret;
+
+ for (int bit = 0; bit < 8; bit++) {
+ ret = dm_gpio_set_value(&priv->dat_desc, byte >> bit & 1);
+ if (ret)
+ break;
+ ht1380_half_period_delay();
+
+ ret = dm_gpio_set_value(&priv->clk_desc, 1);
+ if (ret)
+ break;
+ ht1380_half_period_delay();
+
+ ret = dm_gpio_set_value(&priv->clk_desc, 0);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Leave reset state. The transfer operation can then be started.
+ */
+static int ht1380_reset_off(struct ht1380_priv *priv)
+{
+ const unsigned int T_CC = 4; /* us, Reset to Clock Setup */
+ int ret;
+
+ /*
+ * Leave RESET state.
+ * Make sure we make the minimal delay required by the datasheet.
+ */
+ ret = dm_gpio_set_value(&priv->rst_desc, 0);
+ udelay(T_CC);
+
+ return ret;
+}
+
+/*
+ * Enter reset state. Completes the transfer operation.
+ */
+static int ht1380_reset_on(struct ht1380_priv *priv)
+{
+ const unsigned int T_CWH = 4; /* us, Reset Inactive Time */
+ int ret;
+
+ /*
+ * Enter RESET state.
+ * Make sure we make the minimal delay required by the datasheet.
+ */
+ ret = dm_gpio_set_value(&priv->rst_desc, 1);
+ udelay(T_CWH);
+
+ return ret;
+}
+
+static int ht1380_rtc_get(struct udevice *dev, struct rtc_time *tm)
+{
+ struct ht1380_priv *priv = dev_get_priv(dev);
+ int ret, i, bit, reg[N_REGS];
+
+ ret = dm_gpio_set_value(&priv->clk_desc, 0);
+ if (ret)
+ return ret;
+
+ ret = dm_gpio_set_dir_flags(&priv->dat_desc, GPIOD_IS_OUT);
+ if (ret)
+ return ret;
+
+ ret = ht1380_reset_off(priv);
+ if (ret)
+ goto exit;
+
+ ret = ht1380_send_byte(priv, BURST + READ);
+ if (ret)
+ goto exit;
+
+ ret = dm_gpio_set_dir_flags(&priv->dat_desc, GPIOD_IS_IN);
+ if (ret)
+ goto exit;
+
+ for (i = 0; i < N_REGS; i++) {
+ reg[i] = 0;
+
+ for (bit = 0; bit < 8; bit++) {
+ ht1380_half_period_delay();
+
+ ret = dm_gpio_set_value(&priv->clk_desc, 1);
+ if (ret)
+ goto exit;
+ ht1380_half_period_delay();
+
+ reg[i] |= dm_gpio_get_value(&priv->dat_desc) << bit;
+ ret = dm_gpio_set_value(&priv->clk_desc, 0);
+ if (ret)
+ goto exit;
+ }
+ }
+
+ ret = -EINVAL;
+
+ /* Correctness check: some bits are always zero */
+ if (reg[MIN] & 0x80 || reg[HOUR] & 0x40 || reg[MDAY] & 0xc0 ||
+ reg[MONTH] & 0xe0 || reg[WDAY] & 0xf8 || reg[WP] & 0x7f)
+ goto exit;
+
+ /* Correctness check: some registers are always non-zero */
+ if (!reg[MDAY] || !reg[MONTH] || !reg[WDAY])
+ goto exit;
+
+ tm->tm_sec = bcd2bin(reg[SEC]);
+ tm->tm_min = bcd2bin(reg[MIN]);
+ if (reg[HOUR] & AMPM_MODE) {
+ /* AM-PM Mode, range is 01-12 */
+ tm->tm_hour = bcd2bin(reg[HOUR] & 0x1f) % 12;
+ if (reg[HOUR] & PM_NOW) {
+ /* it is PM (otherwise AM) */
+ tm->tm_hour += 12;
+ }
+ } else {
+ /* 24-hour Mode, range is 0-23 */
+ tm->tm_hour = bcd2bin(reg[HOUR]);
+ }
+ tm->tm_mday = bcd2bin(reg[MDAY]);
+ tm->tm_mon = bcd2bin(reg[MONTH]);
+ tm->tm_year = 2000 + bcd2bin(reg[YEAR]);
+ tm->tm_wday = bcd2bin(reg[WDAY]) - 1;
+ tm->tm_yday = 0;
+ tm->tm_isdst = 0;
+
+ ret = 0;
+
+exit:
+ ht1380_reset_on(priv);
+
+ return ret;
+}
+
+static int ht1380_write_protection_off(struct ht1380_priv *priv)
+{
+ int ret;
+ const int PROTECT = 0x8e;
+
+ ret = ht1380_reset_off(priv);
+ if (ret)
+ return ret;
+
+ ret = ht1380_send_byte(priv, PROTECT);
+ if (ret)
+ return ret;
+ ret = ht1380_send_byte(priv, 0); /* WP bit is 0 */
+ if (ret)
+ return ret;
+
+ return ht1380_reset_on(priv);
+}
+
+static int ht1380_rtc_set(struct udevice *dev, const struct rtc_time *tm)
+{
+ struct ht1380_priv *priv = dev_get_priv(dev);
+ int ret, i, reg[N_REGS];
+
+ ret = dm_gpio_set_value(&priv->clk_desc, 0);
+ if (ret)
+ return ret;
+
+ ret = dm_gpio_set_dir_flags(&priv->dat_desc, GPIOD_IS_OUT);
+ if (ret)
+ goto exit;
+
+ ret = ht1380_write_protection_off(priv);
+ if (ret)
+ goto exit;
+
+ reg[SEC] = bin2bcd(tm->tm_sec);
+ reg[MIN] = bin2bcd(tm->tm_min);
+ reg[HOUR] = bin2bcd(tm->tm_hour);
+ reg[MDAY] = bin2bcd(tm->tm_mday);
+ reg[MONTH] = bin2bcd(tm->tm_mon);
+ reg[WDAY] = bin2bcd(tm->tm_wday) + 1;
+ reg[YEAR] = bin2bcd(tm->tm_year - 2000);
+ reg[WP] = 0x80; /* WP bit is 1 */
+
+ ret = ht1380_reset_off(priv);
+ if (ret)
+ goto exit;
+
+ ret = ht1380_send_byte(priv, BURST);
+ for (i = 0; i < N_REGS && ret; i++)
+ ret = ht1380_send_byte(priv, reg[i]);
+
+exit:
+ ht1380_reset_on(priv);
+
+ return ret;
+}
+
+static int ht1380_probe(struct udevice *dev)
+{
+ int ret;
+ struct ht1380_priv *priv;
+
+ priv = dev_get_priv(dev);
+ if (!priv)
+ return -EINVAL;
+
+ ret = gpio_request_by_name(dev, "rst-gpios", 0,
+ &priv->rst_desc, GPIOD_IS_OUT);
+ if (ret)
+ goto fail_rst;
+
+ ret = gpio_request_by_name(dev, "clk-gpios", 0,
+ &priv->clk_desc, GPIOD_IS_OUT);
+ if (ret)
+ goto fail_clk;
+
+ ret = gpio_request_by_name(dev, "dat-gpios", 0,
+ &priv->dat_desc, 0);
+ if (ret)
+ goto fail_dat;
+
+ ret = ht1380_reset_on(priv);
+ if (ret)
+ goto fail;
+
+ return 0;
+
+fail:
+ dm_gpio_free(dev, &priv->dat_desc);
+fail_dat:
+ dm_gpio_free(dev, &priv->clk_desc);
+fail_clk:
+ dm_gpio_free(dev, &priv->rst_desc);
+fail_rst:
+ return ret;
+}
+
+static int ht1380_remove(struct udevice *dev)
+{
+ struct ht1380_priv *priv = dev_get_priv(dev);
+
+ dm_gpio_free(dev, &priv->rst_desc);
+ dm_gpio_free(dev, &priv->clk_desc);
+ dm_gpio_free(dev, &priv->dat_desc);
+
+ return 0;
+}
+
+static const struct rtc_ops ht1380_rtc_ops = {
+ .get = ht1380_rtc_get,
+ .set = ht1380_rtc_set,
+};
+
+static const struct udevice_id ht1380_rtc_ids[] = {
+ { .compatible = "holtek,ht1380" },
+ { }
+};
+
+U_BOOT_DRIVER(rtc_ht1380) = {
+ .name = "rtc-ht1380",
+ .id = UCLASS_RTC,
+ .probe = ht1380_probe,
+ .remove = ht1380_remove,
+ .of_match = ht1380_rtc_ids,
+ .ops = &ht1380_rtc_ops,
+ .priv_auto = sizeof(struct ht1380_priv),
+};
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 14b0febd1a5..bb5083201b3 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -827,6 +827,15 @@ config S5P_SERIAL
help
Select this to enable Samsung S5P UART support.
+config S5P4418_PL011_SERIAL
+ bool "Extended PL011 driver for S5P4418"
+ depends on DM_SERIAL && PL01X_SERIAL && ARCH_NEXELL
+ default y
+ help
+ Select this to enable support of the PL011 UARTs in the S5P4418 SOC.
+ With this driver the UART-clocks are set to the appropriate rate
+ (if not 'skip-init').
+
config SANDBOX_SERIAL
bool "Sandbox UART support"
depends on SANDBOX
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 37d3f82dbd8..01fef3f323b 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -59,6 +59,7 @@ obj-$(CONFIG_MT7620_SERIAL) += serial_mt7620.o
obj-$(CONFIG_HTIF_CONSOLE) += serial_htif.o
obj-$(CONFIG_SIFIVE_SERIAL) += serial_sifive.o
obj-$(CONFIG_XEN_SERIAL) += serial_xen.o
+obj-$(CONFIG_S5P4418_PL011_SERIAL) += serial_s5p4418_pl011.o
ifndef CONFIG_SPL_BUILD
obj-$(CONFIG_USB_TTY) += usbtty.o
diff --git a/drivers/serial/serial_arc.c b/drivers/serial/serial_arc.c
index b2d95bdbe18..c2fc8a901e2 100644
--- a/drivers/serial/serial_arc.c
+++ b/drivers/serial/serial_arc.c
@@ -53,8 +53,8 @@ static int arc_serial_putc(struct udevice *dev, const char c)
struct arc_serial_plat *plat = dev_get_plat(dev);
struct arc_serial_regs *const regs = plat->reg;
- while (!(readb(&regs->status) & UART_TXEMPTY))
- ;
+ if (!(readb(&regs->status) & UART_TXEMPTY))
+ return -EAGAIN;
writeb(c, &regs->data);
@@ -83,8 +83,8 @@ static int arc_serial_getc(struct udevice *dev)
struct arc_serial_plat *plat = dev_get_plat(dev);
struct arc_serial_regs *const regs = plat->reg;
- while (!arc_serial_tstc(regs))
- ;
+ if (!arc_serial_tstc(regs))
+ return -EAGAIN;
/* Check for overflow errors */
if (readb(&regs->status) & UART_OVERFLOW_ERR)
diff --git a/drivers/serial/serial_lpuart.c b/drivers/serial/serial_lpuart.c
index ff576da516d..51e66abdbc1 100644
--- a/drivers/serial/serial_lpuart.c
+++ b/drivers/serial/serial_lpuart.c
@@ -168,23 +168,24 @@ static void _lpuart_serial_setbrg(struct udevice *dev,
static int _lpuart_serial_getc(struct lpuart_serial_plat *plat)
{
struct lpuart_fsl *base = plat->reg;
- while (!(__raw_readb(&base->us1) & (US1_RDRF | US1_OR)))
- schedule();
+ if (!(__raw_readb(&base->us1) & (US1_RDRF | US1_OR)))
+ return -EAGAIN;
barrier();
return __raw_readb(&base->ud);
}
-static void _lpuart_serial_putc(struct lpuart_serial_plat *plat,
+static int _lpuart_serial_putc(struct lpuart_serial_plat *plat,
const char c)
{
struct lpuart_fsl *base = plat->reg;
- while (!(__raw_readb(&base->us1) & US1_TDRE))
- schedule();
+ if (!(__raw_readb(&base->us1) & US1_TDRE))
+ return -EAGAIN;
__raw_writeb(c, &base->ud);
+ return 0;
}
/* Test whether a character is in the RX buffer */
@@ -328,10 +329,9 @@ static int _lpuart32_serial_getc(struct lpuart_serial_plat *plat)
u32 stat, val;
lpuart_read32(plat->flags, &base->stat, &stat);
- while ((stat & STAT_RDRF) == 0) {
+ if ((stat & STAT_RDRF) == 0) {
lpuart_write32(plat->flags, &base->stat, STAT_FLAGS);
- schedule();
- lpuart_read32(plat->flags, &base->stat, &stat);
+ return -EAGAIN;
}
lpuart_read32(plat->flags, &base->data, &val);
@@ -343,25 +343,18 @@ static int _lpuart32_serial_getc(struct lpuart_serial_plat *plat)
return val & 0x3ff;
}
-static void _lpuart32_serial_putc(struct lpuart_serial_plat *plat,
+static int _lpuart32_serial_putc(struct lpuart_serial_plat *plat,
const char c)
{
struct lpuart_fsl_reg32 *base = plat->reg;
u32 stat;
- if (c == '\n')
- serial_putc('\r');
-
- while (true) {
- lpuart_read32(plat->flags, &base->stat, &stat);
-
- if ((stat & STAT_TDRE))
- break;
-
- schedule();
- }
+ lpuart_read32(plat->flags, &base->stat, &stat);
+ if (!(stat & STAT_TDRE))
+ return -EAGAIN;
lpuart_write32(plat->flags, &base->data, c);
+ return 0;
}
/* Test whether a character is in the RX buffer */
@@ -456,11 +449,9 @@ static int lpuart_serial_putc(struct udevice *dev, const char c)
struct lpuart_serial_plat *plat = dev_get_plat(dev);
if (is_lpuart32(dev))
- _lpuart32_serial_putc(plat, c);
- else
- _lpuart_serial_putc(plat, c);
+ return _lpuart32_serial_putc(plat, c);
- return 0;
+ return _lpuart_serial_putc(plat, c);
}
static int lpuart_serial_pending(struct udevice *dev, bool input)
diff --git a/drivers/serial/serial_mpc8xx.c b/drivers/serial/serial_mpc8xx.c
index aeae6ae6cd2..b8d6a81b650 100644
--- a/drivers/serial/serial_mpc8xx.c
+++ b/drivers/serial/serial_mpc8xx.c
@@ -176,19 +176,15 @@ static int serial_mpc8xx_putc(struct udevice *dev, const char c)
cpm8xx_t __iomem *cpmp = &(im->im_cpm);
struct serialbuffer __iomem *rtx;
- if (c == '\n')
- serial_mpc8xx_putc(dev, '\r');
-
rtx = (struct serialbuffer __iomem *)&cpmp->cp_dpmem[CPM_SERIAL_BASE];
- /* Wait for last character to go. */
+ if (in_be16(&rtx->txbd.cbd_sc) & BD_SC_READY)
+ return -EAGAIN;
+
out_8(&rtx->txbuf, c);
out_be16(&rtx->txbd.cbd_datlen, 1);
setbits_be16(&rtx->txbd.cbd_sc, BD_SC_READY);
- while (in_be16(&rtx->txbd.cbd_sc) & BD_SC_READY)
- schedule();
-
return 0;
}
@@ -202,9 +198,8 @@ static int serial_mpc8xx_getc(struct udevice *dev)
rtx = (struct serialbuffer __iomem *)&cpmp->cp_dpmem[CPM_SERIAL_BASE];
- /* Wait for character to show up. */
- while (in_be16(&rtx->rxbd.cbd_sc) & BD_SC_EMPTY)
- schedule();
+ if (in_be16(&rtx->rxbd.cbd_sc) & BD_SC_EMPTY)
+ return -EAGAIN;
/* the characters are read one by one,
* use the rxindex to know the next char to deliver
diff --git a/drivers/serial/serial_mvebu_a3700.c b/drivers/serial/serial_mvebu_a3700.c
index 0fcd7e88ace..b2017c64556 100644
--- a/drivers/serial/serial_mvebu_a3700.c
+++ b/drivers/serial/serial_mvebu_a3700.c
@@ -40,8 +40,8 @@ static int mvebu_serial_putc(struct udevice *dev, const char ch)
struct mvebu_plat *plat = dev_get_plat(dev);
void __iomem *base = plat->base;
- while (readl(base + UART_STATUS_REG) & UART_STATUS_TXFIFO_FULL)
- ;
+ if (readl(base + UART_STATUS_REG) & UART_STATUS_TXFIFO_FULL)
+ return -EAGAIN;
writel(ch, base + UART_TX_REG);
@@ -53,8 +53,8 @@ static int mvebu_serial_getc(struct udevice *dev)
struct mvebu_plat *plat = dev_get_plat(dev);
void __iomem *base = plat->base;
- while (!(readl(base + UART_STATUS_REG) & UART_STATUS_RX_RDY))
- ;
+ if (!(readl(base + UART_STATUS_REG) & UART_STATUS_RX_RDY))
+ return -EAGAIN;
return readl(base + UART_RX_REG) & 0xff;
}
diff --git a/drivers/serial/serial_s5p4418_pl011.c b/drivers/serial/serial_s5p4418_pl011.c
new file mode 100644
index 00000000000..e4492e662e9
--- /dev/null
+++ b/drivers/serial/serial_s5p4418_pl011.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2022 Stefan Bosch <stefan_b@posteo.net>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/reset.h>
+#include <linux/delay.h>
+
+#include <dm/platform_data/serial_pl01x.h>
+#include <serial.h>
+#include "serial_pl01x_internal.h"
+
+int s5p4418_pl011_serial_probe(struct udevice *dev)
+{
+ struct pl01x_serial_plat *plat = dev_get_plat(dev);
+ struct clk *nx_clk;
+ ulong rate_act;
+ char uart_clk_name[10];
+ int uart_num = -1;
+ int rst_id, ret;
+
+ if (!plat->skip_init) {
+ uart_num = dev->seq_;
+ rst_id = RESET_ID_UART0 + uart_num;
+
+ if (uart_num < 0 || rst_id > RESET_ID_UART5) {
+ /* invalid UART-number */
+ debug("%s: sequence/uart number %d is invalid!\n", __func__, uart_num);
+ return -ENODEV;
+ }
+
+ sprintf(uart_clk_name, "nx-uart.%d", uart_num);
+ nx_clk = clk_get(uart_clk_name);
+ if (!nx_clk) {
+ debug("%s: clk_get('%s') failed!\n", __func__, uart_clk_name);
+ return -ENODEV;
+ }
+
+ /* wait to make sure all pending characters have been sent */
+ mdelay(100);
+ }
+
+ /*
+ * Note: Unless !plat->skip_init, the UART is disabled here, so printf()
+ * or debug() must not be used until pl01x_serial_setbrg() has been called
+ * (enables the UART). Otherwise u-boot is hanging!
+ */
+ ret = pl01x_serial_probe(dev);
+ if (ret)
+ return ret;
+
+ if (!plat->skip_init) {
+ /* do reset UART */
+ nx_rstcon_setrst(rst_id, RSTCON_ASSERT);
+ udelay(10);
+ nx_rstcon_setrst(rst_id, RSTCON_NEGATE);
+ udelay(10);
+ clk_disable(nx_clk);
+
+ rate_act = clk_set_rate(nx_clk, plat->clock);
+ clk_enable(nx_clk);
+
+ plat->clock = rate_act;
+ }
+
+ return 0;
+}
+
+static const struct dm_serial_ops s5p4418_pl011_serial_ops = {
+ .putc = pl01x_serial_putc,
+ .pending = pl01x_serial_pending,
+ .getc = pl01x_serial_getc,
+ .setbrg = pl01x_serial_setbrg,
+};
+
+static const struct udevice_id s5p4418_pl011_serial_id[] = {
+ {.compatible = "nexell,s5p4418-pl011", .data = TYPE_PL011},
+ {}
+};
+
+U_BOOT_DRIVER(s5p4418_pl011_uart) = {
+ .name = "s5p4418_pl011",
+ .id = UCLASS_SERIAL,
+ .of_match = of_match_ptr(s5p4418_pl011_serial_id),
+ .of_to_plat = of_match_ptr(pl01x_serial_of_to_plat),
+ .plat_auto = sizeof(struct pl01x_serial_plat),
+ .probe = s5p4418_pl011_serial_probe,
+ .ops = &s5p4418_pl011_serial_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+ .priv_auto = sizeof(struct pl01x_priv),
+};