diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/clk-rpi.c | 42 | ||||
-rw-r--r-- | drivers/clocksource/bcm2835.c | 3 | ||||
-rw-r--r-- | drivers/i2c/busses/Kconfig | 4 | ||||
-rw-r--r-- | drivers/i2c/busses/Makefile | 1 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-bcm283x.c | 327 | ||||
-rw-r--r-- | drivers/mci/mci-bcm2835.c | 4 | ||||
-rw-r--r-- | drivers/serial/serial_ns16550.c | 4 | ||||
-rw-r--r-- | drivers/watchdog/bcm2835_wdt.c | 23 |
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) |