The RPi3 typically uses the regular UART for high-speed communication with the Bluetooth device, leaving us the mini UART to use for the serial console. Add support for this UART so we can use it. Signed-off-by: Stephen Warren <swarren@wwwdotorg.org>master
parent
92667e89fc
commit
3917c26909
@ -0,0 +1,140 @@ |
||||
/*
|
||||
* (C) Copyright 2016 Stephen Warren <swarren@wwwdotorg.org> |
||||
* |
||||
* Derived from pl01x code: |
||||
* |
||||
* (C) Copyright 2000 |
||||
* Rob Taylor, Flying Pig Systems. robt@flyingpig.com. |
||||
* |
||||
* (C) Copyright 2004 |
||||
* ARM Ltd. |
||||
* Philippe Robin, <philippe.robin@arm.com> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
/* Simple U-Boot driver for the BCM283x mini UART */ |
||||
|
||||
#include <common.h> |
||||
#include <dm.h> |
||||
#include <errno.h> |
||||
#include <watchdog.h> |
||||
#include <asm/io.h> |
||||
#include <serial.h> |
||||
#include <dm/platform_data/serial_bcm283x_mu.h> |
||||
#include <linux/compiler.h> |
||||
#include <fdtdec.h> |
||||
|
||||
struct bcm283x_mu_regs { |
||||
u32 io; |
||||
u32 iir; |
||||
u32 ier; |
||||
u32 lcr; |
||||
u32 mcr; |
||||
u32 lsr; |
||||
u32 msr; |
||||
u32 scratch; |
||||
u32 cntl; |
||||
u32 stat; |
||||
u32 baud; |
||||
}; |
||||
|
||||
#define BCM283X_MU_LCR_DATA_SIZE_8 3 |
||||
|
||||
#define BCM283X_MU_LSR_TX_IDLE BIT(6) |
||||
/* This actually means not full, but is named not empty in the docs */ |
||||
#define BCM283X_MU_LSR_TX_EMPTY BIT(5) |
||||
#define BCM283X_MU_LSR_RX_READY BIT(0) |
||||
|
||||
struct bcm283x_mu_priv { |
||||
struct bcm283x_mu_regs *regs; |
||||
}; |
||||
|
||||
static int bcm283x_mu_serial_setbrg(struct udevice *dev, int baudrate) |
||||
{ |
||||
struct bcm283x_mu_serial_platdata *plat = dev_get_platdata(dev); |
||||
struct bcm283x_mu_priv *priv = dev_get_priv(dev); |
||||
struct bcm283x_mu_regs *regs = priv->regs; |
||||
u32 divider; |
||||
|
||||
if (plat->skip_init) |
||||
return 0; |
||||
|
||||
divider = plat->clock / (baudrate * 8); |
||||
|
||||
writel(BCM283X_MU_LCR_DATA_SIZE_8, ®s->lcr); |
||||
writel(divider - 1, ®s->baud); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int bcm283x_mu_serial_probe(struct udevice *dev) |
||||
{ |
||||
struct bcm283x_mu_serial_platdata *plat = dev_get_platdata(dev); |
||||
struct bcm283x_mu_priv *priv = dev_get_priv(dev); |
||||
|
||||
priv->regs = (struct bcm283x_mu_regs *)plat->base; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int bcm283x_mu_serial_getc(struct udevice *dev) |
||||
{ |
||||
struct bcm283x_mu_priv *priv = dev_get_priv(dev); |
||||
struct bcm283x_mu_regs *regs = priv->regs; |
||||
u32 data; |
||||
|
||||
/* Wait until there is data in the FIFO */ |
||||
if (!(readl(®s->lsr) & BCM283X_MU_LSR_RX_READY)) |
||||
return -EAGAIN; |
||||
|
||||
data = readl(®s->io); |
||||
|
||||
return (int)data; |
||||
} |
||||
|
||||
static int bcm283x_mu_serial_putc(struct udevice *dev, const char data) |
||||
{ |
||||
struct bcm283x_mu_priv *priv = dev_get_priv(dev); |
||||
struct bcm283x_mu_regs *regs = priv->regs; |
||||
|
||||
/* Wait until there is space in the FIFO */ |
||||
if (!(readl(®s->lsr) & BCM283X_MU_LSR_TX_EMPTY)) |
||||
return -EAGAIN; |
||||
|
||||
/* Send the character */ |
||||
writel(data, ®s->io); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int bcm283x_mu_serial_pending(struct udevice *dev, bool input) |
||||
{ |
||||
struct bcm283x_mu_priv *priv = dev_get_priv(dev); |
||||
struct bcm283x_mu_regs *regs = priv->regs; |
||||
unsigned int lsr = readl(®s->lsr); |
||||
|
||||
if (input) { |
||||
WATCHDOG_RESET(); |
||||
return lsr & BCM283X_MU_LSR_RX_READY; |
||||
} else { |
||||
return !(lsr & BCM283X_MU_LSR_TX_IDLE); |
||||
} |
||||
} |
||||
|
||||
static const struct dm_serial_ops bcm283x_mu_serial_ops = { |
||||
.putc = bcm283x_mu_serial_putc, |
||||
.pending = bcm283x_mu_serial_pending, |
||||
.getc = bcm283x_mu_serial_getc, |
||||
.setbrg = bcm283x_mu_serial_setbrg, |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(serial_bcm283x_mu) = { |
||||
.name = "serial_bcm283x_mu", |
||||
.id = UCLASS_SERIAL, |
||||
.platdata_auto_alloc_size = sizeof(struct bcm283x_mu_serial_platdata), |
||||
.probe = bcm283x_mu_serial_probe, |
||||
.ops = &bcm283x_mu_serial_ops, |
||||
.flags = DM_FLAG_PRE_RELOC, |
||||
.priv_auto_alloc_size = sizeof(struct bcm283x_mu_priv), |
||||
}; |
@ -0,0 +1,24 @@ |
||||
/*
|
||||
* (C) Copyright 2016 Stephen Warren <swarren@wwwdotorg.org> |
||||
* |
||||
* Derived from pl01x code: |
||||
* Copyright (c) 2014 Google, Inc |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#ifndef __serial_bcm283x_mu_h |
||||
#define __serial_bcm283x_mu_h |
||||
|
||||
/*
|
||||
*Information about a serial port |
||||
* |
||||
* @base: Register base address |
||||
*/ |
||||
struct bcm283x_mu_serial_platdata { |
||||
unsigned long base; |
||||
unsigned int clock; |
||||
bool skip_init; |
||||
}; |
||||
|
||||
#endif |
Loading…
Reference in new issue