summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/clk-rpi.c42
-rw-r--r--drivers/clocksource/bcm2835.c3
-rw-r--r--drivers/i2c/busses/Kconfig4
-rw-r--r--drivers/i2c/busses/Makefile1
-rw-r--r--drivers/i2c/busses/i2c-bcm283x.c327
-rw-r--r--drivers/mci/mci-bcm2835.c4
-rw-r--r--drivers/serial/serial_ns16550.c4
-rw-r--r--drivers/watchdog/bcm2835_wdt.c23
8 files changed, 369 insertions, 39 deletions
diff --git a/drivers/clk/clk-rpi.c b/drivers/clk/clk-rpi.c
index 59ae8e59ba..71badc04c0 100644
--- a/drivers/clk/clk-rpi.c
+++ b/drivers/clk/clk-rpi.c
@@ -40,10 +40,29 @@ static struct clk *rpi_register_firmware_clock(u32 clock_id, const char *name)
return clk_fixed(name, msg->get_clock_rate.body.resp.rate_hz);
}
-static int bcm2835_cprman_probe(struct device_d *dev)
+static int bcm2835_cprman_init(struct device_d *dev)
{
struct clk *clk_cs;
+ clk_cs = clk_fixed("bcm2835-cs", 1 * 1000 * 1000);
+ clk_register_clkdev(clk_cs, NULL, "bcm2835-cs");
+
+ return 0;
+}
+
+static int rpi_cprman_probe(struct device_d *dev)
+{
+ int (*init)(struct device_d *dev);
+
+ init = device_get_match_data(dev);
+ if (init) {
+ int ret;
+
+ ret = init(dev);
+ if (ret)
+ return ret;
+ }
+
clks[BCM2835_CLOCK_EMMC] =
rpi_register_firmware_clock(BCM2835_MBOX_CLOCK_ID_EMMC,
"bcm2835_mci0");
@@ -56,12 +75,15 @@ static int bcm2835_cprman_probe(struct device_d *dev)
if (IS_ERR(clks[BCM2835_CLOCK_VPU]))
return PTR_ERR(clks[BCM2835_CLOCK_VPU]);
+ clks[BCM2711_CLOCK_EMMC2] =
+ rpi_register_firmware_clock(BCM2835_MBOX_CLOCK_ID_EMMC2,
+ "bcm2711_emmc2");
+ if (IS_ERR(clks[BCM2711_CLOCK_EMMC2]))
+ return PTR_ERR(clks[BCM2711_CLOCK_EMMC2]);
+
clks[BCM2835_CLOCK_UART] = clk_fixed("uart0-pl0110", 48 * 1000 * 1000);
clk_register_clkdev(clks[BCM2835_CLOCK_UART], NULL, "uart0-pl0110");
- clk_cs = clk_fixed("bcm2835-cs", 1 * 1000 * 1000);
- clk_register_clkdev(clk_cs, NULL, "bcm2835-cs");
-
clk_data.clks = clks;
clk_data.clk_num = BCM2711_CLOCK_END;
of_clk_add_provider(dev->device_node, of_clk_src_onecell_get, &clk_data);
@@ -70,16 +92,14 @@ static int bcm2835_cprman_probe(struct device_d *dev)
}
static __maybe_unused struct of_device_id bcm2835_cprman_dt_ids[] = {
- {
- .compatible = "brcm,bcm2835-cprman",
- }, {
- /* sentinel */
- }
+ { .compatible = "brcm,bcm2835-cprman", .data = bcm2835_cprman_init },
+ { .compatible = "brcm,bcm2711-cprman" },
+ { /* sentinel */ }
};
static struct driver_d bcm2835_cprman_driver = {
- .probe = bcm2835_cprman_probe,
- .name = "bcm2835-cprman",
+ .probe = rpi_cprman_probe,
+ .name = "raspberrypi-cprman",
.of_compatible = DRV_OF_COMPAT(bcm2835_cprman_dt_ids),
};
core_platform_driver(bcm2835_cprman_driver);
diff --git a/drivers/clocksource/bcm2835.c b/drivers/clocksource/bcm2835.c
index d84341fc40..d5d3e3477d 100644
--- a/drivers/clocksource/bcm2835.c
+++ b/drivers/clocksource/bcm2835.c
@@ -28,7 +28,8 @@ static uint64_t stc_read_cycles(void)
static struct clocksource bcm2835_stc = {
.read = stc_read_cycles,
.mask = CLOCKSOURCE_MASK(32),
- .priority = 80,
+ /* Give the architected timer precedence on AArch64 */
+ .priority = IS_ENABLED(CONFIG_CPU_V8) ? 60 : 80,
};
static int bcm2835_cs_probe(struct device_d *dev)
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 58f8656067..766aa5edfa 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -17,6 +17,10 @@ config I2C_AT91
bool "AT91 I2C Master driver"
depends on ARCH_AT91
+config I2C_BCM283X
+ bool "BCM283X I2C Master driver"
+ depends on ARCH_BCM283X || COMPILE_TEST
+
config I2C_IMX
bool "MPC85xx/MPC5200/i.MX I2C Master driver"
depends on ARCH_IMX || ARCH_MPC85XX || ARCH_MPC5200 || ARCH_LAYERSCAPE
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index a8661f605e..a1ab46fb28 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_I2C_AT91) += i2c-at91.o
+obj-$(CONFIG_I2C_BCM283X) += i2c-bcm283x.o
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_I2C_IMX) += i2c-imx.o
lwl-$(CONFIG_I2C_IMX_EARLY) += i2c-imx-early.o
diff --git a/drivers/i2c/busses/i2c-bcm283x.c b/drivers/i2c/busses/i2c-bcm283x.c
new file mode 100644
index 0000000000..097f73d983
--- /dev/null
+++ b/drivers/i2c/busses/i2c-bcm283x.c
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * I2C bus driver for the BSC peripheral on Broadcom's bcm283x family of SoCs
+ *
+ * Based on documentation published by Raspberry Pi foundation and the kernel
+ * driver written by Stephen Warren.
+ *
+ * Copyright (C) Stephen Warren
+ * Copyright (C) 2022 Daniel BrĂ¡t
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <malloc.h>
+#include <i2c/i2c.h>
+#include <i2c/i2c-algo-bit.h>
+#include <linux/iopoll.h>
+#include <linux/clk.h>
+#include <init.h>
+#include <of_address.h>
+
+// BSC C (Control) register
+#define BSC_C_READ BIT(0)
+#define BSC_C_CLEAR1 BIT(4)
+#define BSC_C_CLEAR2 BIT(5)
+#define BSC_C_ST BIT(7)
+#define BSC_C_INTD BIT(8)
+#define BSC_C_INTT BIT(9)
+#define BSC_C_INTR BIT(10)
+#define BSC_C_I2CEN BIT(15)
+
+// BSC S (Status) register
+#define BSC_S_TA BIT(0)
+#define BSC_S_DONE BIT(1)
+#define BSC_S_TXW BIT(2)
+#define BSC_S_RXR BIT(3)
+#define BSC_S_TXD BIT(4)
+#define BSC_S_RXD BIT(5)
+#define BSC_S_TXE BIT(6)
+#define BSC_S_RXF BIT(7)
+#define BSC_S_ERR BIT(8)
+#define BSC_S_CLKT BIT(9)
+
+// BSC A (Address) register
+#define BSC_A_MASK 0x7f
+
+// Constants
+#define BSC_CDIV_MIN 0x0002
+#define BSC_CDIV_MAX 0xfffe
+#define BSC_FIFO_SIZE 16U
+
+struct __packed bcm283x_i2c_regs {
+ u32 c;
+ u32 s;
+ u32 dlen;
+ u32 a;
+ u32 fifo;
+ u32 div;
+ u32 del;
+ u32 clkt;
+};
+
+struct bcm283x_i2c {
+ struct i2c_adapter adapter;
+ struct clk *mclk;
+ struct bcm283x_i2c_regs __iomem *regs;
+ u32 bitrate;
+};
+
+static inline struct bcm283x_i2c *to_bcm283x_i2c(struct i2c_adapter *adapter)
+{
+ return container_of(adapter, struct bcm283x_i2c, adapter);
+}
+
+static inline int bcm283x_i2c_init(struct bcm283x_i2c *bcm_i2c)
+{
+ struct device_d *dev = &bcm_i2c->adapter.dev;
+ u32 mclk_rate, cdiv, redl, fedl;
+
+ /*
+ * Reset control reg, flush FIFO, clear all flags and disable
+ * clock stretching
+ */
+ writel(0UL, &bcm_i2c->regs->c);
+ writel(BSC_C_CLEAR1, &bcm_i2c->regs->c);
+ writel(BSC_S_DONE | BSC_S_ERR | BSC_S_CLKT, &bcm_i2c->regs->s);
+ writel(0UL, &bcm_i2c->regs->clkt);
+
+ /*
+ * Set the divider based on the master clock frequency and the
+ * requested i2c bitrate
+ */
+ mclk_rate = clk_get_rate(bcm_i2c->mclk);
+ cdiv = DIV_ROUND_UP(mclk_rate, bcm_i2c->bitrate);
+ dev_dbg(dev, "bcm283x_i2c_init: mclk_rate=%u, cdiv=%08x\n",
+ mclk_rate, cdiv);
+ /* Note from kernel driver:
+ * Per the datasheet, the register is always interpreted as an even
+ * number, by rounding down. In other words, the LSB is ignored. So,
+ * if the LSB is set, increment the divider to avoid any issue.
+ */
+ if (cdiv & 1)
+ cdiv++;
+ if ((cdiv < BSC_CDIV_MIN) || (cdiv > BSC_CDIV_MAX)) {
+ dev_err(dev, "failed to calculate valid clock divider value\n");
+ return -EINVAL;
+ }
+ dev_dbg(dev, "bcm283x_i2c_init: cdiv adjusted to %04x\n", cdiv);
+ fedl = max(cdiv / 16, 1U);
+ redl = max(cdiv / 4, 1U);
+ dev_dbg(dev, "bcm283x_i2c_init: fedl=%04x, redl=%04x\n", fedl, redl);
+ writel(cdiv & 0xffff, &bcm_i2c->regs->div);
+ writel((fedl << 16) | redl, &bcm_i2c->regs->del);
+ dev_dbg(dev, "bcm283x_i2c_init: regs->div=%08x, regs->del=%08x\n",
+ readl(&bcm_i2c->regs->div), readl(&bcm_i2c->regs->del));
+
+ return 0;
+}
+
+/*
+ * Macro to calculate generous timeout for given bitrate and number of bytes
+ */
+#define calc_byte_timeout_us(bitrate) \
+ (3 * 9 * DIV_ROUND_UP(1000000, bitrate))
+#define calc_msg_timeout_us(bitrate, bytes) \
+ ((bytes + 1) * calc_byte_timeout_us(bitrate))
+
+static int bcm283x_i2c_msg_xfer(struct bcm283x_i2c *bcm_i2c,
+ struct i2c_msg *msg)
+{
+ int ret;
+ u32 reg_c, reg_s, reg_dlen, timeout;
+ struct device_d *dev = &bcm_i2c->adapter.dev;
+ bool msg_read = (msg->flags & I2C_M_RD) > 0;
+ bool msg_10bit = (msg->flags & I2C_M_TEN) > 0;
+ u16 buf_pos = 0;
+ u32 bytes_left = reg_dlen = msg->len;
+
+ if (msg_10bit && msg_read) {
+ timeout = calc_byte_timeout_us(bcm_i2c->bitrate);
+ writel(1UL, &bcm_i2c->regs->dlen);
+ writel(msg->addr & 0xff, &bcm_i2c->regs->fifo);
+ writel(((msg->addr >> 8) | 0x78) & BSC_A_MASK, &bcm_i2c->regs->a);
+ writel(BSC_C_ST | BSC_C_I2CEN, &bcm_i2c->regs->c);
+ ret = readl_poll_timeout(&bcm_i2c->regs->s, reg_s,
+ reg_s & (BSC_S_TA | BSC_S_ERR), timeout);
+
+ if (ret) {
+ dev_err(dev, "timeout: 10bit read initilization\n");
+ return ret;
+ }
+ if (reg_s & BSC_S_ERR)
+ goto nack;
+
+ } else if (msg_10bit) {
+ reg_dlen++;
+ writel(msg->addr & 0xff, &bcm_i2c->regs->fifo);
+ writel(((msg->addr >> 8) | 0x78) & BSC_A_MASK, &bcm_i2c->regs->a);
+ } else {
+ writel(msg->addr & BSC_A_MASK, &bcm_i2c->regs->a);
+ }
+
+ writel(reg_dlen, &bcm_i2c->regs->dlen);
+ reg_c = BSC_C_ST | BSC_C_I2CEN;
+ if (msg_read)
+ reg_c |= BSC_C_READ;
+ writel(reg_c, &bcm_i2c->regs->c);
+
+ if (msg_read) {
+ /*
+ * Read out data from FIFO as soon as it is available
+ */
+ timeout = calc_byte_timeout_us(bcm_i2c->bitrate);
+ for (; bytes_left; bytes_left--) {
+ ret = readl_poll_timeout(&bcm_i2c->regs->s, reg_s,
+ reg_s & (BSC_S_RXD | BSC_S_ERR),
+ timeout);
+
+ if (ret) {
+ dev_err(dev, "timeout: waiting for data in FIFO\n");
+ return ret;
+ }
+ if (reg_s & BSC_S_ERR)
+ goto nack;
+
+ msg->buf[buf_pos++] = (u8) readl(&bcm_i2c->regs->fifo);
+ }
+ } else {
+ timeout = calc_byte_timeout_us(bcm_i2c->bitrate);
+ /*
+ * Feed data to FIFO as soon as there is space for them
+ */
+ for (; bytes_left; bytes_left--) {
+ ret = readl_poll_timeout(&bcm_i2c->regs->s, reg_s,
+ reg_s & (BSC_S_TXD | BSC_S_ERR),
+ timeout);
+
+ if (ret) {
+ dev_err(dev, "timeout: waiting for space in FIFO\n");
+ return ret;
+ }
+ if (reg_s & BSC_S_ERR)
+ goto nack;
+
+ writel(msg->buf[buf_pos++], &bcm_i2c->regs->fifo);
+ }
+ }
+
+ /*
+ * Wait for the current transfer to finish and then flush FIFO
+ * and clear any flags so that we are ready for next msg
+ */
+ timeout = calc_msg_timeout_us(bcm_i2c->bitrate, reg_dlen);
+ ret = readl_poll_timeout(&bcm_i2c->regs->s, reg_s,
+ reg_s & (BSC_S_DONE | BSC_S_ERR), timeout);
+
+ if (ret) {
+ dev_err(dev, "timeout: waiting for transfer to end\n");
+ return ret;
+ }
+ if (reg_s & BSC_S_ERR)
+ goto nack;
+ writel(BSC_S_DONE | BSC_S_ERR | BSC_S_CLKT, &bcm_i2c->regs->s);
+ writel(BSC_C_CLEAR1 | BSC_C_I2CEN, &bcm_i2c->regs->c);
+ return 0;
+nack:
+ dev_dbg(dev, "device with addr %x didn't ACK\n", msg->addr);
+ return -EREMOTEIO;
+}
+
+static int bcm283x_i2c_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int count)
+{
+ int ret, i;
+ struct i2c_msg *msg;
+ struct bcm283x_i2c *bcm_i2c = to_bcm283x_i2c(adapter);
+
+ /*
+ * Reset control reg, flush FIFO, clear flags and enable the BSC
+ */
+ writel(0UL, &bcm_i2c->regs->c);
+ writel(BSC_C_CLEAR1, &bcm_i2c->regs->c);
+ writel(BSC_S_DONE | BSC_S_ERR | BSC_S_CLKT, &bcm_i2c->regs->s);
+ writel(BSC_C_I2CEN, &bcm_i2c->regs->c);
+
+ for (i = 0; i < count; i++) {
+ msg = &msgs[i];
+ ret = bcm283x_i2c_msg_xfer(bcm_i2c, msg);
+ if (ret)
+ goto out;
+ }
+
+ writel(0UL, &bcm_i2c->regs->c);
+ return count;
+out:
+ writel(0UL, &bcm_i2c->regs->c);
+ writel(BSC_C_CLEAR1, &bcm_i2c->regs->c);
+ writel(BSC_S_DONE | BSC_S_ERR | BSC_S_CLKT, &bcm_i2c->regs->s);
+ return ret;
+}
+
+static int bcm283x_i2c_probe(struct device_d *dev)
+{
+ int ret;
+ struct resource *iores;
+ struct bcm283x_i2c *bcm_i2c;
+ struct device_node *np = dev->device_node;
+
+ bcm_i2c = xzalloc(sizeof(*bcm_i2c));
+
+ if (!np) {
+ ret = -ENXIO;
+ goto err;
+ }
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores)) {
+ dev_err(dev, "could not get iomem region\n");
+ ret = PTR_ERR(iores);
+ goto err;
+ }
+ bcm_i2c->regs = IOMEM(iores->start);
+
+ bcm_i2c->mclk = clk_get(dev, NULL);
+ if (IS_ERR(bcm_i2c->mclk)) {
+ dev_err(dev, "could not acquire clock\n");
+ ret = PTR_ERR(bcm_i2c->mclk);
+ goto err;
+ }
+ clk_enable(bcm_i2c->mclk);
+
+ bcm_i2c->bitrate = I2C_MAX_STANDARD_MODE_FREQ;
+ of_property_read_u32(np, "clock-frequency", &bcm_i2c->bitrate);
+ if (bcm_i2c->bitrate > I2C_MAX_FAST_MODE_FREQ) {
+ dev_err(dev, "clock frequency of %u is not supported\n",
+ bcm_i2c->bitrate);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ bcm_i2c->adapter.master_xfer = bcm283x_i2c_xfer;
+ bcm_i2c->adapter.nr = dev->id;
+ bcm_i2c->adapter.dev.parent = dev;
+ bcm_i2c->adapter.dev.device_node = np;
+
+ ret = bcm283x_i2c_init(bcm_i2c);
+ if (ret)
+ goto err;
+
+ return i2c_add_numbered_adapter(&bcm_i2c->adapter);
+err:
+ free(bcm_i2c);
+ return ret;
+}
+
+static struct of_device_id bcm283x_i2c_dt_ids[] = {
+ { .compatible = "brcm,bcm2835-i2c", },
+ { .compatible = "brcm,bcm2711-i2c", },
+ { /* sentinel */ }
+};
+
+static struct driver_d bcm283x_i2c_driver = {
+ .name = "i2c-bcm283x",
+ .probe = bcm283x_i2c_probe,
+ .of_compatible = DRV_OF_COMPAT(bcm283x_i2c_dt_ids),
+};
+device_platform_driver(bcm283x_i2c_driver);
diff --git a/drivers/mci/mci-bcm2835.c b/drivers/mci/mci-bcm2835.c
index 3380fa2afc..f092156f9a 100644
--- a/drivers/mci/mci-bcm2835.c
+++ b/drivers/mci/mci-bcm2835.c
@@ -324,7 +324,7 @@ static int bcm2835_mci_reset(struct mci_host *mci, struct device_d *mci_dev)
sdhci_write32(&host->sdhci,
SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL,
- 0x00);
+ (SDHCI_BUS_VOLTAGE_330 | SDHCI_BUS_POWER_EN) << 8);
sdhci_write32(&host->sdhci, SDHCI_ACMD12_ERR__HOST_CONTROL2,
0x00);
sdhci_write32(&host->sdhci,
@@ -424,6 +424,8 @@ static __maybe_unused struct of_device_id bcm2835_mci_compatible[] = {
{
.compatible = "brcm,bcm2835-sdhci",
}, {
+ .compatible = "brcm,bcm2711-emmc2",
+ }, {
/* sentinel */
}
};
diff --git a/drivers/serial/serial_ns16550.c b/drivers/serial/serial_ns16550.c
index f7cab8db4b..07f951e535 100644
--- a/drivers/serial/serial_ns16550.c
+++ b/drivers/serial/serial_ns16550.c
@@ -242,14 +242,10 @@ static void ns16550_jz_init_port(struct console_device *cdev)
ns16550_serial_init_port(cdev);
}
-#define BCM2836_AUX_CLOCK_ENB 0x3f215004 /* BCM2835 AUX Clock enable register */
-#define BCM2836_AUX_CLOCK_EN_UART BIT(0) /* Bit 0 enables the Miniuart */
-
static void rpi_init_port(struct console_device *cdev)
{
struct ns16550_priv *priv = to_ns16550_priv(cdev);
- writeb(BCM2836_AUX_CLOCK_EN_UART, BCM2836_AUX_CLOCK_ENB);
priv->plat.shift = 2;
/*
* We double the clock rate since the 16550 will divide by 16
diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c
index ece80837b5..c6e6a289f2 100644
--- a/drivers/watchdog/bcm2835_wdt.c
+++ b/drivers/watchdog/bcm2835_wdt.c
@@ -10,28 +10,7 @@
#include <restart.h>
#include <watchdog.h>
-#define PM_RSTC 0x1c
-#define PM_RSTS 0x20
-#define PM_WDOG 0x24
-
-#define PM_WDOG_RESET 0000000000
-#define PM_PASSWORD 0x5a000000
-#define PM_WDOG_TIME_SET 0x000fffff
-#define PM_RSTC_WRCFG_CLR 0xffffffcf
-#define PM_RSTC_WRCFG_SET 0x00000030
-#define PM_RSTC_WRCFG_FULL_RESET 0x00000020
-#define PM_RSTC_RESET 0x00000102
-
-#define PM_RSTS_HADPOR_SET 0x00001000
-#define PM_RSTS_HADSRH_SET 0x00000400
-#define PM_RSTS_HADSRF_SET 0x00000200
-#define PM_RSTS_HADSRQ_SET 0x00000100
-#define PM_RSTS_HADWRH_SET 0x00000040
-#define PM_RSTS_HADWRF_SET 0x00000020
-#define PM_RSTS_HADWRQ_SET 0x00000010
-#define PM_RSTS_HADDRH_SET 0x00000004
-#define PM_RSTS_HADDRF_SET 0x00000002
-#define PM_RSTS_HADDRQ_SET 0x00000001
+#include <soc/bcm283x/wdt.h>
#define SECS_TO_WDOG_TICKS(x) ((x) << 16)