diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/Kconfig | 8 | ||||
-rw-r--r-- | drivers/clk/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/clk_bcm6345.c | 78 | ||||
-rw-r--r-- | drivers/cpu/Makefile | 2 | ||||
-rw-r--r-- | drivers/cpu/bmips_cpu.c | 310 | ||||
-rw-r--r-- | drivers/gpio/Kconfig | 6 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/bcm6345_gpio.c | 125 | ||||
-rw-r--r-- | drivers/led/Kconfig | 19 | ||||
-rw-r--r-- | drivers/led/Makefile | 2 | ||||
-rw-r--r-- | drivers/led/led_bcm6328.c | 262 | ||||
-rw-r--r-- | drivers/led/led_bcm6358.c | 227 | ||||
-rw-r--r-- | drivers/power/domain/Kconfig | 7 | ||||
-rw-r--r-- | drivers/power/domain/Makefile | 1 | ||||
-rw-r--r-- | drivers/power/domain/bcm6328-power-domain.c | 83 | ||||
-rw-r--r-- | drivers/ram/Makefile | 1 | ||||
-rw-r--r-- | drivers/ram/bmips_ram.c | 126 | ||||
-rw-r--r-- | drivers/reset/Kconfig | 6 | ||||
-rw-r--r-- | drivers/reset/Makefile | 1 | ||||
-rw-r--r-- | drivers/reset/reset-bcm6345.c | 89 | ||||
-rw-r--r-- | drivers/serial/Kconfig | 14 | ||||
-rw-r--r-- | drivers/serial/Makefile | 1 | ||||
-rw-r--r-- | drivers/serial/serial_bcm6345.c | 300 | ||||
-rw-r--r-- | drivers/sysreset/Kconfig | 8 | ||||
-rw-r--r-- | drivers/sysreset/Makefile | 1 | ||||
-rw-r--r-- | drivers/sysreset/sysreset_syscon.c | 78 |
26 files changed, 1757 insertions, 0 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 5ca958c..44da716 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -20,6 +20,14 @@ config SPL_CLK setting up clocks within SPL, and allows the same drivers to be used as U-Boot proper. +config CLK_BCM6345 + bool "Clock controller driver for BCM6345" + depends on CLK && ARCH_BMIPS + default y + help + This clock driver adds support for enabling and disabling peripheral + clocks on BCM6345 SoCs. HW has no rate changing capabilities. + config CLK_BOSTON def_bool y if TARGET_BOSTON depends on CLK diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 01a8cd6..2746a80 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -17,6 +17,7 @@ obj-y += tegra/ obj-$(CONFIG_CLK_UNIPHIER) += uniphier/ obj-$(CONFIG_CLK_EXYNOS) += exynos/ obj-$(CONFIG_CLK_AT91) += at91/ +obj-$(CONFIG_CLK_BCM6345) += clk_bcm6345.o obj-$(CONFIG_CLK_BOSTON) += clk_boston.o obj-$(CONFIG_ARCH_ASPEED) += aspeed/ obj-$(CONFIG_STM32F7) += clk_stm32f7.o diff --git a/drivers/clk/clk_bcm6345.c b/drivers/clk/clk_bcm6345.c new file mode 100644 index 0000000..4c7a2df --- /dev/null +++ b/drivers/clk/clk_bcm6345.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com> + * + * Derived from linux/arch/mips/bcm63xx/clk.c: + * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm.h> +#include <errno.h> +#include <asm/io.h> + +#define MAX_CLKS 32 + +struct bcm6345_clk_priv { + void __iomem *regs; +}; + +static int bcm6345_clk_enable(struct clk *clk) +{ + struct bcm6345_clk_priv *priv = dev_get_priv(clk->dev); + + if (clk->id >= MAX_CLKS) + return -EINVAL; + + setbits_be32(priv->regs, BIT(clk->id)); + + return 0; +} + +static int bcm6345_clk_disable(struct clk *clk) +{ + struct bcm6345_clk_priv *priv = dev_get_priv(clk->dev); + + if (clk->id >= MAX_CLKS) + return -EINVAL; + + clrbits_be32(priv->regs, BIT(clk->id)); + + return 0; +} + +static struct clk_ops bcm6345_clk_ops = { + .disable = bcm6345_clk_disable, + .enable = bcm6345_clk_enable, +}; + +static const struct udevice_id bcm6345_clk_ids[] = { + { .compatible = "brcm,bcm6345-clk" }, + { /* sentinel */ } +}; + +static int bcm63xx_clk_probe(struct udevice *dev) +{ + struct bcm6345_clk_priv *priv = dev_get_priv(dev); + fdt_addr_t addr; + fdt_size_t size; + + addr = dev_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = ioremap(addr, size); + + return 0; +} + +U_BOOT_DRIVER(clk_bcm6345) = { + .name = "clk_bcm6345", + .id = UCLASS_CLK, + .of_match = bcm6345_clk_ids, + .ops = &bcm6345_clk_ops, + .probe = bcm63xx_clk_probe, + .priv_auto_alloc_size = sizeof(struct bcm6345_clk_priv), +}; diff --git a/drivers/cpu/Makefile b/drivers/cpu/Makefile index 8710160..db515f6 100644 --- a/drivers/cpu/Makefile +++ b/drivers/cpu/Makefile @@ -5,3 +5,5 @@ # SPDX-License-Identifier: GPL-2.0+ # obj-$(CONFIG_CPU) += cpu-uclass.o + +obj-$(CONFIG_ARCH_BMIPS) += bmips_cpu.o diff --git a/drivers/cpu/bmips_cpu.c b/drivers/cpu/bmips_cpu.c new file mode 100644 index 0000000..379acf2 --- /dev/null +++ b/drivers/cpu/bmips_cpu.c @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com> + * + * Derived from linux/arch/mips/bcm63xx/cpu.c: + * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> + * Copyright (C) 2009 Florian Fainelli <florian@openwrt.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <cpu.h> +#include <dm.h> +#include <errno.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define REV_CHIPID_SHIFT 16 +#define REV_CHIPID_MASK (0xffff << REV_CHIPID_SHIFT) +#define REV_LONG_CHIPID_SHIFT 12 +#define REV_LONG_CHIPID_MASK (0xfffff << REV_LONG_CHIPID_SHIFT) +#define REV_REVID_SHIFT 0 +#define REV_REVID_MASK (0xff << REV_REVID_SHIFT) + +#define REG_BCM6328_OTP 0x62c +#define BCM6328_TP1_DISABLED BIT(9) + +#define REG_BCM6328_MISC_STRAPBUS 0x1a40 +#define STRAPBUS_6328_FCVO_SHIFT 7 +#define STRAPBUS_6328_FCVO_MASK (0x1f << STRAPBUS_6328_FCVO_SHIFT) + +#define REG_BCM6358_DDR_DMIPSPLLCFG 0x12b8 +#define DMIPSPLLCFG_6358_M1_SHIFT 0 +#define DMIPSPLLCFG_6358_M1_MASK (0xff << DMIPSPLLCFG_6358_M1_SHIFT) +#define DMIPSPLLCFG_6358_N1_SHIFT 23 +#define DMIPSPLLCFG_6358_N1_MASK (0x3f << DMIPSPLLCFG_6358_N1_SHIFT) +#define DMIPSPLLCFG_6358_N2_SHIFT 29 +#define DMIPSPLLCFG_6358_N2_MASK (0x7 << DMIPSPLLCFG_6358_N2_SHIFT) + +#define REG_BCM63268_MISC_STRAPBUS 0x1814 +#define STRAPBUS_63268_FCVO_SHIFT 21 +#define STRAPBUS_63268_FCVO_MASK (0xf << STRAPBUS_63268_FCVO_SHIFT) + +struct bmips_cpu_priv; + +struct bmips_cpu_hw { + int (*get_cpu_desc)(struct bmips_cpu_priv *priv, char *buf, int size); + ulong (*get_cpu_freq)(struct bmips_cpu_priv *); + int (*get_cpu_count)(struct bmips_cpu_priv *); +}; + +struct bmips_cpu_priv { + void __iomem *regs; + const struct bmips_cpu_hw *hw; +}; + +/* Specific CPU Ops */ +static int bcm6358_get_cpu_desc(struct bmips_cpu_priv *priv, char *buf, + int size) +{ + unsigned short cpu_id; + unsigned char cpu_rev; + u32 val; + + val = readl_be(priv->regs); + cpu_id = (val & REV_CHIPID_MASK) >> REV_CHIPID_SHIFT; + cpu_rev = (val & REV_REVID_MASK) >> REV_REVID_SHIFT; + + snprintf(buf, size, "BCM%04X%02X", cpu_id, cpu_rev); + + return 0; +} + +static int bcm6328_get_cpu_desc(struct bmips_cpu_priv *priv, char *buf, + int size) +{ + unsigned int cpu_id; + unsigned char cpu_rev; + u32 val; + + val = readl_be(priv->regs); + cpu_id = (val & REV_LONG_CHIPID_MASK) >> REV_LONG_CHIPID_SHIFT; + cpu_rev = (val & REV_REVID_MASK) >> REV_REVID_SHIFT; + + snprintf(buf, size, "BCM%05X%02X", cpu_id, cpu_rev); + + return 0; +} + +static ulong bcm6328_get_cpu_freq(struct bmips_cpu_priv *priv) +{ + unsigned int mips_pll_fcvo; + + mips_pll_fcvo = readl_be(priv->regs + REG_BCM6328_MISC_STRAPBUS); + mips_pll_fcvo = (mips_pll_fcvo & STRAPBUS_6328_FCVO_MASK) + >> STRAPBUS_6328_FCVO_SHIFT; + + switch (mips_pll_fcvo) { + case 0x12: + case 0x14: + case 0x19: + return 160000000; + case 0x1c: + return 192000000; + case 0x13: + case 0x15: + return 200000000; + case 0x1a: + return 384000000; + case 0x16: + return 400000000; + default: + return 320000000; + } +} + +static ulong bcm6358_get_cpu_freq(struct bmips_cpu_priv *priv) +{ + unsigned int tmp, n1, n2, m1; + + tmp = readl_be(priv->regs + REG_BCM6358_DDR_DMIPSPLLCFG); + n1 = (tmp & DMIPSPLLCFG_6358_N1_MASK) >> DMIPSPLLCFG_6358_N1_SHIFT; + n2 = (tmp & DMIPSPLLCFG_6358_N2_MASK) >> DMIPSPLLCFG_6358_N2_SHIFT; + m1 = (tmp & DMIPSPLLCFG_6358_M1_MASK) >> DMIPSPLLCFG_6358_M1_SHIFT; + + return (16 * 1000000 * n1 * n2) / m1; +} + +static ulong bcm63268_get_cpu_freq(struct bmips_cpu_priv *priv) +{ + unsigned int mips_pll_fcvo; + + mips_pll_fcvo = readl_be(priv->regs + REG_BCM63268_MISC_STRAPBUS); + mips_pll_fcvo = (mips_pll_fcvo & STRAPBUS_63268_FCVO_MASK) + >> STRAPBUS_63268_FCVO_SHIFT; + + switch (mips_pll_fcvo) { + case 0x3: + case 0xe: + return 320000000; + case 0xa: + return 333000000; + case 0x2: + case 0xb: + case 0xf: + return 400000000; + default: + return 0; + } +} + +static int bcm6328_get_cpu_count(struct bmips_cpu_priv *priv) +{ + u32 val = readl_be(priv->regs + REG_BCM6328_OTP); + + if (val & BCM6328_TP1_DISABLED) + return 1; + else + return 2; +} + +static int bcm6358_get_cpu_count(struct bmips_cpu_priv *priv) +{ + return 2; +} + +static const struct bmips_cpu_hw bmips_cpu_bcm6328 = { + .get_cpu_desc = bcm6328_get_cpu_desc, + .get_cpu_freq = bcm6328_get_cpu_freq, + .get_cpu_count = bcm6328_get_cpu_count, +}; + +static const struct bmips_cpu_hw bmips_cpu_bcm6358 = { + .get_cpu_desc = bcm6358_get_cpu_desc, + .get_cpu_freq = bcm6358_get_cpu_freq, + .get_cpu_count = bcm6358_get_cpu_count, +}; + +static const struct bmips_cpu_hw bmips_cpu_bcm63268 = { + .get_cpu_desc = bcm6328_get_cpu_desc, + .get_cpu_freq = bcm63268_get_cpu_freq, + .get_cpu_count = bcm6358_get_cpu_count, +}; + +/* Generic CPU Ops */ +static int bmips_cpu_get_desc(struct udevice *dev, char *buf, int size) +{ + struct bmips_cpu_priv *priv = dev_get_priv(dev); + const struct bmips_cpu_hw *hw = priv->hw; + + return hw->get_cpu_desc(priv, buf, size); +} + +static int bmips_cpu_get_info(struct udevice *dev, struct cpu_info *info) +{ + struct bmips_cpu_priv *priv = dev_get_priv(dev); + const struct bmips_cpu_hw *hw = priv->hw; + + info->cpu_freq = hw->get_cpu_freq(priv); + info->features = BIT(CPU_FEAT_L1_CACHE); + info->features |= BIT(CPU_FEAT_MMU); + info->features |= BIT(CPU_FEAT_DEVICE_ID); + + return 0; +} + +static int bmips_cpu_get_count(struct udevice *dev) +{ + struct bmips_cpu_priv *priv = dev_get_priv(dev); + const struct bmips_cpu_hw *hw = priv->hw; + + return hw->get_cpu_count(priv); +} + +static int bmips_cpu_get_vendor(struct udevice *dev, char *buf, int size) +{ + snprintf(buf, size, "Broadcom"); + + return 0; +} + +static const struct cpu_ops bmips_cpu_ops = { + .get_desc = bmips_cpu_get_desc, + .get_info = bmips_cpu_get_info, + .get_count = bmips_cpu_get_count, + .get_vendor = bmips_cpu_get_vendor, +}; + +/* BMIPS CPU driver */ +int bmips_cpu_bind(struct udevice *dev) +{ + struct cpu_platdata *plat = dev_get_parent_platdata(dev); + + plat->cpu_id = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), + "reg", -1); + plat->device_id = read_c0_prid(); + + return 0; +} + +int bmips_cpu_probe(struct udevice *dev) +{ + struct bmips_cpu_priv *priv = dev_get_priv(dev); + const struct bmips_cpu_hw *hw = + (const struct bmips_cpu_hw *)dev_get_driver_data(dev); + fdt_addr_t addr; + fdt_size_t size; + + addr = dev_get_addr_size_index(dev_get_parent(dev), 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = ioremap(addr, size); + priv->hw = hw; + + return 0; +} + +static const struct udevice_id bmips_cpu_ids[] = { + { + .compatible = "brcm,bcm6328-cpu", + .data = (ulong)&bmips_cpu_bcm6328, + }, { + .compatible = "brcm,bcm6358-cpu", + .data = (ulong)&bmips_cpu_bcm6358, + }, { + .compatible = "brcm,bcm63268-cpu", + .data = (ulong)&bmips_cpu_bcm63268, + }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(bmips_cpu_drv) = { + .name = "bmips_cpu", + .id = UCLASS_CPU, + .of_match = bmips_cpu_ids, + .bind = bmips_cpu_bind, + .probe = bmips_cpu_probe, + .priv_auto_alloc_size = sizeof(struct bmips_cpu_priv), + .ops = &bmips_cpu_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + +#ifdef CONFIG_DISPLAY_CPUINFO +int print_cpuinfo(void) +{ + struct cpu_info cpu; + struct udevice *dev; + int err; + char desc[100]; + + err = uclass_get_device(UCLASS_CPU, 0, &dev); + if (err) + return 0; + + err = cpu_get_info(dev, &cpu); + if (err) + return 0; + + err = cpu_get_desc(dev, desc, sizeof(desc)); + if (err) + return 0; + + printf("Chip ID: %s, MIPS: ", desc); + print_freq(cpu.cpu_freq, "\n"); + + return 0; +} +#endif diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 9951611..325d053 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -21,6 +21,12 @@ config ALTERA_PIO Select this to enable PIO for Altera devices. Please find details on the "Embedded Peripherals IP User Guide" of Altera. +config BCM6345_GPIO + bool "BCM6345 GPIO driver" + depends on DM_GPIO && ARCH_BMIPS + help + This driver supports the GPIO banks on BCM6345 SoCs. + config DWAPB_GPIO bool "DWAPB GPIO driver" depends on DM && DM_GPIO diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 0ca845f..03df558 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_DM_74X164) += 74x164_gpio.o obj-$(CONFIG_AT91_GPIO) += at91_gpio.o obj-$(CONFIG_ATMEL_PIO4) += atmel_pio4.o +obj-$(CONFIG_BCM6345_GPIO) += bcm6345_gpio.o obj-$(CONFIG_INTEL_ICH6_GPIO) += intel_ich6_gpio.o obj-$(CONFIG_INTEL_BROADWELL_GPIO) += intel_broadwell_gpio.o obj-$(CONFIG_KIRKWOOD_GPIO) += kw_gpio.o diff --git a/drivers/gpio/bcm6345_gpio.c b/drivers/gpio/bcm6345_gpio.c new file mode 100644 index 0000000..1c46020 --- /dev/null +++ b/drivers/gpio/bcm6345_gpio.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com> + * + * Derived from linux/arch/mips/bcm63xx/gpio.c: + * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> + * Copyright (C) 2008-2011 Florian Fainelli <florian@openwrt.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <dm/device.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct bcm6345_gpio_priv { + void __iomem *reg_dirout; + void __iomem *reg_data; +}; + +static int bcm6345_gpio_get_value(struct udevice *dev, unsigned offset) +{ + struct bcm6345_gpio_priv *priv = dev_get_priv(dev); + + return !!(readl_be(priv->reg_data) & BIT(offset)); +} + +static int bcm6345_gpio_set_value(struct udevice *dev, unsigned offset, + int value) +{ + struct bcm6345_gpio_priv *priv = dev_get_priv(dev); + + if (value) + setbits_be32(priv->reg_data, BIT(offset)); + else + clrbits_be32(priv->reg_data, BIT(offset)); + + return 0; +} + +static int bcm6345_gpio_set_direction(void __iomem *dirout, unsigned offset, + bool input) +{ + if (input) + clrbits_be32(dirout, BIT(offset)); + else + setbits_be32(dirout, BIT(offset)); + + return 0; +} + +static int bcm6345_gpio_direction_input(struct udevice *dev, unsigned offset) +{ + struct bcm6345_gpio_priv *priv = dev_get_priv(dev); + + return bcm6345_gpio_set_direction(priv->reg_dirout, offset, 1); +} + +static int bcm6345_gpio_direction_output(struct udevice *dev, unsigned offset, + int value) +{ + struct bcm6345_gpio_priv *priv = dev_get_priv(dev); + + return bcm6345_gpio_set_direction(priv->reg_dirout, offset, 0); +} + +static int bcm6345_gpio_get_function(struct udevice *dev, unsigned offset) +{ + struct bcm6345_gpio_priv *priv = dev_get_priv(dev); + + if (readl_be(priv->reg_dirout) & BIT(offset)) + return GPIOF_OUTPUT; + else + return GPIOF_INPUT; +} + +static const struct dm_gpio_ops bcm6345_gpio_ops = { + .direction_input = bcm6345_gpio_direction_input, + .direction_output = bcm6345_gpio_direction_output, + .get_value = bcm6345_gpio_get_value, + .set_value = bcm6345_gpio_set_value, + .get_function = bcm6345_gpio_get_function, +}; + +static int bcm6345_gpio_probe(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct bcm6345_gpio_priv *priv = dev_get_priv(dev); + fdt_addr_t data_addr, dirout_addr; + fdt_size_t data_size, dirout_size; + + dirout_addr = dev_get_addr_size_index(dev, 0, &dirout_size); + if (dirout_addr == FDT_ADDR_T_NONE) + return -EINVAL; + + data_addr = dev_get_addr_size_index(dev, 1, &data_size); + if (data_addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->reg_data = ioremap(data_addr, data_size); + priv->reg_dirout = ioremap(dirout_addr, dirout_size); + + uc_priv->gpio_count = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), + "ngpios", 32); + uc_priv->bank_name = dev->name; + + return 0; +} + +static const struct udevice_id bcm6345_gpio_ids[] = { + { .compatible = "brcm,bcm6345-gpio" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(bcm6345_gpio) = { + .name = "bcm6345-gpio", + .id = UCLASS_GPIO, + .of_match = bcm6345_gpio_ids, + .ops = &bcm6345_gpio_ops, + .priv_auto_alloc_size = sizeof(struct bcm6345_gpio_priv), + .probe = bcm6345_gpio_probe, +}; diff --git a/drivers/led/Kconfig b/drivers/led/Kconfig index 309372a..5da5c4a 100644 --- a/drivers/led/Kconfig +++ b/drivers/led/Kconfig @@ -9,6 +9,25 @@ config LED can provide access to board-specific LEDs. Use of the device tree for configuration is encouraged. +config LED_BCM6328 + bool "LED Support for BCM6328" + depends on LED && ARCH_BMIPS + help + This option enables support for LEDs connected to the BCM6328 + LED HW controller accessed via MMIO registers. + HW blinking is supported and up to 24 LEDs can be controlled. + All LEDs can blink at the same time but the delay is shared, which + means that if one LED is set to blink at 100ms and then a different + LED is set to blink at 200ms, both will blink at 200ms. + +config LED_BCM6358 + bool "LED Support for BCM6358" + depends on LED && ARCH_BMIPS + help + This option enables support for LEDs connected to the BCM6358 + LED HW controller accessed via MMIO registers. + HW has no blinking capabilities and up to 32 LEDs can be controlled. + config LED_BLINK bool "Support LED blinking" depends on LED diff --git a/drivers/led/Makefile b/drivers/led/Makefile index 02367fd..9d079f8 100644 --- a/drivers/led/Makefile +++ b/drivers/led/Makefile @@ -6,4 +6,6 @@ # obj-y += led-uclass.o +obj-$(CONFIG_LED_BCM6328) += led_bcm6328.o +obj-$(CONFIG_LED_BCM6358) += led_bcm6358.o obj-$(CONFIG_$(SPL_)LED_GPIO) += led_gpio.o diff --git a/drivers/led/led_bcm6328.c b/drivers/led/led_bcm6328.c new file mode 100644 index 0000000..ef8c6a7 --- /dev/null +++ b/drivers/led/led_bcm6328.c @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <led.h> +#include <asm/io.h> +#include <dm/lists.h> + +#define LEDS_MAX 24 + +/* LED Init register */ +#define LED_INIT_REG 0x00 +#define LED_INIT_FASTINTV_MS 20 +#define LED_INIT_FASTINTV_SHIFT 6 +#define LED_INIT_FASTINTV_MASK (0x3f << LED_INIT_FASTINTV_SHIFT) +#define LED_INIT_SLEDEN_SHIFT 12 +#define LED_INIT_SLEDEN_MASK (1 << LED_INIT_SLEDEN_SHIFT) +#define LED_INIT_SLEDMUX_SHIFT 13 +#define LED_INIT_SLEDMUX_MASK (1 << LED_INIT_SLEDMUX_SHIFT) +#define LED_INIT_SLEDCLKNPOL_SHIFT 14 +#define LED_INIT_SLEDCLKNPOL_MASK (1 << LED_INIT_SLEDCLKNPOL_SHIFT) +#define LED_INIT_SLEDDATAPPOL_SHIFT 15 +#define LED_INIT_SLEDDATANPOL_MASK (1 << LED_INIT_SLEDDATAPPOL_SHIFT) +#define LED_INIT_SLEDSHIFTDIR_SHIFT 16 +#define LED_INIT_SLEDSHIFTDIR_MASK (1 << LED_INIT_SLEDSHIFTDIR_SHIFT) + +/* LED Mode registers */ +#define LED_MODE_REG_HI 0x04 +#define LED_MODE_REG_LO 0x08 +#define LED_MODE_ON 0 +#define LED_MODE_FAST 1 +#define LED_MODE_BLINK 2 +#define LED_MODE_OFF 3 +#define LED_MODE_MASK 0x3 + +DECLARE_GLOBAL_DATA_PTR; + +struct bcm6328_led_priv { + void __iomem *regs; + void __iomem *mode; + uint8_t shift; + bool active_low; +}; + +static unsigned long bcm6328_led_get_mode(struct bcm6328_led_priv *priv) +{ + return ((readl_be(priv->mode) >> priv->shift) & LED_MODE_MASK); +} + +static int bcm6328_led_set_mode(struct bcm6328_led_priv *priv, uint8_t mode) +{ + clrsetbits_be32(priv->mode, (LED_MODE_MASK << priv->shift), + (mode << priv->shift)); + + return 0; +} + +static enum led_state_t bcm6328_led_get_state(struct udevice *dev) +{ + struct bcm6328_led_priv *priv = dev_get_priv(dev); + enum led_state_t state = LEDST_OFF; + + switch (bcm6328_led_get_mode(priv)) { +#ifdef CONFIG_LED_BLINK + case LED_MODE_BLINK: + case LED_MODE_FAST: + state = LEDST_BLINK; + break; +#endif + case LED_MODE_OFF: + state = (priv->active_low ? LEDST_ON : LEDST_OFF); + break; + case LED_MODE_ON: + state = (priv->active_low ? LEDST_OFF : LEDST_ON); + break; + } + + return state; +} + +static int bcm6328_led_set_state(struct udevice *dev, enum led_state_t state) +{ + struct bcm6328_led_priv *priv = dev_get_priv(dev); + unsigned long mode; + + switch (state) { +#ifdef CONFIG_LED_BLINK + case LEDST_BLINK: + mode = LED_MODE_BLINK; + break; +#endif + case LEDST_OFF: + mode = (priv->active_low ? LED_MODE_ON : LED_MODE_OFF); + break; + case LEDST_ON: + mode = (priv->active_low ? LED_MODE_OFF : LED_MODE_ON); + break; + case LEDST_TOGGLE: + if (bcm6328_led_get_state(dev) == LEDST_OFF) + return bcm6328_led_set_state(dev, LEDST_ON); + else + return bcm6328_led_set_state(dev, LEDST_OFF); + break; + default: + return -ENOSYS; + } + + return bcm6328_led_set_mode(priv, mode); +} + +#ifdef CONFIG_LED_BLINK +static unsigned long bcm6328_blink_delay(int delay) +{ + unsigned long bcm6328_delay = delay; + + bcm6328_delay += (LED_INIT_FASTINTV_MS / 2); + bcm6328_delay /= LED_INIT_FASTINTV_MS; + bcm6328_delay <<= LED_INIT_FASTINTV_SHIFT; + + if (bcm6328_delay > LED_INIT_FASTINTV_MASK) + return LED_INIT_FASTINTV_MASK; + else + return bcm6328_delay; +} + +static int bcm6328_led_set_period(struct udevice *dev, int period_ms) +{ + struct bcm6328_led_priv *priv = dev_get_priv(dev); + + clrsetbits_be32(priv->regs + LED_INIT_REG, LED_INIT_FASTINTV_MASK, + bcm6328_blink_delay(period_ms)); + + return 0; +} +#endif + +static const struct led_ops bcm6328_led_ops = { + .get_state = bcm6328_led_get_state, + .set_state = bcm6328_led_set_state, +#ifdef CONFIG_LED_BLINK + .set_period = bcm6328_led_set_period, +#endif +}; + +static int bcm6328_led_probe(struct udevice *dev) +{ + struct led_uc_plat *uc_plat = dev_get_uclass_platdata(dev); + fdt_addr_t addr; + fdt_size_t size; + + /* Top-level LED node */ + if (!uc_plat->label) { + void __iomem *regs; + u32 set_bits = 0; + + addr = dev_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + regs = ioremap(addr, size); + + if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev), + "brcm,serial-leds")) + set_bits |= LED_INIT_SLEDEN_MASK; + if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev), + "brcm,serial-mux")) + set_bits |= LED_INIT_SLEDMUX_MASK; + if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev), + "brcm,serial-clk-low")) + set_bits |= LED_INIT_SLEDCLKNPOL_MASK; + if (!fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev), + "brcm,serial-dat-low")) + set_bits |= LED_INIT_SLEDDATANPOL_MASK; + if (!fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev), + "brcm,serial-shift-inv")) + set_bits |= LED_INIT_SLEDSHIFTDIR_MASK; + + clrsetbits_be32(regs + LED_INIT_REG, ~0, set_bits); + } else { + struct bcm6328_led_priv *priv = dev_get_priv(dev); + unsigned int pin; + + addr = dev_get_addr_size_index(dev_get_parent(dev), 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + pin = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), "reg", + LEDS_MAX); + if (pin >= LEDS_MAX) + return -EINVAL; + + priv->regs = ioremap(addr, size); + if (pin < 8) { + /* LEDs 0-7 (bits 47:32) */ + priv->mode = priv->regs + LED_MODE_REG_HI; + priv->shift = (pin << 1); + } else { + /* LEDs 8-23 (bits 31:0) */ + priv->mode = priv->regs + LED_MODE_REG_LO; + priv->shift = ((pin - 8) << 1); + } + + if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev), + "active-low")) + priv->active_low = true; + } + + return 0; +} + +static int bcm6328_led_bind(struct udevice *parent) +{ + const void *blob = gd->fdt_blob; + int node; + + for (node = fdt_first_subnode(blob, dev_of_offset(parent)); + node > 0; + node = fdt_next_subnode(blob, node)) { + struct led_uc_plat *uc_plat; + struct udevice *dev; + const char *label; + int ret; + + label = fdt_getprop(blob, node, "label", NULL); + if (!label) { + debug("%s: node %s has no label\n", __func__, + fdt_get_name(blob, node, NULL)); + return -EINVAL; + } + + ret = device_bind_driver_to_node(parent, "bcm6328-led", + fdt_get_name(blob, node, NULL), + node, &dev); + if (ret) + return ret; + + uc_plat = dev_get_uclass_platdata(dev); + uc_plat->label = label; + } + + return 0; +} + +static const struct udevice_id bcm6328_led_ids[] = { + { .compatible = "brcm,bcm6328-leds" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(bcm6328_led) = { + .name = "bcm6328-led", + .id = UCLASS_LED, + .of_match = bcm6328_led_ids, + .ops = &bcm6328_led_ops, + .bind = bcm6328_led_bind, + .probe = bcm6328_led_probe, + .priv_auto_alloc_size = sizeof(struct bcm6328_led_priv), +}; diff --git a/drivers/led/led_bcm6358.c b/drivers/led/led_bcm6358.c new file mode 100644 index 0000000..11caecd --- /dev/null +++ b/drivers/led/led_bcm6358.c @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <led.h> +#include <asm/io.h> +#include <dm/lists.h> + +#define LEDS_MAX 32 +#define LEDS_WAIT 100 + +/* LED Mode register */ +#define LED_MODE_REG 0x0 +#define LED_MODE_OFF 0 +#define LED_MODE_ON 1 +#define LED_MODE_MASK 1 + +/* LED Control register */ +#define LED_CTRL_REG 0x4 +#define LED_CTRL_CLK_MASK 0x3 +#define LED_CTRL_CLK_1 0 +#define LED_CTRL_CLK_2 1 +#define LED_CTRL_CLK_4 2 +#define LED_CTRL_CLK_8 3 +#define LED_CTRL_POL_SHIFT 2 +#define LED_CTRL_POL_MASK (1 << LED_CTRL_POL_SHIFT) +#define LED_CTRL_BUSY_SHIFT 3 +#define LED_CTRL_BUSY_MASK (1 << LED_CTRL_BUSY_SHIFT) + +DECLARE_GLOBAL_DATA_PTR; + +struct bcm6358_led_priv { + void __iomem *regs; + uint8_t pin; + bool active_low; +}; + +static void bcm6358_led_busy(void __iomem *regs) +{ + while (readl_be(regs + LED_CTRL_REG) & LED_CTRL_BUSY_MASK) + udelay(LEDS_WAIT); +} + +static unsigned long bcm6358_led_get_mode(struct bcm6358_led_priv *priv) +{ + bcm6358_led_busy(priv->regs); + + return (readl_be(priv->regs + LED_MODE_REG) >> priv->pin) & + LED_MODE_MASK; +} + +static int bcm6358_led_set_mode(struct bcm6358_led_priv *priv, uint8_t mode) +{ + bcm6358_led_busy(priv->regs); + + clrsetbits_be32(priv->regs + LED_MODE_REG, + (LED_MODE_MASK << priv->pin), + (mode << priv->pin)); + + return 0; +} + +static enum led_state_t bcm6358_led_get_state(struct udevice *dev) +{ + struct bcm6358_led_priv *priv = dev_get_priv(dev); + enum led_state_t state = LEDST_OFF; + + switch (bcm6358_led_get_mode(priv)) { + case LED_MODE_OFF: + state = (priv->active_low ? LEDST_ON : LEDST_OFF); + break; + case LED_MODE_ON: + state = (priv->active_low ? LEDST_OFF : LEDST_ON); + break; + } + + return state; +} + +static int bcm6358_led_set_state(struct udevice *dev, enum led_state_t state) +{ + struct bcm6358_led_priv *priv = dev_get_priv(dev); + unsigned long mode; + + switch (state) { + case LEDST_OFF: + mode = (priv->active_low ? LED_MODE_ON : LED_MODE_OFF); + break; + case LEDST_ON: + mode = (priv->active_low ? LED_MODE_OFF : LED_MODE_ON); + break; + case LEDST_TOGGLE: + if (bcm6358_led_get_state(dev) == LEDST_OFF) + return bcm6358_led_set_state(dev, LEDST_ON); + else + return bcm6358_led_set_state(dev, LEDST_OFF); + break; + default: + return -ENOSYS; + } + + return bcm6358_led_set_mode(priv, mode); +} + +static const struct led_ops bcm6358_led_ops = { + .get_state = bcm6358_led_get_state, + .set_state = bcm6358_led_set_state, +}; + +static int bcm6358_led_probe(struct udevice *dev) +{ + struct led_uc_plat *uc_plat = dev_get_uclass_platdata(dev); + fdt_addr_t addr; + fdt_size_t size; + + /* Top-level LED node */ + if (!uc_plat->label) { + void __iomem *regs; + unsigned int clk_div; + u32 set_bits = 0; + + addr = dev_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + regs = ioremap(addr, size); + + if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev), + "brcm,clk-dat-low")) + set_bits |= LED_CTRL_POL_MASK; + clk_div = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), + "brcm,clk-div", LED_CTRL_CLK_1); + switch (clk_div) { + case 8: + set_bits |= LED_CTRL_CLK_8; + break; + case 4: + set_bits |= LED_CTRL_CLK_4; + break; + case 2: + set_bits |= LED_CTRL_CLK_2; + break; + default: + set_bits |= LED_CTRL_CLK_1; + break; + } + + bcm6358_led_busy(regs); + clrsetbits_be32(regs + LED_CTRL_REG, + LED_CTRL_POL_MASK | LED_CTRL_CLK_MASK, + set_bits); + } else { + struct bcm6358_led_priv *priv = dev_get_priv(dev); + unsigned int pin; + + addr = dev_get_addr_size_index(dev_get_parent(dev), 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + pin = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), "reg", + LEDS_MAX); + if (pin >= LEDS_MAX) + return -EINVAL; + + priv->regs = ioremap(addr, size); + priv->pin = pin; + + if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev), + "active-low")) + priv->active_low = true; + } + + return 0; +} + +static int bcm6358_led_bind(struct udevice *parent) +{ + const void *blob = gd->fdt_blob; + int node; + + for (node = fdt_first_subnode(blob, dev_of_offset(parent)); + node > 0; + node = fdt_next_subnode(blob, node)) { + struct led_uc_plat *uc_plat; + struct udevice *dev; + const char *label; + int ret; + + label = fdt_getprop(blob, node, "label", NULL); + if (!label) { + debug("%s: node %s has no label\n", __func__, + fdt_get_name(blob, node, NULL)); + return -EINVAL; + } + + ret = device_bind_driver_to_node(parent, "bcm6358-led", + fdt_get_name(blob, node, NULL), + node, &dev); + if (ret) + return ret; + + uc_plat = dev_get_uclass_platdata(dev); + uc_plat->label = label; + } + + return 0; +} + +static const struct udevice_id bcm6358_led_ids[] = { + { .compatible = "brcm,bcm6358-leds" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(bcm6358_led) = { + .name = "bcm6358-led", + .id = UCLASS_LED, + .of_match = bcm6358_led_ids, + .bind = bcm6358_led_bind, + .probe = bcm6358_led_probe, + .priv_auto_alloc_size = sizeof(struct bcm6358_led_priv), + .ops = &bcm6358_led_ops, +}; diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig index 132e332..7cfa761 100644 --- a/drivers/power/domain/Kconfig +++ b/drivers/power/domain/Kconfig @@ -9,6 +9,13 @@ config POWER_DOMAIN domains). This may be used to save power. This API provides the means to control such power management hardware. +config BCM6328_POWER_DOMAIN + bool "Enable the BCM6328 power domain driver" + depends on POWER_DOMAIN && ARCH_BMIPS + help + Enable support for manipulating BCM6345 power domains via MMIO + mapped registers. + config SANDBOX_POWER_DOMAIN bool "Enable the sandbox power domain test driver" depends on POWER_DOMAIN && SANDBOX diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile index 2c3d926..c7d7644 100644 --- a/drivers/power/domain/Makefile +++ b/drivers/power/domain/Makefile @@ -3,6 +3,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_POWER_DOMAIN) += power-domain-uclass.o +obj-$(CONFIG_BCM6328_POWER_DOMAIN) += bcm6328-power-domain.o obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain.o obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain-test.o obj-$(CONFIG_TEGRA186_POWER_DOMAIN) += tegra186-power-domain.o diff --git a/drivers/power/domain/bcm6328-power-domain.c b/drivers/power/domain/bcm6328-power-domain.c new file mode 100644 index 0000000..15638bf --- /dev/null +++ b/drivers/power/domain/bcm6328-power-domain.c @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <power-domain-uclass.h> +#include <asm/io.h> + +#define MAX_DOMAINS 32 + +struct bcm6328_power_domain { + void __iomem *regs; +}; + +static int bcm6328_power_domain_request(struct power_domain *power_domain) +{ + if (power_domain->id >= MAX_DOMAINS) + return -EINVAL; + + return 0; +} + +static int bcm6328_power_domain_free(struct power_domain *power_domain) +{ + return 0; +} + +static int bcm6328_power_domain_on(struct power_domain *power_domain) +{ + struct bcm6328_power_domain *priv = dev_get_priv(power_domain->dev); + + clrbits_be32(priv->regs, BIT(power_domain->id)); + + return 0; +} + +static int bcm6328_power_domain_off(struct power_domain *power_domain) +{ + struct bcm6328_power_domain *priv = dev_get_priv(power_domain->dev); + + setbits_be32(priv->regs, BIT(power_domain->id)); + + return 0; +} + +static int bcm6328_power_domain_probe(struct udevice *dev) +{ + struct bcm6328_power_domain *priv = dev_get_priv(dev); + fdt_addr_t addr; + fdt_size_t size; + + addr = dev_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = ioremap(addr, size); + + return 0; +} + +static const struct udevice_id bcm6328_power_domain_ids[] = { + { .compatible = "brcm,bcm6328-power-domain" }, + { /* sentinel */ } +}; + +struct power_domain_ops bcm6328_power_domain_ops = { + .free = bcm6328_power_domain_free, + .off = bcm6328_power_domain_off, + .on = bcm6328_power_domain_on, + .request = bcm6328_power_domain_request, +}; + +U_BOOT_DRIVER(bcm6328_power_domain) = { + .name = "bcm6328_power_domain", + .id = UCLASS_POWER_DOMAIN, + .of_match = bcm6328_power_domain_ids, + .ops = &bcm6328_power_domain_ops, + .priv_auto_alloc_size = sizeof(struct bcm6328_power_domain), + .probe = bcm6328_power_domain_probe, +}; diff --git a/drivers/ram/Makefile b/drivers/ram/Makefile index ecb036d..c409c48 100644 --- a/drivers/ram/Makefile +++ b/drivers/ram/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_RAM) += ram-uclass.o obj-$(CONFIG_SANDBOX) += sandbox_ram.o obj-$(CONFIG_STM32_SDRAM) += stm32_sdram.o +obj-$(CONFIG_ARCH_BMIPS) += bmips_ram.o diff --git a/drivers/ram/bmips_ram.c b/drivers/ram/bmips_ram.c new file mode 100644 index 0000000..9c0b23b --- /dev/null +++ b/drivers/ram/bmips_ram.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com> + * + * Derived from linux/arch/mips/bcm63xx/cpu.c: + * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> + * Copyright (C) 2009 Florian Fainelli <florian@openwrt.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <ram.h> +#include <asm/io.h> +#include <dm/device.h> + +#define MEMC_CFG_REG 0x4 +#define MEMC_CFG_32B_SHIFT 1 +#define MEMC_CFG_32B_MASK (1 << MEMC_CFG_32B_SHIFT) +#define MEMC_CFG_COL_SHIFT 3 +#define MEMC_CFG_COL_MASK (0x3 << MEMC_CFG_COL_SHIFT) +#define MEMC_CFG_ROW_SHIFT 6 +#define MEMC_CFG_ROW_MASK (0x3 << MEMC_CFG_ROW_SHIFT) + +#define DDR_CSEND_REG 0x8 + +struct bmips_ram_priv; + +struct bmips_ram_hw { + ulong (*get_ram_size)(struct bmips_ram_priv *); +}; + +struct bmips_ram_priv { + void __iomem *regs; + const struct bmips_ram_hw *hw; +}; + +static ulong bcm6328_get_ram_size(struct bmips_ram_priv *priv) +{ + return readl_be(priv->regs + DDR_CSEND_REG) << 24; +} + +static ulong bcm6358_get_ram_size(struct bmips_ram_priv *priv) +{ + unsigned int cols = 0, rows = 0, is_32bits = 0, banks = 0; + u32 val; + + val = readl_be(priv->regs + MEMC_CFG_REG); + rows = (val & MEMC_CFG_ROW_MASK) >> MEMC_CFG_ROW_SHIFT; + cols = (val & MEMC_CFG_COL_MASK) >> MEMC_CFG_COL_SHIFT; + is_32bits = (val & MEMC_CFG_32B_MASK) ? 0 : 1; + banks = 2; + + /* 0 => 11 address bits ... 2 => 13 address bits */ + rows += 11; + + /* 0 => 8 address bits ... 2 => 10 address bits */ + cols += 8; + + return 1 << (cols + rows + (is_32bits + 1) + banks); +} + +static int bmips_ram_get_info(struct udevice *dev, struct ram_info *info) +{ + struct bmips_ram_priv *priv = dev_get_priv(dev); + const struct bmips_ram_hw *hw = priv->hw; + + info->base = 0x80000000; + info->size = hw->get_ram_size(priv); + + return 0; +} + +static const struct ram_ops bmips_ram_ops = { + .get_info = bmips_ram_get_info, +}; + +static const struct bmips_ram_hw bmips_ram_bcm6328 = { + .get_ram_size = bcm6328_get_ram_size, +}; + +static const struct bmips_ram_hw bmips_ram_bcm6358 = { + .get_ram_size = bcm6358_get_ram_size, +}; + +static const struct udevice_id bmips_ram_ids[] = { + { + .compatible = "brcm,bcm6328-mc", + .data = (ulong)&bmips_ram_bcm6328, + }, { + .compatible = "brcm,bcm6358-mc", + .data = (ulong)&bmips_ram_bcm6358, + }, { + .compatible = "brcm,bcm63268-mc", + .data = (ulong)&bmips_ram_bcm6328, + }, + { /* sentinel */ } +}; + +static int bmips_ram_probe(struct udevice *dev) +{ + struct bmips_ram_priv *priv = dev_get_priv(dev); + const struct bmips_ram_hw *hw = + (const struct bmips_ram_hw *)dev_get_driver_data(dev); + fdt_addr_t addr; + fdt_size_t size; + + addr = dev_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = ioremap(addr, size); + priv->hw = hw; + + return 0; +} + +U_BOOT_DRIVER(bmips_ram) = { + .name = "bmips-mc", + .id = UCLASS_RAM, + .of_match = bmips_ram_ids, + .probe = bmips_ram_probe, + .priv_auto_alloc_size = sizeof(struct bmips_ram_priv), + .ops = &bmips_ram_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 80f4646..e6af7da 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -42,6 +42,12 @@ config TEGRA186_RESET Enable support for manipulating Tegra's on-SoC reset signals via IPC requests to the BPMP (Boot and Power Management Processor). +config RESET_BCM6345 + bool "Reset controller driver for BCM6345" + depends on DM_RESET && ARCH_BMIPS + help + Support reset controller on BCM6345. + config RESET_UNIPHIER bool "Reset controller driver for UniPhier SoCs" depends on ARCH_UNIPHIER diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 630b4b4..d5e06c2 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -8,5 +8,6 @@ obj-$(CONFIG_SANDBOX_MBOX) += sandbox-reset-test.o obj-$(CONFIG_STI_RESET) += sti-reset.o obj-$(CONFIG_TEGRA_CAR_RESET) += tegra-car-reset.o obj-$(CONFIG_TEGRA186_RESET) += tegra186-reset.o +obj-$(CONFIG_RESET_BCM6345) += reset-bcm6345.o obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o obj-$(CONFIG_AST2500_RESET) += ast2500-reset.o diff --git a/drivers/reset/reset-bcm6345.c b/drivers/reset/reset-bcm6345.c new file mode 100644 index 0000000..774c2a7 --- /dev/null +++ b/drivers/reset/reset-bcm6345.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com> + * + * Derived from linux/arch/mips/bcm63xx/reset.c: + * Copyright (C) 2012 Jonas Gorski <jonas.gorski@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <reset-uclass.h> +#include <asm/io.h> + +#define MAX_RESETS 32 + +struct bcm6345_reset_priv { + void __iomem *regs; +}; + +static int bcm6345_reset_assert(struct reset_ctl *rst) +{ + struct bcm6345_reset_priv *priv = dev_get_priv(rst->dev); + + clrbits_be32(priv->regs, BIT(rst->id)); + mdelay(20); + + return 0; +} + +static int bcm6345_reset_deassert(struct reset_ctl *rst) +{ + struct bcm6345_reset_priv *priv = dev_get_priv(rst->dev); + + setbits_be32(priv->regs, BIT(rst->id)); + mdelay(20); + + return 0; +} + +static int bcm6345_reset_free(struct reset_ctl *rst) +{ + return 0; +} + +static int bcm6345_reset_request(struct reset_ctl *rst) +{ + if (rst->id >= MAX_RESETS) + return -EINVAL; + + return bcm6345_reset_assert(rst); +} + +struct reset_ops bcm6345_reset_reset_ops = { + .free = bcm6345_reset_free, + .request = bcm6345_reset_request, + .rst_assert = bcm6345_reset_assert, + .rst_deassert = bcm6345_reset_deassert, +}; + +static const struct udevice_id bcm6345_reset_ids[] = { + { .compatible = "brcm,bcm6345-reset" }, + { /* sentinel */ } +}; + +static int bcm6345_reset_probe(struct udevice *dev) +{ + struct bcm6345_reset_priv *priv = dev_get_priv(dev); + fdt_addr_t addr; + fdt_size_t size; + + addr = dev_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = ioremap(addr, size); + + return 0; +} + +U_BOOT_DRIVER(bcm6345_reset) = { + .name = "bcm6345-reset", + .id = UCLASS_RESET, + .of_match = bcm6345_reset_ids, + .ops = &bcm6345_reset_reset_ops, + .probe = bcm6345_reset_probe, + .priv_auto_alloc_size = sizeof(struct bcm6345_reset_priv), +}; diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 5832066..7249945 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -145,6 +145,14 @@ config DEBUG_UART_ATMEL will need to provide parameters to make this work. The driver will be available until the real driver-model serial is running. +config DEBUG_UART_BCM6345 + bool "BCM6345 UART" + depends on BCM6345_SERIAL + help + Select this to enable a debug UART on BCM6345 SoCs. You + will need to provide parameters to make this work. The driver will + be available until the real driver model serial is running. + config DEBUG_UART_NS16550 bool "ns16550" help @@ -350,6 +358,12 @@ config ATMEL_USART configured in the device tree, and input clock frequency can be got from the clk node. +config BCM6345_SERIAL + bool "Support for BCM6345 UART" + depends on DM_SERIAL && ARCH_BMIPS + help + Select this to enable UART on BCM6345 SoCs. + config FSL_LPUART bool "Freescale LPUART support" help diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 4382cf9..dca31b2 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_ALTERA_JTAG_UART) += altera_jtag_uart.o obj-$(CONFIG_AR933X_UART) += serial_ar933x.o obj-$(CONFIG_ARM_DCC) += arm_dcc.o obj-$(CONFIG_ATMEL_USART) += atmel_usart.o +obj-$(CONFIG_BCM6345_SERIAL) += serial_bcm6345.o obj-$(CONFIG_EFI_APP) += serial_efi.o obj-$(CONFIG_LPC32XX_HSUART) += lpc32xx_hsuart.o obj-$(CONFIG_MCFUART) += mcfuart.o diff --git a/drivers/serial/serial_bcm6345.c b/drivers/serial/serial_bcm6345.c new file mode 100644 index 0000000..db270e3 --- /dev/null +++ b/drivers/serial/serial_bcm6345.c @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com> + * + * Derived from linux/drivers/tty/serial/bcm63xx_uart.c: + * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <clk.h> +#include <debug_uart.h> +#include <errno.h> +#include <serial.h> +#include <asm/io.h> +#include <asm/types.h> +#include <dm/device.h> + +/* UART Control register */ +#define UART_CTL_REG 0x0 +#define UART_CTL_RXTIMEOUT_MASK 0x1f +#define UART_CTL_RXTIMEOUT_5 0x5 +#define UART_CTL_RSTRXFIFO_SHIFT 6 +#define UART_CTL_RSTRXFIFO_MASK (1 << UART_CTL_RSTRXFIFO_SHIFT) +#define UART_CTL_RSTTXFIFO_SHIFT 7 +#define UART_CTL_RSTTXFIFO_MASK (1 << UART_CTL_RSTTXFIFO_SHIFT) +#define UART_CTL_STOPBITS_SHIFT 8 +#define UART_CTL_STOPBITS_MASK (0xf << UART_CTL_STOPBITS_SHIFT) +#define UART_CTL_STOPBITS_1 (0x7 << UART_CTL_STOPBITS_SHIFT) +#define UART_CTL_BITSPERSYM_SHIFT 12 +#define UART_CTL_BITSPERSYM_MASK (0x3 << UART_CTL_BITSPERSYM_SHIFT) +#define UART_CTL_BITSPERSYM_8 (0x3 << UART_CTL_BITSPERSYM_SHIFT) +#define UART_CTL_XMITBRK_SHIFT 14 +#define UART_CTL_XMITBRK_MASK (1 << UART_CTL_XMITBRK_SHIFT) +#define UART_CTL_RSVD_SHIFT 15 +#define UART_CTL_RSVD_MASK (1 << UART_CTL_RSVD_SHIFT) +#define UART_CTL_RXPAREVEN_SHIFT 16 +#define UART_CTL_RXPAREVEN_MASK (1 << UART_CTL_RXPAREVEN_SHIFT) +#define UART_CTL_RXPAREN_SHIFT 17 +#define UART_CTL_RXPAREN_MASK (1 << UART_CTL_RXPAREN_SHIFT) +#define UART_CTL_TXPAREVEN_SHIFT 18 +#define UART_CTL_TXPAREVEN_MASK (1 << UART_CTL_TXPAREVEN_SHIFT) +#define UART_CTL_TXPAREN_SHIFT 19 +#define UART_CTL_TXPAREN_MASK (1 << UART_CTL_TXPAREN_SHIFT) +#define UART_CTL_LOOPBACK_SHIFT 20 +#define UART_CTL_LOOPBACK_MASK (1 << UART_CTL_LOOPBACK_SHIFT) +#define UART_CTL_RXEN_SHIFT 21 +#define UART_CTL_RXEN_MASK (1 << UART_CTL_RXEN_SHIFT) +#define UART_CTL_TXEN_SHIFT 22 +#define UART_CTL_TXEN_MASK (1 << UART_CTL_TXEN_SHIFT) +#define UART_CTL_BRGEN_SHIFT 23 +#define UART_CTL_BRGEN_MASK (1 << UART_CTL_BRGEN_SHIFT) + +/* UART Baudword register */ +#define UART_BAUD_REG 0x4 + +/* UART FIFO Config register */ +#define UART_FIFO_CFG_REG 0x8 +#define UART_FIFO_CFG_RX_SHIFT 8 +#define UART_FIFO_CFG_RX_MASK (0xf << UART_FIFO_CFG_RX_SHIFT) +#define UART_FIFO_CFG_RX_4 (0x4 << UART_FIFO_CFG_RX_SHIFT) +#define UART_FIFO_CFG_TX_SHIFT 12 +#define UART_FIFO_CFG_TX_MASK (0xf << UART_FIFO_CFG_TX_SHIFT) +#define UART_FIFO_CFG_TX_4 (0x4 << UART_FIFO_CFG_TX_SHIFT) + +/* UART Interrupt register */ +#define UART_IR_REG 0x10 +#define UART_IR_STAT(x) (1 << (x)) +#define UART_IR_TXEMPTY 5 +#define UART_IR_RXOVER 7 +#define UART_IR_RXNOTEMPTY 11 + +/* UART FIFO register */ +#define UART_FIFO_REG 0x14 +#define UART_FIFO_VALID_MASK 0xff +#define UART_FIFO_FRAMEERR_SHIFT 8 +#define UART_FIFO_FRAMEERR_MASK (1 << UART_FIFO_FRAMEERR_SHIFT) +#define UART_FIFO_PARERR_SHIFT 9 +#define UART_FIFO_PARERR_MASK (1 << UART_FIFO_PARERR_SHIFT) +#define UART_FIFO_BRKDET_SHIFT 10 +#define UART_FIFO_BRKDET_MASK (1 << UART_FIFO_BRKDET_SHIFT) +#define UART_FIFO_ANYERR_MASK (UART_FIFO_FRAMEERR_MASK | \ + UART_FIFO_PARERR_MASK | \ + UART_FIFO_BRKDET_MASK) + +struct bcm6345_serial_priv { + void __iomem *base; + ulong uartclk; +}; + +/* enable rx & tx operation on uart */ +static void bcm6345_serial_enable(void __iomem *base) +{ + setbits_be32(base + UART_CTL_REG, UART_CTL_BRGEN_MASK | + UART_CTL_TXEN_MASK | UART_CTL_RXEN_MASK); +} + +/* disable rx & tx operation on uart */ +static void bcm6345_serial_disable(void __iomem *base) +{ + clrbits_be32(base + UART_CTL_REG, UART_CTL_BRGEN_MASK | + UART_CTL_TXEN_MASK | UART_CTL_RXEN_MASK); +} + +/* clear all unread data in rx fifo and unsent data in tx fifo */ +static void bcm6345_serial_flush(void __iomem *base) +{ + /* empty rx and tx fifo */ + setbits_be32(base + UART_CTL_REG, UART_CTL_RSTRXFIFO_MASK | + UART_CTL_RSTTXFIFO_MASK); + + /* read any pending char to make sure all irq status are cleared */ + readl_be(base + UART_FIFO_REG); +} + +static int bcm6345_serial_init(void __iomem *base, ulong clk, u32 baudrate) +{ + u32 val; + + /* mask all irq and flush port */ + bcm6345_serial_disable(base); + bcm6345_serial_flush(base); + + /* set uart control config */ + clrsetbits_be32(base + UART_CTL_REG, + /* clear rx timeout */ + UART_CTL_RXTIMEOUT_MASK | + /* clear stop bits */ + UART_CTL_STOPBITS_MASK | + /* clear bits per symbol */ + UART_CTL_BITSPERSYM_MASK | + /* clear xmit break */ + UART_CTL_XMITBRK_MASK | + /* clear reserved bit */ + UART_CTL_RSVD_MASK | + /* disable parity */ + UART_CTL_RXPAREN_MASK | + UART_CTL_TXPAREN_MASK | + /* disable loopback */ + UART_CTL_LOOPBACK_MASK, + /* set timeout to 5 */ + UART_CTL_RXTIMEOUT_5 | + /* set 8 bits/symbol */ + UART_CTL_BITSPERSYM_8 | + /* set parity to even */ + UART_CTL_RXPAREVEN_MASK | + UART_CTL_TXPAREVEN_MASK); + + /* set uart fifo config */ + clrsetbits_be32(base + UART_FIFO_CFG_REG, + /* clear fifo config */ + UART_FIFO_CFG_RX_MASK | + UART_FIFO_CFG_TX_MASK, + /* set fifo config to 4 */ + UART_FIFO_CFG_RX_4 | + UART_FIFO_CFG_TX_4); + + /* set baud rate */ + val = (clk / baudrate) / 16; + if (val & 0x1) + val = val; + else + val = val / 2 - 1; + writel_be(val, base + UART_BAUD_REG); + + /* clear interrupts */ + writel_be(0, base + UART_IR_REG); + + /* enable uart */ + bcm6345_serial_enable(base); + + return 0; +} + +static int bcm6345_serial_pending(struct udevice *dev, bool input) +{ + struct bcm6345_serial_priv *priv = dev_get_priv(dev); + u32 val = readl_be(priv->base + UART_IR_REG); + + if (input) + return !!(val & UART_IR_STAT(UART_IR_RXNOTEMPTY)); + else + return !(val & UART_IR_STAT(UART_IR_TXEMPTY)); +} + +static int bcm6345_serial_setbrg(struct udevice *dev, int baudrate) +{ + struct bcm6345_serial_priv *priv = dev_get_priv(dev); + + return bcm6345_serial_init(priv->base, priv->uartclk, baudrate); +} + +static int bcm6345_serial_putc(struct udevice *dev, const char ch) +{ + struct bcm6345_serial_priv *priv = dev_get_priv(dev); + u32 val; + + val = readl_be(priv->base + UART_IR_REG); + if (!(val & UART_IR_STAT(UART_IR_TXEMPTY))) + return -EAGAIN; + + writel_be(ch, priv->base + UART_FIFO_REG); + + return 0; +} + +static int bcm6345_serial_getc(struct udevice *dev) +{ + struct bcm6345_serial_priv *priv = dev_get_priv(dev); + u32 val; + + val = readl_be(priv->base + UART_IR_REG); + if (val & UART_IR_STAT(UART_IR_RXOVER)) + setbits_be32(priv->base + UART_CTL_REG, + UART_CTL_RSTRXFIFO_MASK); + if (!(val & UART_IR_STAT(UART_IR_RXNOTEMPTY))) + return -EAGAIN; + + val = readl_be(priv->base + UART_FIFO_REG); + if (val & UART_FIFO_ANYERR_MASK) + return -EAGAIN; + + return val & UART_FIFO_VALID_MASK; +} + +static int bcm6345_serial_probe(struct udevice *dev) +{ + struct bcm6345_serial_priv *priv = dev_get_priv(dev); + struct clk clk; + fdt_addr_t addr; + fdt_size_t size; + int ret; + + /* get address */ + addr = dev_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->base = ioremap(addr, size); + + /* get clock rate */ + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) + return ret; + priv->uartclk = clk_get_rate(&clk) / 2; + clk_free(&clk); + + /* initialize serial */ + return bcm6345_serial_init(priv->base, priv->uartclk, CONFIG_BAUDRATE); +} + +static const struct dm_serial_ops bcm6345_serial_ops = { + .putc = bcm6345_serial_putc, + .pending = bcm6345_serial_pending, + .getc = bcm6345_serial_getc, + .setbrg = bcm6345_serial_setbrg, +}; + +static const struct udevice_id bcm6345_serial_ids[] = { + { .compatible = "brcm,bcm6345-uart" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(bcm6345_serial) = { + .name = "bcm6345-uart", + .id = UCLASS_SERIAL, + .of_match = bcm6345_serial_ids, + .probe = bcm6345_serial_probe, + .priv_auto_alloc_size = sizeof(struct bcm6345_serial_priv), + .ops = &bcm6345_serial_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + +#ifdef CONFIG_DEBUG_UART_BCM6345 +static inline void _debug_uart_init(void) +{ + void __iomem *base = (void __iomem *)CONFIG_DEBUG_UART_BASE; + + bcm6345_serial_init(base, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE); +} + +static inline void wait_xfered(void __iomem *base) +{ + do { + u32 val = readl_be(base + UART_IR_REG); + if (val & UART_IR_STAT(UART_IR_TXEMPTY)) + break; + } while (1); +} + +static inline void _debug_uart_putc(int ch) +{ + void __iomem *base = (void __iomem *)CONFIG_DEBUG_UART_BASE; + + wait_xfered(base); + writel_be(ch, base + UART_FIFO_REG); + wait_xfered(base); +} + +DEBUG_UART_FUNCS +#endif diff --git a/drivers/sysreset/Kconfig b/drivers/sysreset/Kconfig index 9664630..b2f7464 100644 --- a/drivers/sysreset/Kconfig +++ b/drivers/sysreset/Kconfig @@ -23,4 +23,12 @@ config SYSRESET_PSCI must be running on your system. endif + +config SYSRESET_SYSCON + bool "Enable support for mfd syscon reboot driver" + select REGMAP + select SYSCON + help + Reboot support for generic SYSCON mapped register reset. + endmenu diff --git a/drivers/sysreset/Makefile b/drivers/sysreset/Makefile index 7bb8406..bd352e7 100644 --- a/drivers/sysreset/Makefile +++ b/drivers/sysreset/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_SYSRESET) += sysreset-uclass.o obj-$(CONFIG_SYSRESET_PSCI) += sysreset_psci.o +obj-$(CONFIG_SYSRESET_SYSCON) += sysreset_syscon.o ifndef CONFIG_SPL_BUILD obj-$(CONFIG_ROCKCHIP_RK3036) += sysreset_rk3036.o diff --git a/drivers/sysreset/sysreset_syscon.c b/drivers/sysreset/sysreset_syscon.c new file mode 100644 index 0000000..3818fae --- /dev/null +++ b/drivers/sysreset/sysreset_syscon.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com> + * + * Derived from linux/drivers/power/reset/syscon-reboot.c: + * Copyright (C) 2013, Applied Micro Circuits Corporation + * Author: Feng Kan <fkan@apm.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <regmap.h> +#include <sysreset.h> +#include <syscon.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct syscon_reboot_priv { + struct regmap *regmap; + unsigned int offset; + unsigned int mask; +}; + +static int syscon_reboot_request(struct udevice *dev, enum sysreset_t type) +{ + struct syscon_reboot_priv *priv = dev_get_priv(dev); + + regmap_write(priv->regmap, priv->offset, priv->mask); + + return -EINPROGRESS; +} + +static struct sysreset_ops syscon_reboot_ops = { + .request = syscon_reboot_request, +}; + +int syscon_reboot_probe(struct udevice *dev) +{ + struct udevice *syscon; + struct syscon_reboot_priv *priv = dev_get_priv(dev); + int err; + + err = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, + "regmap", &syscon); + if (err) { + error("unable to find syscon device\n"); + return err; + } + + priv->regmap = syscon_get_regmap(syscon); + if (!priv->regmap) { + error("unable to find regmap\n"); + return -ENODEV; + } + + priv->offset = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), + "offset", 0); + priv->mask = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), + "mask", 0); + + return 0; +} + +static const struct udevice_id syscon_reboot_ids[] = { + { .compatible = "syscon-reboot" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(syscon_reboot) = { + .name = "syscon_reboot", + .id = UCLASS_SYSRESET, + .of_match = syscon_reboot_ids, + .probe = syscon_reboot_probe, + .priv_auto_alloc_size = sizeof(struct syscon_reboot_priv), + .ops = &syscon_reboot_ops, +}; |