13917c269SStephen Warren /*
23917c269SStephen Warren * (C) Copyright 2016 Stephen Warren <swarren@wwwdotorg.org>
33917c269SStephen Warren *
43917c269SStephen Warren * Derived from pl01x code:
53917c269SStephen Warren *
63917c269SStephen Warren * (C) Copyright 2000
73917c269SStephen Warren * Rob Taylor, Flying Pig Systems. robt@flyingpig.com.
83917c269SStephen Warren *
93917c269SStephen Warren * (C) Copyright 2004
103917c269SStephen Warren * ARM Ltd.
113917c269SStephen Warren * Philippe Robin, <philippe.robin@arm.com>
123917c269SStephen Warren *
133917c269SStephen Warren * SPDX-License-Identifier: GPL-2.0+
143917c269SStephen Warren */
153917c269SStephen Warren
163917c269SStephen Warren /* Simple U-Boot driver for the BCM283x mini UART */
173917c269SStephen Warren
183917c269SStephen Warren #include <common.h>
193917c269SStephen Warren #include <dm.h>
203917c269SStephen Warren #include <errno.h>
213917c269SStephen Warren #include <watchdog.h>
223917c269SStephen Warren #include <asm/io.h>
233917c269SStephen Warren #include <serial.h>
243917c269SStephen Warren #include <dm/platform_data/serial_bcm283x_mu.h>
253917c269SStephen Warren #include <linux/compiler.h>
263917c269SStephen Warren #include <fdtdec.h>
273917c269SStephen Warren
289f755f5dSFabian Vogt DECLARE_GLOBAL_DATA_PTR;
299f755f5dSFabian Vogt
303917c269SStephen Warren struct bcm283x_mu_regs {
313917c269SStephen Warren u32 io;
323917c269SStephen Warren u32 iir;
333917c269SStephen Warren u32 ier;
343917c269SStephen Warren u32 lcr;
353917c269SStephen Warren u32 mcr;
363917c269SStephen Warren u32 lsr;
373917c269SStephen Warren u32 msr;
383917c269SStephen Warren u32 scratch;
393917c269SStephen Warren u32 cntl;
403917c269SStephen Warren u32 stat;
413917c269SStephen Warren u32 baud;
423917c269SStephen Warren };
433917c269SStephen Warren
443917c269SStephen Warren #define BCM283X_MU_LCR_DATA_SIZE_8 3
453917c269SStephen Warren
463917c269SStephen Warren #define BCM283X_MU_LSR_TX_IDLE BIT(6)
473917c269SStephen Warren /* This actually means not full, but is named not empty in the docs */
483917c269SStephen Warren #define BCM283X_MU_LSR_TX_EMPTY BIT(5)
493917c269SStephen Warren #define BCM283X_MU_LSR_RX_READY BIT(0)
503917c269SStephen Warren
513917c269SStephen Warren struct bcm283x_mu_priv {
523917c269SStephen Warren struct bcm283x_mu_regs *regs;
533917c269SStephen Warren };
543917c269SStephen Warren
bcm283x_mu_serial_setbrg(struct udevice * dev,int baudrate)553917c269SStephen Warren static int bcm283x_mu_serial_setbrg(struct udevice *dev, int baudrate)
563917c269SStephen Warren {
573917c269SStephen Warren struct bcm283x_mu_serial_platdata *plat = dev_get_platdata(dev);
583917c269SStephen Warren struct bcm283x_mu_priv *priv = dev_get_priv(dev);
593917c269SStephen Warren struct bcm283x_mu_regs *regs = priv->regs;
603917c269SStephen Warren u32 divider;
613917c269SStephen Warren
62cb97ad47SFabian Vogt if (plat->disabled || plat->skip_init)
633917c269SStephen Warren return 0;
643917c269SStephen Warren
653917c269SStephen Warren divider = plat->clock / (baudrate * 8);
663917c269SStephen Warren
673917c269SStephen Warren writel(BCM283X_MU_LCR_DATA_SIZE_8, ®s->lcr);
683917c269SStephen Warren writel(divider - 1, ®s->baud);
693917c269SStephen Warren
703917c269SStephen Warren return 0;
713917c269SStephen Warren }
723917c269SStephen Warren
bcm283x_mu_serial_probe(struct udevice * dev)733917c269SStephen Warren static int bcm283x_mu_serial_probe(struct udevice *dev)
743917c269SStephen Warren {
753917c269SStephen Warren struct bcm283x_mu_serial_platdata *plat = dev_get_platdata(dev);
763917c269SStephen Warren struct bcm283x_mu_priv *priv = dev_get_priv(dev);
773917c269SStephen Warren
78601147b0SAlexander Graf if (plat->disabled)
79601147b0SAlexander Graf return -ENODEV;
80601147b0SAlexander Graf
813917c269SStephen Warren priv->regs = (struct bcm283x_mu_regs *)plat->base;
823917c269SStephen Warren
833917c269SStephen Warren return 0;
843917c269SStephen Warren }
853917c269SStephen Warren
bcm283x_mu_serial_getc(struct udevice * dev)863917c269SStephen Warren static int bcm283x_mu_serial_getc(struct udevice *dev)
873917c269SStephen Warren {
88cb97ad47SFabian Vogt struct bcm283x_mu_serial_platdata *plat = dev_get_platdata(dev);
893917c269SStephen Warren struct bcm283x_mu_priv *priv = dev_get_priv(dev);
903917c269SStephen Warren struct bcm283x_mu_regs *regs = priv->regs;
913917c269SStephen Warren u32 data;
923917c269SStephen Warren
93cb97ad47SFabian Vogt if (plat->disabled)
94cb97ad47SFabian Vogt return -EAGAIN;
95cb97ad47SFabian Vogt
963917c269SStephen Warren /* Wait until there is data in the FIFO */
973917c269SStephen Warren if (!(readl(®s->lsr) & BCM283X_MU_LSR_RX_READY))
983917c269SStephen Warren return -EAGAIN;
993917c269SStephen Warren
1003917c269SStephen Warren data = readl(®s->io);
1013917c269SStephen Warren
1023917c269SStephen Warren return (int)data;
1033917c269SStephen Warren }
1043917c269SStephen Warren
bcm283x_mu_serial_putc(struct udevice * dev,const char data)1053917c269SStephen Warren static int bcm283x_mu_serial_putc(struct udevice *dev, const char data)
1063917c269SStephen Warren {
107cb97ad47SFabian Vogt struct bcm283x_mu_serial_platdata *plat = dev_get_platdata(dev);
1083917c269SStephen Warren struct bcm283x_mu_priv *priv = dev_get_priv(dev);
1093917c269SStephen Warren struct bcm283x_mu_regs *regs = priv->regs;
1103917c269SStephen Warren
111cb97ad47SFabian Vogt if (plat->disabled)
112cb97ad47SFabian Vogt return 0;
113cb97ad47SFabian Vogt
1143917c269SStephen Warren /* Wait until there is space in the FIFO */
1153917c269SStephen Warren if (!(readl(®s->lsr) & BCM283X_MU_LSR_TX_EMPTY))
1163917c269SStephen Warren return -EAGAIN;
1173917c269SStephen Warren
1183917c269SStephen Warren /* Send the character */
1193917c269SStephen Warren writel(data, ®s->io);
1203917c269SStephen Warren
1213917c269SStephen Warren return 0;
1223917c269SStephen Warren }
1233917c269SStephen Warren
bcm283x_mu_serial_pending(struct udevice * dev,bool input)1243917c269SStephen Warren static int bcm283x_mu_serial_pending(struct udevice *dev, bool input)
1253917c269SStephen Warren {
126cb97ad47SFabian Vogt struct bcm283x_mu_serial_platdata *plat = dev_get_platdata(dev);
1273917c269SStephen Warren struct bcm283x_mu_priv *priv = dev_get_priv(dev);
1283917c269SStephen Warren struct bcm283x_mu_regs *regs = priv->regs;
129cb97ad47SFabian Vogt unsigned int lsr;
130cb97ad47SFabian Vogt
131cb97ad47SFabian Vogt if (plat->disabled)
132cb97ad47SFabian Vogt return 0;
133cb97ad47SFabian Vogt
134cb97ad47SFabian Vogt lsr = readl(®s->lsr);
1353917c269SStephen Warren
1363917c269SStephen Warren if (input) {
1373917c269SStephen Warren WATCHDOG_RESET();
138e3a46e3eSStephen Warren return (lsr & BCM283X_MU_LSR_RX_READY) ? 1 : 0;
1393917c269SStephen Warren } else {
140e3a46e3eSStephen Warren return (lsr & BCM283X_MU_LSR_TX_IDLE) ? 0 : 1;
1413917c269SStephen Warren }
1423917c269SStephen Warren }
1433917c269SStephen Warren
1443917c269SStephen Warren static const struct dm_serial_ops bcm283x_mu_serial_ops = {
1453917c269SStephen Warren .putc = bcm283x_mu_serial_putc,
1463917c269SStephen Warren .pending = bcm283x_mu_serial_pending,
1473917c269SStephen Warren .getc = bcm283x_mu_serial_getc,
1483917c269SStephen Warren .setbrg = bcm283x_mu_serial_setbrg,
1493917c269SStephen Warren };
1503917c269SStephen Warren
1519f755f5dSFabian Vogt #if CONFIG_IS_ENABLED(OF_CONTROL)
1529f755f5dSFabian Vogt static const struct udevice_id bcm283x_mu_serial_id[] = {
1539f755f5dSFabian Vogt {.compatible = "brcm,bcm2835-aux-uart"},
1549f755f5dSFabian Vogt {}
1559f755f5dSFabian Vogt };
1569f755f5dSFabian Vogt
bcm283x_mu_serial_ofdata_to_platdata(struct udevice * dev)1579f755f5dSFabian Vogt static int bcm283x_mu_serial_ofdata_to_platdata(struct udevice *dev)
1589f755f5dSFabian Vogt {
1599f755f5dSFabian Vogt struct bcm283x_mu_serial_platdata *plat = dev_get_platdata(dev);
1609f755f5dSFabian Vogt fdt_addr_t addr;
1619f755f5dSFabian Vogt
162*a821c4afSSimon Glass addr = devfdt_get_addr(dev);
1639f755f5dSFabian Vogt if (addr == FDT_ADDR_T_NONE)
1649f755f5dSFabian Vogt return -EINVAL;
1659f755f5dSFabian Vogt
1669f755f5dSFabian Vogt plat->base = addr;
167e160f7d4SSimon Glass plat->clock = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "clock",
168e160f7d4SSimon Glass 1);
169e160f7d4SSimon Glass plat->skip_init = fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
1709f755f5dSFabian Vogt "skip-init");
1719f755f5dSFabian Vogt plat->disabled = false;
1729f755f5dSFabian Vogt return 0;
1739f755f5dSFabian Vogt }
1749f755f5dSFabian Vogt #endif
1759f755f5dSFabian Vogt
1763917c269SStephen Warren U_BOOT_DRIVER(serial_bcm283x_mu) = {
1773917c269SStephen Warren .name = "serial_bcm283x_mu",
1783917c269SStephen Warren .id = UCLASS_SERIAL,
1799f755f5dSFabian Vogt .of_match = of_match_ptr(bcm283x_mu_serial_id),
1809f755f5dSFabian Vogt .ofdata_to_platdata = of_match_ptr(bcm283x_mu_serial_ofdata_to_platdata),
1813917c269SStephen Warren .platdata_auto_alloc_size = sizeof(struct bcm283x_mu_serial_platdata),
1823917c269SStephen Warren .probe = bcm283x_mu_serial_probe,
1833917c269SStephen Warren .ops = &bcm283x_mu_serial_ops,
1843917c269SStephen Warren .flags = DM_FLAG_PRE_RELOC,
1853917c269SStephen Warren .priv_auto_alloc_size = sizeof(struct bcm283x_mu_priv),
1863917c269SStephen Warren };
187