xref: /rk3399_rockchip-uboot/drivers/serial/serial_bcm283x_mu.c (revision a821c4af79e4f5ce9b629b20473863397bbe9b10)
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, &regs->lcr);
683917c269SStephen Warren 	writel(divider - 1, &regs->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(&regs->lsr) & BCM283X_MU_LSR_RX_READY))
983917c269SStephen Warren 		return -EAGAIN;
993917c269SStephen Warren 
1003917c269SStephen Warren 	data = readl(&regs->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(&regs->lsr) & BCM283X_MU_LSR_TX_EMPTY))
1163917c269SStephen Warren 		return -EAGAIN;
1173917c269SStephen Warren 
1183917c269SStephen Warren 	/* Send the character */
1193917c269SStephen Warren 	writel(data, &regs->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(&regs->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