diff options
Diffstat (limited to 'drivers')
46 files changed, 1895 insertions, 233 deletions
diff --git a/drivers/adc/adc-uclass.c b/drivers/adc/adc-uclass.c index 16600be821c..b02430eb7d7 100644 --- a/drivers/adc/adc-uclass.c +++ b/drivers/adc/adc-uclass.c @@ -382,7 +382,7 @@ static int adc_vdd_plat_set(struct udevice *dev) if (!ret) return adc_vdd_plat_update(dev); - if (ret != -ENOENT) + if (ret != -ENOSYS && ret != -ENOENT) return ret; /* No vdd-supply phandle. */ @@ -406,7 +406,7 @@ static int adc_vss_plat_set(struct udevice *dev) if (!ret) return adc_vss_plat_update(dev); - if (ret != -ENOENT) + if (ret != -ENOSYS && ret != -ENOENT) return ret; /* No vss-supply phandle. */ diff --git a/drivers/adc/rockchip-saradc.c b/drivers/adc/rockchip-saradc.c index f6832ab3073..7cf9735f60d 100644 --- a/drivers/adc/rockchip-saradc.c +++ b/drivers/adc/rockchip-saradc.c @@ -241,7 +241,7 @@ int rockchip_saradc_probe(struct udevice *dev) { struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(dev); struct rockchip_saradc_priv *priv = dev_get_priv(dev); - struct udevice *vref; + struct udevice *vref = NULL; struct clk clk; int vref_uv; int ret; @@ -259,7 +259,7 @@ int rockchip_saradc_probe(struct udevice *dev) priv->active_channel = -1; ret = device_get_supply_regulator(dev, "vref-supply", &vref); - if (ret) { + if (ret && uc_pdata->vdd_microvolts <= 0) { printf("can't get vref-supply: %d\n", ret); return ret; } @@ -267,7 +267,10 @@ int rockchip_saradc_probe(struct udevice *dev) if (priv->reset) rockchip_saradc_reset_controller(priv->reset); - vref_uv = regulator_get_value(vref); + if (vref) + vref_uv = regulator_get_value(vref); + else + vref_uv = uc_pdata->vdd_microvolts; if (vref_uv < 0) { printf("can't get vref-supply value: %d\n", vref_uv); return vref_uv; diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 6cca561f974..4fbb63a148a 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -78,14 +78,16 @@ config MTK_AHCI Enable this driver to support Sata devices through Mediatek AHCI controller (e.g. MT7622). -config AHCI_MVEBU - bool "Marvell EBU AHCI SATA support" - depends on ARCH_MVEBU || ARCH_OCTEON +config AHCI_GENERIC + bool "Generic AHCI SATA support" + depends on OF_CONTROL select SCSI_AHCI select SCSI help - This option enables support for the Marvell EBU SoC's - onboard AHCI SATA. + This option enables support for generic onboard AHCI SATA controller + that do not need platform specific quirks, like emulated devices, + Marvell EBU SoC's onboard AHCI SATA controllers or Cavium's Octeon + 7130 AHCI controllers. If unsure, say N. diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index ee10c4445b0..69fa9b707e0 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -14,6 +14,6 @@ obj-$(CONFIG_SATA) += sata.o sata_bootdev.o obj-$(CONFIG_SATA_CEVA) += sata_ceva.o obj-$(CONFIG_SATA_MV) += sata_mv.o obj-$(CONFIG_SATA_SIL) += sata_sil.o -obj-$(CONFIG_AHCI_MVEBU) += ahci_mvebu.o +obj-$(CONFIG_AHCI_GENERIC) += ahci_generic.o obj-$(CONFIG_SUNXI_AHCI) += ahci_sunxi.o obj-$(CONFIG_MTK_AHCI) += mtk_ahci.o diff --git a/drivers/ata/ahci_mvebu.c b/drivers/ata/ahci_generic.c index f6e2d6bee45..6e5a6cbafd8 100644 --- a/drivers/ata/ahci_mvebu.c +++ b/drivers/ata/ahci_generic.c @@ -16,7 +16,7 @@ __weak int board_ahci_enable(void) return 0; } -static int mvebu_ahci_bind(struct udevice *dev) +static int generic_ahci_bind(struct udevice *dev) { struct udevice *scsi_dev; int ret; @@ -30,7 +30,7 @@ static int mvebu_ahci_bind(struct udevice *dev) return 0; } -static int mvebu_ahci_probe(struct udevice *dev) +static int generic_ahci_probe(struct udevice *dev) { /* * Board specific SATA / AHCI enable code, e.g. enable the @@ -43,18 +43,19 @@ static int mvebu_ahci_probe(struct udevice *dev) return 0; } -static const struct udevice_id mvebu_ahci_ids[] = { +static const struct udevice_id generic_ahci_ids[] = { { .compatible = "marvell,armada-380-ahci" }, { .compatible = "marvell,armada-3700-ahci" }, { .compatible = "marvell,armada-8k-ahci" }, { .compatible = "cavium,octeon-7130-ahci" }, + { .compatible = "generic-ahci" }, { } }; -U_BOOT_DRIVER(ahci_mvebu_drv) = { - .name = "ahci_mvebu", +U_BOOT_DRIVER(ahci_generic_drv) = { + .name = "ahci_generic", .id = UCLASS_AHCI, - .of_match = mvebu_ahci_ids, - .bind = mvebu_ahci_bind, - .probe = mvebu_ahci_probe, + .of_match = generic_ahci_ids, + .bind = generic_ahci_bind, + .probe = generic_ahci_probe, }; diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c index 312e038445c..f3ac8db9464 100644 --- a/drivers/block/blk-uclass.c +++ b/drivers/block/blk-uclass.c @@ -695,9 +695,22 @@ static int blk_claim_devnum(enum uclass_id uclass_id, int devnum) return -ENOENT; } -int blk_create_device(struct udevice *parent, const char *drv_name, - const char *name, int uclass_id, int devnum, int blksz, - lbaint_t lba, struct udevice **devp) +/** + * blk_create_device() - Create a new block device + * + * @parent: Parent of the new device + * @drv_name: Driver name to use for the block device + * @name: Name for the device + * @uclass_id: Interface type (enum uclass_id_t) + * @devnum: Device number, specific to the interface type, or -1 to + * allocate the next available number + * @blksz: Block size of the device in bytes (typically 512) + * @lba: Total number of blocks of the device + * @devp: the new device (which has not been probed) + */ +static int blk_create_device(struct udevice *parent, const char *drv_name, + const char *name, int uclass_id, int devnum, + int blksz, lbaint_t lba, struct udevice **devp) { struct blk_desc *desc; struct udevice *dev; diff --git a/drivers/block/rkmtd.c b/drivers/block/rkmtd.c index c55f052e51b..f84cacd7ead 100644 --- a/drivers/block/rkmtd.c +++ b/drivers/block/rkmtd.c @@ -794,36 +794,19 @@ int rkmtd_init_plat(struct udevice *dev) return 0; } -static void rkmtd_blk_kmalloc_release(struct udevice *dev, void *res) -{ - /* noop */ -} - static int rkmtd_bind(struct udevice *dev) { struct rkmtd_dev *plat = dev_get_plat(dev); - char dev_name[30], *str; struct blk_desc *desc; struct udevice *bdev; int ret; - snprintf(dev_name, sizeof(dev_name), "%s.%s", dev->name, "blk"); - - str = devres_alloc(rkmtd_blk_kmalloc_release, strlen(dev_name) + 1, GFP_KERNEL); - if (unlikely(!str)) - return -ENOMEM; - - strcpy(str, dev_name); - - ret = blk_create_device(dev, "rkmtd_blk", str, UCLASS_RKMTD, - -1, 512, LBA, &bdev); + ret = blk_create_devicef(dev, "rkmtd_blk", "blk", UCLASS_RKMTD, + -1, 512, LBA, &bdev); if (ret) { - free(str); return log_msg_ret("blk", ret); } - devres_add(dev, str); - desc = dev_get_uclass_plat(bdev); sprintf(desc->vendor, "0x%.4x", 0x2207); memcpy(desc->product, "RKMTD", sizeof("RKMTD")); diff --git a/drivers/clk/imx/clk-imxrt1050.c b/drivers/clk/imx/clk-imxrt1050.c index 788e0650a92..2c029ec5a6e 100644 --- a/drivers/clk/imx/clk-imxrt1050.c +++ b/drivers/clk/imx/clk-imxrt1050.c @@ -144,6 +144,9 @@ static int imxrt1050_clk_probe(struct udevice *dev) clk_dm(IMXRT1050_CLK_AHB_PODF, imx_clk_divider("ahb_podf", "periph_sel", base + 0x14, 10, 3)); + clk_dm(IMXRT1050_CLK_IPG_PDOF, + imx_clk_divider("ipg_podf", "ahb_podf", + base + 0x14, 8, 2)); clk_dm(IMXRT1050_CLK_USDHC1_PODF, imx_clk_divider("usdhc1_podf", "usdhc1_sel", base + 0x24, 11, 3)); diff --git a/drivers/clk/rockchip/clk_px30.c b/drivers/clk/rockchip/clk_px30.c index 22ede1c38a8..ad7e1c0f246 100644 --- a/drivers/clk/rockchip/clk_px30.c +++ b/drivers/clk/rockchip/clk_px30.c @@ -1588,6 +1588,105 @@ static ulong px30_pmuclk_set_gpll_rate(struct px30_pmuclk_priv *priv, ulong hz) return priv->gpll_hz; } +static ulong px30_pmu_uart0_get_clk(struct px30_pmuclk_priv *priv) +{ + struct px30_pmucru *pmucru = priv->pmucru; + u32 clk_div_con; + u32 clk_pll_sel; + ulong pll_rate; + u32 clk_sel; + ulong clk; + u32 con; + + con = readl(&pmucru->pmu_clksel_con[3]); + clk_div_con = bitfield_extract_by_mask(con, UART0_DIV_CON_MASK); + clk_pll_sel = bitfield_extract_by_mask(con, UART0_PLL_SEL_MASK); + + switch (clk_pll_sel) { + case UART0_PLL_SEL_GPLL: + pll_rate = px30_pmuclk_get_gpll_rate(priv); + break; + case UART0_PLL_SEL_24M: + pll_rate = OSC_HZ; + break; + case UART0_PLL_SEL_480M: + case UART0_PLL_SEL_NPLL: + /* usbphy480M and NPLL clocks, generated by CRU, are not supported yet */ + default: + return -ENOENT; + } + + clk = DIV_TO_RATE(pll_rate, clk_div_con); + con = readl(&pmucru->pmu_clksel_con[4]); + clk_sel = bitfield_extract_by_mask(con, UART0_CLK_SEL_MASK); + + switch (clk_sel) { + case UART0_CLK_SEL_UART0: + return clk; + case UART0_CLK_SEL_UART0_NP5:{ + u32 clk_divnp5_div_con; + + clk_divnp5_div_con = + bitfield_extract_by_mask(con, UART0_DIVNP5_MASK); + return 2 * (u64) clk / (2 * clk_divnp5_div_con + 3); + } + case UART0_CLK_SEL_UART0_FRAC:{ + u32 fracdiv, n, m; + + fracdiv = readl(&pmucru->pmu_clksel_con[5]); + n = bitfield_extract_by_mask(fracdiv, + CLK_UART_FRAC_NUMERATOR_MASK); + m = bitfield_extract_by_mask(fracdiv, + CLK_UART_FRAC_DENOMINATOR_MASK); + return (u64) clk * n / m; + } + default: + return -ENOENT; + } +} + +static ulong px30_pmu_uart0_set_clk(struct px30_pmuclk_priv *priv, ulong rate) +{ + struct px30_pmucru *pmucru = priv->pmucru; + ulong m = 0, n = 0; + ulong gpll_rate; + u32 clk_div_con; + u32 clk_pll_sel; + u32 clk_sel; + + gpll_rate = px30_pmuclk_get_gpll_rate(priv); + if (gpll_rate % rate == 0) { + clk_pll_sel = UART0_PLL_SEL_GPLL; + clk_sel = UART0_CLK_SEL_UART0; + clk_div_con = DIV_ROUND_UP(priv->gpll_hz, rate); + } else if (rate == OSC_HZ) { + clk_pll_sel = UART0_PLL_SEL_24M; + clk_sel = UART0_CLK_SEL_UART0; + clk_div_con = 1; + } else { + clk_pll_sel = UART0_PLL_SEL_GPLL; + clk_sel = UART0_CLK_SEL_UART0_FRAC; + clk_div_con = 1; + rational_best_approximation(rate, priv->gpll_hz, + GENMASK(16 - 1, 0), + GENMASK(16 - 1, 0), &m, &n); + } + + rk_clrsetreg(&pmucru->pmu_clksel_con[3], + UART0_PLL_SEL_MASK | UART0_DIV_CON_MASK, + clk_pll_sel << UART0_PLL_SEL_SHIFT | (clk_div_con - 1)); + rk_clrsetreg(&pmucru->pmu_clksel_con[4], UART0_CLK_SEL_MASK, + clk_sel << UART0_CLK_SEL_SHIFT); + if (m && n) { + u32 fracdiv; + + fracdiv = m << CLK_UART_FRAC_NUMERATOR_SHIFT | n; + writel(fracdiv, &pmucru->pmu_clksel_con[5]); + } + + return px30_pmu_uart0_get_clk(priv); +} + static ulong px30_pmuclk_get_rate(struct clk *clk) { struct px30_pmuclk_priv *priv = dev_get_priv(clk->dev); @@ -1601,6 +1700,9 @@ static ulong px30_pmuclk_get_rate(struct clk *clk) case PCLK_PMU_PRE: rate = px30_pclk_pmu_get_pmuclk(priv); break; + case SCLK_UART0_PMU: + rate = px30_pmu_uart0_get_clk(priv); + break; default: return -ENOENT; } @@ -1621,6 +1723,9 @@ static ulong px30_pmuclk_set_rate(struct clk *clk, ulong rate) case PCLK_PMU_PRE: ret = px30_pclk_pmu_set_pmuclk(priv, rate); break; + case SCLK_UART0_PMU: + ret = px30_pmu_uart0_set_clk(priv, rate); + break; default: return -ENOENT; } diff --git a/drivers/core/acpi.c b/drivers/core/acpi.c index 9f784228921..4763963914b 100644 --- a/drivers/core/acpi.c +++ b/drivers/core/acpi.c @@ -48,6 +48,7 @@ enum method_t { METHOD_FILL_SSDT, METHOD_INJECT_DSDT, METHOD_SETUP_NHLT, + METHOD_FILL_MADT, }; /* Prototype for all methods */ @@ -282,6 +283,8 @@ acpi_method acpi_get_method(struct udevice *dev, enum method_t method) switch (method) { case METHOD_WRITE_TABLES: return aops->write_tables; + case METHOD_FILL_MADT: + return aops->fill_madt; case METHOD_FILL_SSDT: return aops->fill_ssdt; case METHOD_INJECT_DSDT: @@ -328,6 +331,19 @@ int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent, return 0; } +int acpi_fill_madt_subtbl(struct acpi_ctx *ctx) +{ + int ret; + + log_debug("Writing MADT table\n"); + ret = acpi_recurse_method(ctx, dm_root(), METHOD_FILL_MADT, TYPE_NONE); + log_debug("Writing MADT finished, err=%d\n", ret); + if (ret) + return log_msg_ret("build", ret); + + return ret; +} + int acpi_fill_ssdt(struct acpi_ctx *ctx) { void *start = ctx->current; diff --git a/drivers/cpu/Kconfig b/drivers/cpu/Kconfig index 5c06cd9f60e..4cc3679c009 100644 --- a/drivers/cpu/Kconfig +++ b/drivers/cpu/Kconfig @@ -26,6 +26,13 @@ config CPU_RISCV help Support CPU cores for RISC-V architecture. +config CPU_ARMV8 + bool "Enable generic ARMv8 CPU driver" + depends on CPU && ARM64 + select IRQ + help + Support CPU cores for armv8 architecture. + config CPU_MICROBLAZE bool "Enable Microblaze CPU driver" depends on CPU && MICROBLAZE diff --git a/drivers/cpu/Makefile b/drivers/cpu/Makefile index bc75d9b974e..eaf494706e2 100644 --- a/drivers/cpu/Makefile +++ b/drivers/cpu/Makefile @@ -6,10 +6,12 @@ obj-$(CONFIG_CPU) += cpu-uclass.o +obj-$(CONFIG_ARCH_BCM283X) += bcm283x_cpu.o obj-$(CONFIG_ARCH_BMIPS) += bmips_cpu.o obj-$(CONFIG_ARCH_IMX8) += imx8_cpu.o obj-$(CONFIG_ARCH_AT91) += at91_cpu.o obj-$(CONFIG_ARCH_MEDIATEK) += mtk_cpu.o +obj-$(CONFIG_CPU_ARMV8) += armv8_cpu.o obj-$(CONFIG_CPU_IMX) += imx8_cpu.o obj-$(CONFIG_CPU_MPC83XX) += mpc83xx_cpu.o obj-$(CONFIG_CPU_RISCV) += riscv_cpu.o diff --git a/drivers/cpu/armv8_cpu.c b/drivers/cpu/armv8_cpu.c new file mode 100644 index 00000000000..4eedfe5e2c5 --- /dev/null +++ b/drivers/cpu/armv8_cpu.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 9elements GmbH + */ +#include <cpu.h> +#include <dm.h> +#include <irq.h> +#include <acpi/acpigen.h> +#include <asm/armv8/cpu.h> +#include <asm/io.h> +#include <dm/acpi.h> +#include <linux/bitops.h> +#include <linux/printk.h> +#include <linux/sizes.h> + +static int armv8_cpu_get_desc(const struct udevice *dev, char *buf, int size) +{ + int cpuid; + + cpuid = (read_midr() & MIDR_PARTNUM_MASK) >> MIDR_PARTNUM_SHIFT; + + snprintf(buf, size, "CPU MIDR %04x", cpuid); + + return 0; +} + +static int armv8_cpu_get_info(const struct udevice *dev, + struct cpu_info *info) +{ + info->cpu_freq = 0; + info->features = BIT(CPU_FEAT_L1_CACHE) | BIT(CPU_FEAT_MMU); + + return 0; +} + +static int armv8_cpu_get_count(const struct udevice *dev) +{ + return uclass_id_count(UCLASS_CPU); +} + +#ifdef CONFIG_ACPIGEN +int armv8_cpu_fill_ssdt(const struct udevice *dev, struct acpi_ctx *ctx) +{ + uint core_id = dev_seq(dev); + + acpigen_write_processor_device(ctx, core_id); + + return 0; +} + +int armv8_cpu_fill_madt(const struct udevice *dev, struct acpi_ctx *ctx) +{ + struct acpi_madt_gicc *gicc; + struct cpu_plat *cpu_plat; + struct udevice *gic; + u64 gicc_gicv = 0; + u64 gicc_gich = 0; + u64 gicc_gicr_base = 0; + u64 gicc_phys_base = 0; + u32 gicc_perf_gsiv = 0; + u64 gicc_mpidr; + u32 gicc_vgic_maint_irq = 0; + int addr_index; + fdt_addr_t addr; + int ret; + struct irq req_irq; + + cpu_plat = dev_get_parent_plat(dev); + if (!cpu_plat) + return 0; + + ret = irq_get_interrupt_parent(dev, &gic); + if (ret) { + log_err("%s: Failed to find interrupt parent for %s\n", + __func__, dev->name); + return -ENODEV; + } + + addr_index = 1; + + if (device_is_compatible(gic, "arm,gic-v3")) { + addr = dev_read_addr_index(gic, addr_index++); + if (addr != FDT_ADDR_T_NONE) + gicc_gicr_base = addr; + } + + addr = dev_read_addr_index(gic, addr_index++); + if (addr != FDT_ADDR_T_NONE) + gicc_phys_base = addr; + + addr = dev_read_addr_index(gic, addr_index++); + if (addr != FDT_ADDR_T_NONE) + gicc_gich = addr; + + addr = dev_read_addr_index(gic, addr_index++); + if (addr != FDT_ADDR_T_NONE) + gicc_gicv = addr; + + ret = irq_get_by_index(gic, 0, &req_irq); + if (!ret) + gicc_vgic_maint_irq = req_irq.id; + + gicc_mpidr = dev_read_u64_default(dev, "reg", 0); + if (!gicc_mpidr) + gicc_mpidr = dev_read_u32_default(dev, "reg", 0); + + /* + * gicc_vgic_maint_irq and gicc_gicv are the same for every CPU + */ + gicc = ctx->current; + acpi_write_madt_gicc(gicc, + dev_seq(dev), + gicc_perf_gsiv, /* FIXME: needs a PMU driver */ + gicc_phys_base, + gicc_gicv, + gicc_gich, + gicc_vgic_maint_irq, + gicc_gicr_base, + gicc_mpidr, + 0); /* FIXME: Not defined in DT */ + + acpi_inc(ctx, gicc->length); + + return 0; +} + +struct acpi_ops armv8_cpu_acpi_ops = { + .fill_ssdt = armv8_cpu_fill_ssdt, + .fill_madt = armv8_cpu_fill_madt, +}; +#endif + +static const struct cpu_ops cpu_ops = { + .get_count = armv8_cpu_get_count, + .get_desc = armv8_cpu_get_desc, + .get_info = armv8_cpu_get_info, +}; + +static const struct udevice_id cpu_ids[] = { + { .compatible = "arm,armv8" }, + {} +}; + +U_BOOT_DRIVER(arm_cpu) = { + .name = "arm-cpu", + .id = UCLASS_CPU, + .of_match = cpu_ids, + .ops = &cpu_ops, + .flags = DM_FLAG_PRE_RELOC, + ACPI_OPS_PTR(&armv8_cpu_acpi_ops) +}; diff --git a/drivers/cpu/armv8_cpu.h b/drivers/cpu/armv8_cpu.h new file mode 100644 index 00000000000..48c705e98de --- /dev/null +++ b/drivers/cpu/armv8_cpu.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 9elements GmbH + */ +#include <dm/acpi.h> +#include <dm/device.h> + +#ifndef _ARMV8_CPU_H_ +#define _ARMV8_CPU_H_ + +/** + * armv8_cpu_fill_ssdt() - Fill the SSDT + * Parses the FDT and writes the SSDT nodes. + * + * @dev: cpu device to generate ACPI tables for + * @ctx: ACPI context pointer + * @return: 0 if OK, or a negative error code. + */ +int armv8_cpu_fill_ssdt(const struct udevice *dev, struct acpi_ctx *ctx); + +/** + * armv8_cpu_fill_madt() - Fill the MADT + * Parses the FDT and writes the MADT subtables. + * + * @dev: cpu device to generate ACPI tables for + * @ctx: ACPI context pointer + * @return: 0 if OK, or a negative error code. + */ +int armv8_cpu_fill_madt(const struct udevice *dev, struct acpi_ctx *ctx); + +#endif
\ No newline at end of file diff --git a/drivers/cpu/bcm283x_cpu.c b/drivers/cpu/bcm283x_cpu.c new file mode 100644 index 00000000000..59a7b142c95 --- /dev/null +++ b/drivers/cpu/bcm283x_cpu.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 9elements GmbH + */ + +#include <cpu.h> +#include <cpu_func.h> +#include <dm.h> +#include <fdt_support.h> +#include <acpi/acpigen.h> +#include <asm/armv8/cpu.h> +#include <asm/cache.h> +#include <asm/io.h> +#include <asm/global_data.h> +#include <asm/system.h> +#include <asm-generic/sections.h> +#include <linux/bitops.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include "armv8_cpu.h" + +DECLARE_GLOBAL_DATA_PTR; + +struct bcm_plat { + u64 release_addr; +}; + +static int cpu_bcm_get_desc(const struct udevice *dev, char *buf, int size) +{ + struct cpu_plat *plat = dev_get_parent_plat(dev); + const char *name; + + if (size < 32) + return -ENOSPC; + + if (device_is_compatible(dev, "arm,cortex-a53")) + name = "A53"; + else if (device_is_compatible(dev, "arm,cortex-a72")) + name = "A72"; + else + name = "?"; + + snprintf(buf, size, "Broadcom Cortex-%s at %u MHz\n", + name, plat->timebase_freq); + + return 0; +} + +static int cpu_bcm_get_info(const struct udevice *dev, struct cpu_info *info) +{ + struct cpu_plat *plat = dev_get_parent_plat(dev); + + info->cpu_freq = plat->timebase_freq * 1000; + info->features = BIT(CPU_FEAT_L1_CACHE) | BIT(CPU_FEAT_MMU); + + return 0; +} + +static int cpu_bcm_get_count(const struct udevice *dev) +{ + return uclass_id_count(UCLASS_CPU); +} + +static int cpu_bcm_get_vendor(const struct udevice *dev, char *buf, int size) +{ + snprintf(buf, size, "Broadcom"); + + return 0; +} + +static int cpu_bcm_is_current(struct udevice *dev) +{ + struct cpu_plat *plat = dev_get_parent_plat(dev); + + if (plat->cpu_id == (read_mpidr() & 0xffff)) + return 1; + + return 0; +} + +/** + * bcm_cpu_on - Releases the secondary CPU from it's spintable + * + * Write the CPU's spintable mailbox and let the CPU enter U-Boot. + * + * @dev: Device to start + * @return: zero on success or error code on failure. + */ +static int bcm_cpu_on(struct udevice *dev) +{ + struct bcm_plat *plat = dev_get_plat(dev); + ulong *start_address; + + if (plat->release_addr == ~0ULL) + return -ENODATA; + + start_address = map_physmem(plat->release_addr, sizeof(uintptr_t), MAP_NOCACHE); + + /* Point secondary CPU to U-Boot entry */ + *start_address = (uintptr_t)_start; + + /* Make sure the other CPUs see the written start address */ + if (!CONFIG_IS_ENABLED(SYS_DCACHE_OFF)) + flush_dcache_all(); + + /* Send an event to wake up the secondary CPU. */ + asm("dsb ishst\n" + "sev"); + + unmap_physmem(start_address, MAP_NOCACHE); + + return 0; +} + +static const struct cpu_ops cpu_bcm_ops = { + .get_desc = cpu_bcm_get_desc, + .get_info = cpu_bcm_get_info, + .get_count = cpu_bcm_get_count, + .get_vendor = cpu_bcm_get_vendor, + .is_current = cpu_bcm_is_current, +}; + +static const struct udevice_id cpu_bcm_ids[] = { + { .compatible = "arm,cortex-a53" }, /* RPi 3 */ + { .compatible = "arm,cortex-a72" }, /* RPi 4 */ + { } +}; + +static int bcm_cpu_bind(struct udevice *dev) +{ + struct cpu_plat *plat = dev_get_parent_plat(dev); + + plat->cpu_id = dev_read_addr(dev); + + return 0; +} + +/** + * bcm_cpu_of_to_plat - Gather spin-table release address + * + * Read the spin-table release address to allow all seconary CPUs to enter + * U-Boot when necessary. + * + * @dev: Device to start + */ +static int bcm_cpu_of_to_plat(struct udevice *dev) +{ + struct bcm_plat *plat = dev_get_plat(dev); + const char *prop; + + if (CONFIG_IS_ENABLED(ARMV8_MULTIENTRY)) { + plat->release_addr = ~0ULL; + + prop = dev_read_string(dev, "enable-method"); + if (!prop || strcmp(prop, "spin-table")) + return -ENODEV; + + plat->release_addr = dev_read_u64_default(dev, "cpu-release-addr", ~0ULL); + + if (plat->release_addr == ~0ULL) + return -ENODEV; + } + + return 0; +} + +static int bcm_cpu_probe(struct udevice *dev) +{ + struct cpu_plat *plat = dev_get_parent_plat(dev); + struct clk clk; + int ret; + + /* Get a clock if it exists */ + ret = clk_get_by_index(dev, 0, &clk); + if (!ret) { + ret = clk_enable(&clk); + if (ret && (ret != -ENOSYS || ret != -EOPNOTSUPP)) + return ret; + ret = clk_get_rate(&clk); + if (IS_ERR_VALUE(ret)) + return ret; + plat->timebase_freq = ret; + } + + /* + * The armstub holds the secondary CPUs in a spinloop. When + * ARMV8_MULTIENTRY is enabled release the secondary CPUs and + * let them enter U-Boot as well. + */ + if (CONFIG_IS_ENABLED(ARMV8_MULTIENTRY)) { + ret = bcm_cpu_on(dev); + if (ret) + return ret; + } + + return ret; +} + +struct acpi_ops bcm283x_cpu_acpi_ops = { + .fill_ssdt = armv8_cpu_fill_ssdt, + .fill_madt = armv8_cpu_fill_madt, +}; + +U_BOOT_DRIVER(cpu_bcm_drv) = { + .name = "bcm283x_cpu", + .id = UCLASS_CPU, + .of_match = cpu_bcm_ids, + .ops = &cpu_bcm_ops, + .probe = bcm_cpu_probe, + .bind = bcm_cpu_bind, + .of_to_plat = bcm_cpu_of_to_plat, + .plat_auto = sizeof(struct bcm_plat), + ACPI_OPS_PTR(&bcm283x_cpu_acpi_ops) +}; diff --git a/drivers/ddr/altera/sdram_n5x.c b/drivers/ddr/altera/sdram_n5x.c index db09986f64b..d1fc93b6bdd 100644 --- a/drivers/ddr/altera/sdram_n5x.c +++ b/drivers/ddr/altera/sdram_n5x.c @@ -22,6 +22,7 @@ #include <asm/io.h> #include <linux/err.h> #include <linux/sizes.h> +#include <u-boot/schedule.h> DECLARE_GLOBAL_DATA_PTR; diff --git a/drivers/ddr/altera/sdram_soc64.c b/drivers/ddr/altera/sdram_soc64.c index 9e57c2ecfa4..10a8e64af3d 100644 --- a/drivers/ddr/altera/sdram_soc64.c +++ b/drivers/ddr/altera/sdram_soc64.c @@ -24,6 +24,7 @@ #include <asm/io.h> #include <dm/device_compat.h> #include <linux/sizes.h> +#include <u-boot/schedule.h> #define PGTABLE_OFF 0x4000 diff --git a/drivers/ddr/imx/imx9/Kconfig b/drivers/ddr/imx/imx9/Kconfig index b1795eec353..0a45340ffb6 100644 --- a/drivers/ddr/imx/imx9/Kconfig +++ b/drivers/ddr/imx/imx9/Kconfig @@ -17,6 +17,11 @@ config IMX9_DRAM_PM_COUNTER help Enable DDR controller performance monitor counter for reference events. +config IMX9_DRAM_INLINE_ECC + bool "Enable DDR INLINE ECC feature" + help + Select to enable DDR INLINE ECC feature + config SAVED_DRAM_TIMING_BASE hex "Define the base address for saved dram timing" help diff --git a/drivers/dfu/dfu_sf.c b/drivers/dfu/dfu_sf.c index 7c1c0f9e2dc..8f7296adec6 100644 --- a/drivers/dfu/dfu_sf.c +++ b/drivers/dfu/dfu_sf.c @@ -123,6 +123,7 @@ static struct spi_flash *parse_dev(char *devstr) unsigned int mode = CONFIG_SF_DEFAULT_MODE; char *s, *endp; struct spi_flash *dev; + bool use_dt = true; s = strsep(&devstr, ":"); if (!s || !*s || (bus = simple_strtoul(s, &endp, 0), *endp)) { @@ -143,6 +144,8 @@ static struct spi_flash *parse_dev(char *devstr) printf("Invalid SPI speed %s\n", s); return NULL; } + if (IS_ENABLED(CONFIG_DM_SPI_FLASH)) + use_dt = false; } s = strsep(&devstr, ":"); @@ -152,9 +155,20 @@ static struct spi_flash *parse_dev(char *devstr) printf("Invalid SPI mode %s\n", s); return NULL; } + if (IS_ENABLED(CONFIG_DM_SPI_FLASH)) + use_dt = false; } - dev = spi_flash_probe(bus, cs, speed, mode); + if (IS_ENABLED(CONFIG_DM_SPI_FLASH) && use_dt) { + struct udevice *new; + + if (!spi_flash_probe_bus_cs(bus, cs, &new)) + dev = dev_get_uclass_priv(new); + else + dev = NULL; + } else { + dev = spi_flash_probe(bus, cs, speed, mode); + } if (!dev) { printf("Failed to create SPI flash at %u:%u:%u:%u\n", bus, cs, speed, mode); diff --git a/drivers/gpio/dwapb_gpio.c b/drivers/gpio/dwapb_gpio.c index 7a6eae9ba18..04639a4cb68 100644 --- a/drivers/gpio/dwapb_gpio.c +++ b/drivers/gpio/dwapb_gpio.c @@ -177,7 +177,9 @@ static int gpio_dwapb_bind(struct udevice *dev) plat->base = (void *)base; plat->bank = bank; - plat->pins = ofnode_read_u32_default(node, "snps,nr-gpios", 0); + + if (ofnode_read_u32(node, "ngpios", &plat->pins)) + plat->pins = ofnode_read_u32_default(node, "snps,nr-gpios", 0); if (ofnode_read_string_index(node, "bank-name", 0, &plat->name)) { diff --git a/drivers/gpio/mcp230xx_gpio.c b/drivers/gpio/mcp230xx_gpio.c index 42e7fe9d474..b6f533b1306 100644 --- a/drivers/gpio/mcp230xx_gpio.c +++ b/drivers/gpio/mcp230xx_gpio.c @@ -14,6 +14,7 @@ #include <asm/gpio.h> #include <dm/device_compat.h> #include <dt-bindings/gpio/gpio.h> +#include <linux/delay.h> enum mcp230xx_type { UNKNOWN = 0, diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index 2f3cb5908c9..2dfc1c4eab5 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -29,7 +29,6 @@ #include <watchdog.h> #include <dm.h> #include <dm/pinctrl.h> -#include <fdtdec.h> DECLARE_GLOBAL_DATA_PTR; @@ -867,8 +866,7 @@ static int mxc_i2c_set_bus_speed(struct udevice *bus, unsigned int speed) static int mxc_i2c_probe(struct udevice *bus) { struct mxc_i2c_bus *i2c_bus = dev_get_priv(bus); - const void *fdt = gd->fdt_blob; - int node = dev_of_offset(bus); + ofnode node = dev_ofnode(bus); fdt_addr_t addr; int ret, ret2; @@ -912,17 +910,15 @@ static int mxc_i2c_probe(struct udevice *bus) * See Documentation/devicetree/bindings/i2c/i2c-imx.txt * Use gpio to force bus idle when necessary. */ - ret = fdt_stringlist_search(fdt, node, "pinctrl-names", "gpio"); + ret = ofnode_stringlist_search(node, "pinctrl-names", "gpio"); if (ret < 0) { debug("i2c bus %d at 0x%2lx, no gpio pinctrl state.\n", dev_seq(bus), i2c_bus->base); } else { - ret = gpio_request_by_name_nodev(offset_to_ofnode(node), - "scl-gpios", 0, &i2c_bus->scl_gpio, - GPIOD_IS_OUT); - ret2 = gpio_request_by_name_nodev(offset_to_ofnode(node), - "sda-gpios", 0, &i2c_bus->sda_gpio, - GPIOD_IS_OUT); + ret = gpio_request_by_name(bus, "scl-gpios", 0, &i2c_bus->scl_gpio, + GPIOD_IS_OUT); + ret2 = gpio_request_by_name(bus, "sda-gpios", 0, &i2c_bus->sda_gpio, + GPIOD_IS_OUT); if (!dm_gpio_is_valid(&i2c_bus->sda_gpio) || !dm_gpio_is_valid(&i2c_bus->scl_gpio) || ret || ret2) { diff --git a/drivers/i2c/rz_riic.c b/drivers/i2c/rz_riic.c index 5f3f8d1b24b..f292c824362 100644 --- a/drivers/i2c/rz_riic.c +++ b/drivers/i2c/rz_riic.c @@ -14,6 +14,7 @@ #include <linux/bitops.h> #include <linux/delay.h> #include <reset.h> +#include <u-boot/schedule.h> #include <wait_bit.h> #define RIIC_ICCR1 0x00 diff --git a/drivers/led/led_sw_blink.c b/drivers/led/led_sw_blink.c index 06a43db340c..ee1546d02d4 100644 --- a/drivers/led/led_sw_blink.c +++ b/drivers/led/led_sw_blink.c @@ -5,6 +5,7 @@ * Author: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu> */ +#include <cyclic.h> #include <dm.h> #include <led.h> #include <time.h> diff --git a/drivers/misc/irq-uclass.c b/drivers/misc/irq-uclass.c index 79eb7c200dc..ac778593c96 100644 --- a/drivers/misc/irq-uclass.c +++ b/drivers/misc/irq-uclass.c @@ -62,6 +62,40 @@ int irq_read_and_clear(struct irq *irq) return ops->read_and_clear(irq); } +int irq_get_interrupt_parent(const struct udevice *dev, + struct udevice **interrupt_parent) +{ + struct ofnode_phandle_args phandle_args; + struct udevice *irq = NULL; + ofnode node; + int ret; + + if (!dev || !interrupt_parent) + return -EINVAL; + + *interrupt_parent = NULL; + + node = dev_ofnode(dev); + if (!ofnode_valid(node)) + return -EINVAL; + + while (ofnode_valid(node)) { + ret = ofnode_parse_phandle_with_args(node, "interrupt-parent", + NULL, 0, 0, &phandle_args); + if (!ret && !device_get_global_by_ofnode(phandle_args.node, &irq)) + break; + node = ofnode_get_parent(node); + } + + if (!irq) { + log_err("Cannot find an interrupt parent for device %s\n", dev->name); + return -ENODEV; + } + *interrupt_parent = irq; + + return 0; +} + #if CONFIG_IS_ENABLED(OF_PLATDATA) int irq_get_by_phandle(struct udevice *dev, const struct phandle_2_arg *cells, struct irq *irq) @@ -142,10 +176,40 @@ err: int irq_get_by_index(struct udevice *dev, int index, struct irq *irq) { struct ofnode_phandle_args args; - int ret; + struct udevice *interrupt_parent; + int ret, size, i; + const __be32 *list; + u32 count; ret = dev_read_phandle_with_args(dev, "interrupts-extended", "#interrupt-cells", 0, index, &args); + if (ret) { + list = dev_read_prop(dev, "interrupts", &size); + if (!list) + return -ENOENT; + + ret = irq_get_interrupt_parent(dev, &interrupt_parent); + if (ret) + return -ENODEV; + args.node = dev_ofnode(interrupt_parent); + + if (dev_read_u32(interrupt_parent, "#interrupt-cells", &count)) { + log_err("%s: could not get #interrupt-cells for %s\n", + __func__, dev->name); + return -ENOENT; + } + + if (index * count >= size / sizeof(*list)) + return -ENOENT; + if (count > OF_MAX_PHANDLE_ARGS) + count = OF_MAX_PHANDLE_ARGS; + args.args_count = count; + for (i = 0; i < count; i++) + args.args[i] = be32_to_cpup(&list[index * count + i]); + + return irq_get_by_index_tail(ret, dev_ofnode(dev), &args, + "interrupts", index, irq); + } return irq_get_by_index_tail(ret, dev_ofnode(dev), &args, "interrupts-extended", index > 0, irq); diff --git a/drivers/mmc/fsl_esdhc_imx.c b/drivers/mmc/fsl_esdhc_imx.c index fb410104c1f..d7a45ef0ad0 100644 --- a/drivers/mmc/fsl_esdhc_imx.c +++ b/drivers/mmc/fsl_esdhc_imx.c @@ -1398,8 +1398,7 @@ static int fsl_esdhc_of_to_plat(struct udevice *dev) struct udevice *vqmmc_dev; int ret; - const void *fdt = gd->fdt_blob; - int node = dev_of_offset(dev); + ofnode node = dev_ofnode(dev); fdt_addr_t addr; unsigned int val; @@ -1413,15 +1412,15 @@ static int fsl_esdhc_of_to_plat(struct udevice *dev) priv->dev = dev; priv->mode = -1; - val = fdtdec_get_int(fdt, node, "fsl,tuning-step", 1); + val = ofnode_read_u32_default(node, "fsl,tuning-step", 1); priv->tuning_step = val; - val = fdtdec_get_int(fdt, node, "fsl,tuning-start-tap", - ESDHC_TUNING_START_TAP_DEFAULT); + val = ofnode_read_u32_default(node, "fsl,tuning-start-tap", + ESDHC_TUNING_START_TAP_DEFAULT); priv->tuning_start_tap = val; - val = fdtdec_get_int(fdt, node, "fsl,strobe-dll-delay-target", - ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_DEFAULT); + val = ofnode_read_u32_default(node, "fsl,strobe-dll-delay-target", + ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_DEFAULT); priv->strobe_dll_delay_target = val; - val = fdtdec_get_int(fdt, node, "fsl,signal-voltage-switch-extra-delay-ms", 0); + val = ofnode_read_u32_default(node, "fsl,signal-voltage-switch-extra-delay-ms", 0); priv->signal_voltage_switch_extra_delay_ms = val; if (dev_read_bool(dev, "broken-cd")) diff --git a/drivers/mtd/spi/Kconfig b/drivers/mtd/spi/Kconfig index 63b0fd899fd..ca60a425ba3 100644 --- a/drivers/mtd/spi/Kconfig +++ b/drivers/mtd/spi/Kconfig @@ -176,6 +176,11 @@ config SPI_FLASH_MACRONIX help Add support for various Macronix SPI flash chips (MX25Lxxx) +config SPI_FLASH_PUYA + bool "Puya Semiconductor SPI flash support" + help + Add support for various Puya Semiconductor SPI flash chips (P25xxx) + config SPI_FLASH_SILICONKAISER bool "Silicon Kaiser SPI flash support" help diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c index dfe92c3986e..91ae49c9484 100644 --- a/drivers/mtd/spi/spi-nor-ids.c +++ b/drivers/mtd/spi/spi-nor-ids.c @@ -294,6 +294,36 @@ const struct flash_info spi_nor_ids[] = { { INFO("mx25uw6445g", 0xc28137, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) }, { INFO("mx25uw6345g", 0xc28437, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) }, #endif +#ifdef CONFIG_SPI_FLASH_PUYA + /* Puya Semiconductor (Shanghai) Co., Ltd */ + { INFO + ("p25q05h", 0x856010, 0, 64 * 1024, 1, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO + ("p25q10h", 0x856011, 0, 64 * 1024, 2, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO + ("p25q20h", 0x856012, 0, 64 * 1024, 4, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO + ("p25q40h", 0x856013, 0, 64 * 1024, 8, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO + ("p25q80h", 0x856014, 0, 64 * 1024, 16, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO + ("p25q16h", 0x856015, 0, 64 * 1024, 32, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO + ("p25q32h", 0x856016, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO + ("p25q64h", 0x856017, 0, 64 * 1024, 128, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO + ("p25q128h", 0x856018, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, +#endif #ifdef CONFIG_SPI_FLASH_SILICONKAISER { INFO("sk25lp128", 0x257018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, @@ -328,6 +358,7 @@ const struct flash_info spi_nor_ids[] = { #ifdef CONFIG_SPI_FLASH_MT35XU { INFO("mt35xl512aba", 0x2c5a1a, 0, 128 * 1024, 512, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES | SPI_NOR_OCTAL_DTR_READ) }, { INFO("mt35xu512aba", 0x2c5b1a, 0, 128 * 1024, 512, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES | SPI_NOR_OCTAL_DTR_READ) }, + { INFO("mt35xu01gaba", 0x2c5b1b, 0, 128 * 1024, 1024, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) }, #endif /* CONFIG_SPI_FLASH_MT35XU */ { INFO6("mt35xu01g", 0x2c5b1b, 0x104100, 128 * 1024, 1024, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) }, { INFO("mt35xu02g", 0x2c5b1c, 0, 128 * 1024, 2048, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) }, diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 89f7411bdf3..576cd2d50ad 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -525,11 +525,11 @@ config KS8851_MLL The Microchip KS8851 parallel bus external ethernet interface chip. config KSZ9477 - bool "Microchip KSZ9477 I2C controller driver" - depends on DM_DSA && DM_I2C + bool "Microchip KSZ9477 controller driver" + depends on DM_DSA && (DM_I2C || DM_SPI) help This driver implements a DSA switch driver for the KSZ9477 family - of GbE switches using the I2C interface. + of GbE switches using the I2C or SPI interface. config LITEETH bool "LiteX LiteEth Ethernet MAC" @@ -762,6 +762,38 @@ config TULIP help This driver supports DEC DC2114x Fast ethernet chips. +config TULIP_SUPPORT_NON_PCI + bool "No PCI controller" + depends on TULIP + default n + help + Say Y to this and you can run this driver on platforms that do not + have PCI controllers. + +config TULIP_IGNORE_TX_NO_CARRIER + bool "Ignore tx no carrier error" + depends on TULIP + default n + help + Some IP cores of dc2114x or its variants do not comply so well with + the behaviors described by the official document. A packet could be + sent successfully but reported with No Carrier error. Latest drivers + of this IP core do not detect this error anymore. Say Y to this could + disable handling of this error. + +config TULIP_MULTIPLE_TX_DESC + bool "Use multiple tx descriptors" + depends on TULIP + default n + help + Some IP cores of dc2114x or its variants do not comply so well with + the behaviors described by the official document. Originally this + driver uses only one tx descriptor and organizes it as a ring buffer, + which would lead to a problem that one packet would be sent twice. + Say Y to this could prevent this bug if you are using IP cores with + this issue, by using multiple tx descriptors and organizing them as + a real well-defined ring buffer. + config XILINX_AXIEMAC select PHYLIB select MII diff --git a/drivers/net/dc2114x.c b/drivers/net/dc2114x.c index ce028f451f1..7c0665faa8e 100644 --- a/drivers/net/dc2114x.c +++ b/drivers/net/dc2114x.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ #include <asm/io.h> +#include <cpu_func.h> #include <dm.h> #include <malloc.h> #include <net.h> @@ -72,10 +73,20 @@ #define POLL_DEMAND 1 +#if CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI) +#define phys_to_bus(dev, a) virt_to_phys((volatile const void *)(a)) +#else #define phys_to_bus(dev, a) dm_pci_phys_to_mem((dev), (a)) +#endif + +/* Number of TX descriptors */ +#if CONFIG_IS_ENABLED(TULIP_MULTIPLE_TX_DESC) +#define NUM_TX_DESC 4 +#else +#define NUM_TX_DESC 1 +#endif #define NUM_RX_DESC PKTBUFSRX -#define NUM_TX_DESC 1 /* Number of TX descriptors */ #define RX_BUFF_SZ PKTSIZE_ALIGN #define TOUT_LOOP 1000000 @@ -89,9 +100,17 @@ struct de4x5_desc { u32 next; }; +/* Assigned for network card's ring buffer: + * Some CPU might treat these memories as cached, and changes to these memories + * won't immediately be visible to each other. It is necessary to ensure that + * these memories between the CPU and the network card are marked as uncached. + */ +static struct de4x5_desc rx_ring[NUM_RX_DESC] __aligned(32); +static struct de4x5_desc tx_ring[NUM_TX_DESC] __aligned(32); + struct dc2114x_priv { - struct de4x5_desc rx_ring[NUM_RX_DESC] __aligned(32); - struct de4x5_desc tx_ring[NUM_TX_DESC] __aligned(32); + struct de4x5_desc *rx_ring; /* Must be uncached to CPU */ + struct de4x5_desc *tx_ring; /* Must be uncached to CPU */ int rx_new; /* RX descriptor ring pointer */ int tx_new; /* TX descriptor ring pointer */ char rx_ring_size; @@ -271,7 +290,12 @@ static int read_srom(struct dc2114x_priv *priv, u_long ioaddr, int index) static void send_setup_frame(struct dc2114x_priv *priv) { - char setup_frame[SETUP_FRAME_LEN]; + /* We are writing setup frame and these changes should be visible to the + * network card immediately. So let's directly read/write through the + * uncached window. + */ + char __setup_frame[SETUP_FRAME_LEN] __aligned(32); + char *setup_frame = (char *)map_physmem((phys_addr_t)virt_to_phys(__setup_frame), 0, MAP_NOCACHE); char *pa = &setup_frame[0]; int i; @@ -292,8 +316,13 @@ static void send_setup_frame(struct dc2114x_priv *priv) } priv->tx_ring[priv->tx_new].buf = cpu_to_le32(phys_to_bus(priv->devno, - (u32)&setup_frame[0])); + (phys_addr_t)&setup_frame[0])); +#if CONFIG_IS_ENABLED(TULIP_MULTIPLE_TX_DESC) + priv->tx_ring[priv->tx_new].des1 = cpu_to_le32(TD_SET | SETUP_FRAME_LEN); + priv->tx_ring[priv->tx_ring_size - 1].des1 |= cpu_to_le32(TD_TER); +#else priv->tx_ring[priv->tx_new].des1 = cpu_to_le32(TD_TER | TD_SET | SETUP_FRAME_LEN); +#endif priv->tx_ring[priv->tx_new].status = cpu_to_le32(T_OWN); dc2114x_outl(priv, POLL_DEMAND, DE4X5_TPD); @@ -307,7 +336,7 @@ static void send_setup_frame(struct dc2114x_priv *priv) } if (le32_to_cpu(priv->tx_ring[priv->tx_new].status) != 0x7FFFFFFF) { - printf("TX error status2 = 0x%08X\n", + debug("TX error status2 = 0x%08X\n", le32_to_cpu(priv->tx_ring[priv->tx_new].status)); } @@ -332,9 +361,17 @@ static int dc21x4x_send_common(struct dc2114x_priv *priv, void *packet, int leng goto done; } + /* Packet should be visible to the network card */ + flush_dcache_range((phys_addr_t)packet, (phys_addr_t)(packet + RX_BUFF_SZ)); + priv->tx_ring[priv->tx_new].buf = cpu_to_le32(phys_to_bus(priv->devno, - (u32)packet)); + (phys_addr_t)packet)); +#if CONFIG_IS_ENABLED(TULIP_MULTIPLE_TX_DESC) + priv->tx_ring[priv->tx_new].des1 = cpu_to_le32(TD_LS | TD_FS | length); + priv->tx_ring[priv->tx_ring_size - 1].des1 |= cpu_to_le32(TD_TER); +#else priv->tx_ring[priv->tx_new].des1 = cpu_to_le32(TD_TER | TD_LS | TD_FS | length); +#endif priv->tx_ring[priv->tx_new].status = cpu_to_le32(T_OWN); dc2114x_outl(priv, POLL_DEMAND, DE4X5_TPD); @@ -349,7 +386,9 @@ static int dc21x4x_send_common(struct dc2114x_priv *priv, void *packet, int leng if (le32_to_cpu(priv->tx_ring[priv->tx_new].status) & TD_ES) { priv->tx_ring[priv->tx_new].status = 0x0; +#if !CONFIG_IS_ENABLED(TULIP_IGNORE_TX_NO_CARRIER) goto done; +#endif } status = length; @@ -398,13 +437,22 @@ static int dc21x4x_init_common(struct dc2114x_priv *priv) return -1; } - dc2114x_outl(priv, OMR_SDP | OMR_PS | OMR_PM, DE4X5_OMR); + /* 2024-07: + * Remove the OMR_PM flag and choose 16 perfect filtering mode since in + * modern networks there're plenty of multicasts and set ORM_PM flag will + * increase the dc2114x's workload and ask the U-Boot to handle packets + * not related to itself. And most of the time, U-Boot does not need this + * feature. + * + * A better way: let user to decide whether to have this flag. + */ + dc2114x_outl(priv, OMR_SDP | OMR_PS, DE4X5_OMR); for (i = 0; i < NUM_RX_DESC; i++) { priv->rx_ring[i].status = cpu_to_le32(R_OWN); priv->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ); priv->rx_ring[i].buf = cpu_to_le32(phys_to_bus(priv->devno, - (u32)net_rx_packets[i])); + (phys_addr_t)net_rx_packets[i])); priv->rx_ring[i].next = 0; } @@ -423,9 +471,9 @@ static int dc21x4x_init_common(struct dc2114x_priv *priv) priv->tx_ring[priv->tx_ring_size - 1].des1 |= cpu_to_le32(TD_TER); /* Tell the adapter where the TX/RX rings are located. */ - dc2114x_outl(priv, phys_to_bus(priv->devno, (u32)&priv->rx_ring), + dc2114x_outl(priv, phys_to_bus(priv->devno, (phys_addr_t)priv->rx_ring), DE4X5_RRBA); - dc2114x_outl(priv, phys_to_bus(priv->devno, (u32)&priv->tx_ring), + dc2114x_outl(priv, phys_to_bus(priv->devno, (phys_addr_t)priv->tx_ring), DE4X5_TRBA); start_de4x5(priv); @@ -461,21 +509,32 @@ static void read_hw_addr(struct dc2114x_priv *priv) } } +#if !CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI) static struct pci_device_id supported[] = { { PCI_DEVICE(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_FAST) }, { PCI_DEVICE(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142) }, { } }; +#endif static int dc2114x_start(struct udevice *dev) { - struct eth_pdata *plat = dev_get_plat(dev); struct dc2114x_priv *priv = dev_get_priv(dev); + int rval; - memcpy(priv->enetaddr, plat->enetaddr, sizeof(plat->enetaddr)); + if (!priv->enetaddr) { + rval = eth_env_get_enetaddr("ethaddr", priv->enetaddr); + if (!rval) { + printf("dc2114x: Err: please set a valid MAC address\n"); + return -EINVAL; + } + } + +#if !CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI) /* Ensure we're not sleeping. */ dm_pci_write_config8(dev, PCI_CFDA_PSM, WAKEUP); +#endif return dc21x4x_init_common(priv); } @@ -485,8 +544,9 @@ static void dc2114x_stop(struct udevice *dev) struct dc2114x_priv *priv = dev_get_priv(dev); dc21x4x_halt_common(priv); - +#if !CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI) dm_pci_write_config8(dev, PCI_CFDA_PSM, SLEEP); +#endif } static int dc2114x_send(struct udevice *dev, void *packet, int length) @@ -515,7 +575,8 @@ static int dc2114x_recv(struct udevice *dev, int flags, uchar **packetp) if (!ret) return 0; - *packetp = net_rx_packets[priv->rx_new]; + invalidate_dcache_range((phys_addr_t)net_rx_packets[priv->rx_new], (phys_addr_t)(net_rx_packets[priv->rx_new] + RX_BUFF_SZ)); + *packetp = (uchar *)net_rx_packets[priv->rx_new]; return ret - 4; } @@ -543,7 +604,7 @@ static int dc2114x_read_rom_hwaddr(struct udevice *dev) static int dc2114x_bind(struct udevice *dev) { - static int card_number; + static int card_number = 0; char name[16]; sprintf(name, "dc2114x#%u", card_number++); @@ -555,6 +616,8 @@ static int dc2114x_probe(struct udevice *dev) { struct eth_pdata *plat = dev_get_plat(dev); struct dc2114x_priv *priv = dev_get_priv(dev); + +#if !CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI) u16 command, status; u32 iobase; @@ -562,9 +625,6 @@ static int dc2114x_probe(struct udevice *dev) iobase &= ~0xf; debug("dc2114x: DEC 2114x PCI Device @0x%x\n", iobase); - - priv->devno = dev; - priv->enetaddr = plat->enetaddr; priv->iobase = (void __iomem *)dm_pci_mem_to_phys(dev, iobase); command = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; @@ -576,10 +636,29 @@ static int dc2114x_probe(struct udevice *dev) } dm_pci_write_config8(dev, PCI_LATENCY_TIMER, 0x60); +#endif + + priv->devno = dev; + priv->enetaddr = plat->enetaddr; + priv->rx_ring = (struct de4x5_desc *)map_physmem((phys_addr_t)virt_to_phys(rx_ring), 0, MAP_NOCACHE); + priv->tx_ring = (struct de4x5_desc *)map_physmem((phys_addr_t)virt_to_phys(tx_ring), 0, MAP_NOCACHE); return 0; } +#if CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI) +static int dc2114x_of_to_plat(struct udevice *dev) +{ + struct eth_pdata *plat = dev_get_plat(dev); + struct dc2114x_priv *priv = dev_get_priv(dev); + + plat->iobase = (phys_addr_t)map_physmem((phys_addr_t)devfdt_get_addr(dev), 0, MAP_NOCACHE); + priv->iobase = (void *)plat->iobase; + + return 0; +} +#endif + static const struct eth_ops dc2114x_ops = { .start = dc2114x_start, .send = dc2114x_send, @@ -589,9 +668,23 @@ static const struct eth_ops dc2114x_ops = { .read_rom_hwaddr = dc2114x_read_rom_hwaddr, }; +#if CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI) +static const struct udevice_id dc2114x_eth_ids[] = { + { .compatible = "dec,dmfe" }, + { .compatible = "tulip,dmfe" }, + { .compatible = "dec,dc2114x" }, + { .compatible = "tulip,dc2114x" }, + { } +}; +#endif + U_BOOT_DRIVER(eth_dc2114x) = { .name = "eth_dc2114x", .id = UCLASS_ETH, +#if CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI) + .of_match = dc2114x_eth_ids, + .of_to_plat = dc2114x_of_to_plat, +#endif .bind = dc2114x_bind, .probe = dc2114x_probe, .ops = &dc2114x_ops, @@ -599,4 +692,6 @@ U_BOOT_DRIVER(eth_dc2114x) = { .plat_auto = sizeof(struct eth_pdata), }; +#if !CONFIG_IS_ENABLED(TULIP_SUPPORT_NON_PCI) U_BOOT_PCI_DEVICE(eth_dc2114x, supported); +#endif diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c index d18a8d577ca..f64dbb7d6a1 100644 --- a/drivers/net/eepro100.c +++ b/drivers/net/eepro100.c @@ -678,7 +678,7 @@ static int eepro100_recv_common(struct eepro100_priv *priv, uchar **packetp) status = le16_to_cpu(desc->status); if (!(status & RFD_STATUS_C)) - return 0; + return -EAGAIN; /* Valid frame status. */ if (status & RFD_STATUS_OK) { diff --git a/drivers/net/ksz9477.c b/drivers/net/ksz9477.c index 43baa699619..7ebbe197660 100644 --- a/drivers/net/ksz9477.c +++ b/drivers/net/ksz9477.c @@ -11,7 +11,12 @@ #include <eth_phy.h> #include <linux/delay.h> #include <miiphy.h> -#include <i2c.h> +#if CONFIG_IS_ENABLED(DM_I2C) +# include <i2c.h> +#endif +#if CONFIG_IS_ENABLED(DM_SPI) +# include <spi.h> +#endif #include <net/dsa.h> #include <asm-generic/gpio.h> @@ -71,15 +76,157 @@ #define MMD_SETUP(mode, dev) (((u16)(mode) << PORT_MMD_OP_MODE_S) | (dev)) #define REG_PORT_PHY_MMD_INDEX_DATA 0x011C +/* SPI specific define (opcodes) */ +#define KSZ_SPI_OP_RD 3 +#define KSZ_SPI_OP_WR 2 + +#define KSZ9477_SPI_ADDR_SHIFT 24 +#define KSZ9477_SPI_ADDR_ALIGN 3 +#define KSZ9477_SPI_TURNAROUND_SHIFT 5 + +/** + * struct ksz_phy_ops - low-level KSZ bus operations + */ +struct ksz_phy_ops { + /* read() - Read bytes from the device + * + * @udev: bus device + * @reg: register offset + * @val: data read + * @len: Number of bytes to read + * + * @return: 0 on success, negative on failure + */ + int (*read)(struct udevice *udev, u32 reg, u8 *val, int len); + + /* write() - Write bytes to the device + * + * @udev: bus device + * @reg: register offset + * @val: data to write + * @len: Number of bytes to write + * + * @return: 0 on success, negative on failure + */ + int (*write)(struct udevice *udev, u32 reg, u8 *val, int len); +}; + struct ksz_dsa_priv { struct udevice *dev; + struct ksz_phy_ops *phy_ops; u32 features; /* chip specific features */ }; +#if CONFIG_IS_ENABLED(DM_I2C) +static inline int ksz_i2c_read(struct udevice *dev, u32 reg, u8 *val, int len) +{ + return dm_i2c_read(dev, reg, val, len); +} + +static inline int ksz_i2c_write(struct udevice *dev, u32 reg, u8 *val, int len) +{ + return dm_i2c_write(dev, reg, val, len); +} + +static struct ksz_phy_ops phy_i2c_ops = { + .read = ksz_i2c_read, + .write = ksz_i2c_write, +}; +#endif + +#if CONFIG_IS_ENABLED(DM_SPI) +/** + * ksz_spi_xfer() - only used for 8/16/32 bits bus access + * + * @dev: The SPI slave device which will be sending/receiving the data. + * @reg: register address. + * @out: Pointer to a string of bits to send out. The bits are + * held in a byte array and are sent MSB first. + * @in: Pointer to a string of bits that will be filled in. + * @len: number of bytes to read. + * + * Return: 0 on success, not 0 on failure + */ +static int ksz_spi_xfer(struct udevice *dev, u32 reg, const u8 *out, + u8 *in, u16 len) +{ + int ret; + u32 addr = 0; + u8 opcode; + + if (in && out) { + printf("%s: can't do full duplex\n", __func__); + return -EINVAL; + } + + if (len > 4 || len == 0) { + printf("%s: only 8/16/32 bits bus access supported\n", + __func__); + return -EINVAL; + } + + ret = dm_spi_claim_bus(dev); + if (ret < 0) { + printf("%s: could not claim bus\n", __func__); + return ret; + } + + opcode = (in ? KSZ_SPI_OP_RD : KSZ_SPI_OP_WR); + + /* The actual device address space is 16 bits (A15 - A0), + * so the values of address bits A23 - A16 in the SPI + * command/address phase are “don't care”. + */ + addr |= opcode << (KSZ9477_SPI_ADDR_SHIFT + KSZ9477_SPI_TURNAROUND_SHIFT); + addr |= reg << KSZ9477_SPI_TURNAROUND_SHIFT; + + addr = __swab32(addr); + + ret = dm_spi_xfer(dev, 32, &addr, NULL, SPI_XFER_BEGIN); + if (ret) { + printf("%s ERROR: dm_spi_xfer addr (%u)\n", __func__, ret); + goto release_bus; + } + + ret = dm_spi_xfer(dev, len * 8, out, in, SPI_XFER_END); + if (ret) { + printf("%s ERROR: dm_spi_xfer data (%u)\n", __func__, ret); + goto release_bus; + } + +release_bus: + /* If an error occurred, release the chip by deasserting the CS */ + if (ret < 0) + dm_spi_xfer(dev, 0, NULL, NULL, SPI_XFER_END); + + dm_spi_release_bus(dev); + + return ret; +} + +static inline int ksz_spi_read(struct udevice *dev, u32 reg, u8 *val, int len) +{ + return ksz_spi_xfer(dev, reg, NULL, val, len); +} + +static inline int ksz_spi_write(struct udevice *dev, u32 reg, u8 *val, int len) +{ + return ksz_spi_xfer(dev, reg, val, NULL, len); +} + +static struct ksz_phy_ops phy_spi_ops = { + .read = ksz_spi_read, + .write = ksz_spi_write, +}; +#endif + static inline int ksz_read8(struct udevice *dev, u32 reg, u8 *val) { - int ret = dm_i2c_read(dev, reg, val, 1); + struct ksz_dsa_priv *priv = dev_get_priv(dev); + struct ksz_phy_ops *phy_ops = priv->phy_ops; + + int ret = phy_ops->read(dev, reg, val, 1); dev_dbg(dev, "%s 0x%04x<<0x%02x\n", __func__, reg, *val); @@ -93,8 +240,11 @@ static inline int ksz_pread8(struct udevice *dev, int port, int reg, u8 *val) static inline int ksz_write8(struct udevice *dev, u32 reg, u8 val) { + struct ksz_dsa_priv *priv = dev_get_priv(dev); + struct ksz_phy_ops *phy_ops = priv->phy_ops; + dev_dbg(dev, "%s 0x%04x>>0x%02x\n", __func__, reg, val); - return dm_i2c_write(dev, reg, &val, 1); + return phy_ops->write(dev, reg, &val, 1); } static inline int ksz_pwrite8(struct udevice *dev, int port, int reg, u8 val) @@ -104,13 +254,15 @@ static inline int ksz_pwrite8(struct udevice *dev, int port, int reg, u8 val) static inline int ksz_write16(struct udevice *dev, u32 reg, u16 val) { + struct ksz_dsa_priv *priv = dev_get_priv(dev); + struct ksz_phy_ops *phy_ops = priv->phy_ops; u8 buf[2]; buf[1] = val & 0xff; buf[0] = val >> 8; dev_dbg(dev, "%s 0x%04x>>0x%04x\n", __func__, reg, val); - return dm_i2c_write(dev, reg, buf, 2); + return phy_ops->write(dev, reg, buf, 2); } static inline int ksz_pwrite16(struct udevice *dev, int port, int reg, u16 val) @@ -120,10 +272,12 @@ static inline int ksz_pwrite16(struct udevice *dev, int port, int reg, u16 val) static inline int ksz_read16(struct udevice *dev, u32 reg, u16 *val) { + struct ksz_dsa_priv *priv = dev_get_priv(dev); + struct ksz_phy_ops *phy_ops = priv->phy_ops; u8 buf[2]; int ret; - ret = dm_i2c_read(dev, reg, buf, 2); + ret = phy_ops->read(dev, reg, buf, 2); *val = (buf[0] << 8) | buf[1]; dev_dbg(dev, "%s 0x%04x<<0x%04x\n", __func__, reg, *val); @@ -137,7 +291,10 @@ static inline int ksz_pread16(struct udevice *dev, int port, int reg, u16 *val) static inline int ksz_read32(struct udevice *dev, u32 reg, u32 *val) { - return dm_i2c_read(dev, reg, (u8 *)val, 4); + struct ksz_dsa_priv *priv = dev_get_priv(dev); + struct ksz_phy_ops *phy_ops = priv->phy_ops; + + return phy_ops->read(dev, reg, (u8 *)val, 4); } static inline int ksz_pread32(struct udevice *dev, int port, int reg, u32 *val) @@ -147,6 +304,8 @@ static inline int ksz_pread32(struct udevice *dev, int port, int reg, u32 *val) static inline int ksz_write32(struct udevice *dev, u32 reg, u32 val) { + struct ksz_dsa_priv *priv = dev_get_priv(dev); + struct ksz_phy_ops *phy_ops = priv->phy_ops; u8 buf[4]; buf[3] = val & 0xff; @@ -155,7 +314,7 @@ static inline int ksz_write32(struct udevice *dev, u32 reg, u32 val) buf[0] = (val >> 8) & 0xff; dev_dbg(dev, "%s 0x%04x>>0x%04x\n", __func__, reg, val); - return dm_i2c_write(dev, reg, buf, 4); + return phy_ops->write(dev, reg, buf, 4); } static inline int ksz_pwrite32(struct udevice *dev, int port, int reg, u32 val) @@ -276,7 +435,7 @@ static int ksz_mdio_probe(struct udevice *dev) struct ksz_mdio_priv *priv = dev_get_priv(dev); dev_dbg(dev, "%s\n", __func__); - priv->ksz = dev_get_parent_priv(dev->parent); + priv->ksz = dev_get_priv(dev->parent); return 0; } @@ -355,12 +514,12 @@ static int ksz_port_setup(struct udevice *dev, int port, phy_interface_t interface) { struct dsa_pdata *pdata = dev_get_uclass_plat(dev); + struct ksz_dsa_priv *priv = dev_get_priv(dev); u8 data8; dev_dbg(dev, "%s P%d %s\n", __func__, port + 1, (port == pdata->cpu_port) ? "cpu" : ""); - struct ksz_dsa_priv *priv = dev_get_priv(dev); if (port != pdata->cpu_port) { if (priv->features & NEW_XMII) /* phy port: config errata and leds */ @@ -503,23 +662,59 @@ static int ksz_probe_mdio(struct udevice *dev) return 0; } -/* - * I2C driver - */ -static int ksz_i2c_probe(struct udevice *dev) +static void ksz_ops_register(struct udevice *dev, struct ksz_phy_ops *ops) +{ + struct ksz_dsa_priv *priv = dev_get_priv(dev); + + priv->phy_ops = ops; +} + +static bool dsa_ksz_check_ops(struct ksz_phy_ops *phy_ops) +{ + if (!phy_ops || !phy_ops->read || !phy_ops->write) + return false; + + return true; +} + +static int ksz_probe(struct udevice *dev) { struct dsa_pdata *pdata = dev_get_uclass_plat(dev); struct ksz_dsa_priv *priv = dev_get_priv(dev); + enum uclass_id parent_id = UCLASS_INVALID; int i, ret; u8 data8; u32 id; - dev_set_parent_priv(dev, priv); + parent_id = device_get_uclass_id(dev_get_parent(dev)); + switch (parent_id) { +#if CONFIG_IS_ENABLED(DM_I2C) + case UCLASS_I2C: { + ksz_ops_register(dev, &phy_i2c_ops); - ret = i2c_set_chip_offset_len(dev, 2); - if (ret) { - printf("i2c_set_chip_offset_len failed: %d\n", ret); - return ret; + ret = i2c_set_chip_offset_len(dev, 2); + if (ret) { + printf("i2c_set_chip_offset_len failed: %d\n", ret); + return ret; + } + break; + } +#endif +#if CONFIG_IS_ENABLED(DM_SPI) + case UCLASS_SPI: { + ksz_ops_register(dev, &phy_spi_ops); + break; + } +#endif + default: + dev_err(dev, "invalid parent bus (%s)\n", + uclass_get_name(parent_id)); + return -EINVAL; + } + + if (!dsa_ksz_check_ops(priv->phy_ops)) { + printf("Driver bug. No bus ops defined\n"); + return -EINVAL; } /* default config */ @@ -543,6 +738,9 @@ static int ksz_i2c_probe(struct udevice *dev) case 0x00956700: puts("KSZ9567R: "); break; + case 0x00989600: + puts("KSZ9896C: "); + break; case 0x00989700: puts("KSZ9897S: "); break; @@ -573,19 +771,20 @@ static int ksz_i2c_probe(struct udevice *dev) return 0; }; -static const struct udevice_id ksz_i2c_ids[] = { +static const struct udevice_id ksz_ids[] = { { .compatible = "microchip,ksz9897" }, { .compatible = "microchip,ksz9477" }, { .compatible = "microchip,ksz9567" }, { .compatible = "microchip,ksz9893" }, + { .compatible = "microchip,ksz9896" }, { } }; U_BOOT_DRIVER(ksz) = { .name = "ksz-switch", .id = UCLASS_DSA, - .of_match = ksz_i2c_ids, - .probe = ksz_i2c_probe, + .of_match = ksz_ids, + .probe = ksz_probe, .ops = &ksz_dsa_ops, .priv_auto = sizeof(struct ksz_dsa_priv), }; diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c index a96430cec43..4d67203ee70 100644 --- a/drivers/net/phy/motorcomm.c +++ b/drivers/net/phy/motorcomm.c @@ -12,6 +12,7 @@ #define PHY_ID_YT8511 0x0000010a #define PHY_ID_YT8531 0x4f51e91b +#define PHY_ID_YT8821 0x4f51ea19 #define PHY_ID_MASK GENMASK(31, 0) /* Extended Register's Address Offset Register */ @@ -102,8 +103,12 @@ #define YTPHY_SPECIFIC_STATUS_REG 0x11 #define YTPHY_DUPLEX_MASK BIT(13) #define YTPHY_DUPLEX_SHIFT 13 -#define YTPHY_SPEED_MODE_MASK GENMASK(15, 14) -#define YTPHY_SPEED_MODE_SHIFT 14 +#define YTPHY_SPEED_MASK ((0x3 << 14) | BIT(9)) +#define YTPHY_SPEED_10M ((0x0 << 14)) +#define YTPHY_SPEED_100M ((0x1 << 14)) +#define YTPHY_SPEED_1000M ((0x2 << 14)) +#define YTPHY_SPEED_10G ((0x3 << 14)) +#define YTPHY_SPEED_2500M ((0x0 << 14) | BIT(9)) #define YT8531_EXTREG_SLEEP_CONTROL1_REG 0x27 #define YT8531_ESC1R_SLEEP_SW BIT(15) @@ -131,6 +136,91 @@ #define TX_CLK_100_INVERTED BIT(4) #define TX_CLK_1000_INVERTED BIT(5) +#define YT8821_SDS_EXT_CSR_CTRL_REG 0x23 +#define YT8821_SDS_EXT_CSR_VCO_LDO_EN BIT(15) +#define YT8821_SDS_EXT_CSR_VCO_BIAS_LPF_EN BIT(8) + +#define YT8821_UTP_EXT_PI_CTRL_REG 0x56 +#define YT8821_UTP_EXT_PI_RST_N_FIFO BIT(5) +#define YT8821_UTP_EXT_PI_TX_CLK_SEL_AFE BIT(4) +#define YT8821_UTP_EXT_PI_RX_CLK_3_SEL_AFE BIT(3) +#define YT8821_UTP_EXT_PI_RX_CLK_2_SEL_AFE BIT(2) +#define YT8821_UTP_EXT_PI_RX_CLK_1_SEL_AFE BIT(1) +#define YT8821_UTP_EXT_PI_RX_CLK_0_SEL_AFE BIT(0) + +#define YT8821_UTP_EXT_VCT_CFG6_CTRL_REG 0x97 +#define YT8821_UTP_EXT_FECHO_AMP_TH_HUGE GENMASK(15, 8) + +#define YT8821_UTP_EXT_ECHO_CTRL_REG 0x336 +#define YT8821_UTP_EXT_TRACE_LNG_GAIN_THR_1000 GENMASK(14, 8) + +#define YT8821_UTP_EXT_GAIN_CTRL_REG 0x340 +#define YT8821_UTP_EXT_TRACE_MED_GAIN_THR_1000 GENMASK(6, 0) + +#define YT8821_UTP_EXT_RPDN_CTRL_REG 0x34E +#define YT8821_UTP_EXT_RPDN_BP_FFE_LNG_2500 BIT(15) +#define YT8821_UTP_EXT_RPDN_BP_FFE_SHT_2500 BIT(7) +#define YT8821_UTP_EXT_RPDN_IPR_SHT_2500 GENMASK(6, 0) + +#define YT8821_UTP_EXT_TH_20DB_2500_CTRL_REG 0x36A +#define YT8821_UTP_EXT_TH_20DB_2500 GENMASK(15, 0) + +#define YT8821_UTP_EXT_TRACE_CTRL_REG 0x372 +#define YT8821_UTP_EXT_TRACE_LNG_GAIN_THE_2500 GENMASK(14, 8) +#define YT8821_UTP_EXT_TRACE_MED_GAIN_THE_2500 GENMASK(6, 0) + +#define YT8821_UTP_EXT_ALPHA_IPR_CTRL_REG 0x374 +#define YT8821_UTP_EXT_ALPHA_SHT_2500 GENMASK(14, 8) +#define YT8821_UTP_EXT_IPR_LNG_2500 GENMASK(6, 0) + +#define YT8821_UTP_EXT_PLL_CTRL_REG 0x450 +#define YT8821_UTP_EXT_PLL_SPARE_CFG GENMASK(7, 0) + +#define YT8821_UTP_EXT_DAC_IMID_CH_2_3_CTRL_REG 0x466 +#define YT8821_UTP_EXT_DAC_IMID_CH_3_10_ORG GENMASK(14, 8) +#define YT8821_UTP_EXT_DAC_IMID_CH_2_10_ORG GENMASK(6, 0) + +#define YT8821_UTP_EXT_DAC_IMID_CH_0_1_CTRL_REG 0x467 +#define YT8821_UTP_EXT_DAC_IMID_CH_1_10_ORG GENMASK(14, 8) +#define YT8821_UTP_EXT_DAC_IMID_CH_0_10_ORG GENMASK(6, 0) + +#define YT8821_UTP_EXT_DAC_IMSB_CH_2_3_CTRL_REG 0x468 +#define YT8821_UTP_EXT_DAC_IMSB_CH_3_10_ORG GENMASK(14, 8) +#define YT8821_UTP_EXT_DAC_IMSB_CH_2_10_ORG GENMASK(6, 0) + +#define YT8821_UTP_EXT_DAC_IMSB_CH_0_1_CTRL_REG 0x469 +#define YT8821_UTP_EXT_DAC_IMSB_CH_1_10_ORG GENMASK(14, 8) +#define YT8821_UTP_EXT_DAC_IMSB_CH_0_10_ORG GENMASK(6, 0) + +#define YT8821_UTP_EXT_MU_COARSE_FR_CTRL_REG 0x4B3 +#define YT8821_UTP_EXT_MU_COARSE_FR_F_FFE GENMASK(14, 12) +#define YT8821_UTP_EXT_MU_COARSE_FR_F_FBE GENMASK(10, 8) + +#define YT8821_UTP_EXT_MU_FINE_FR_CTRL_REG 0x4B5 +#define YT8821_UTP_EXT_MU_FINE_FR_F_FFE GENMASK(14, 12) +#define YT8821_UTP_EXT_MU_FINE_FR_F_FBE GENMASK(10, 8) + +#define YT8821_UTP_EXT_VGA_LPF1_CAP_CTRL_REG 0x4D2 +#define YT8821_UTP_EXT_VGA_LPF1_CAP_OTHER GENMASK(7, 4) +#define YT8821_UTP_EXT_VGA_LPF1_CAP_2500 GENMASK(3, 0) + +#define YT8821_UTP_EXT_VGA_LPF2_CAP_CTRL_REG 0x4D3 +#define YT8821_UTP_EXT_VGA_LPF2_CAP_OTHER GENMASK(7, 4) +#define YT8821_UTP_EXT_VGA_LPF2_CAP_2500 GENMASK(3, 0) + +#define YT8821_UTP_EXT_TXGE_NFR_FR_THP_CTRL_REG 0x660 +#define YT8821_UTP_EXT_NFR_TX_ABILITY BIT(3) + +#define YT8821_CHIP_MODE_FORCE_BX2500 1 + +/* chip config register */ +#define YTPHY_CCR_MODE_SEL_MASK GENMASK(2, 0) + +#define YTPHY_REG_SPACE_SELECT_REG 0xA000 +#define YTPHY_RSSR_SPACE_MASK BIT(1) +#define YTPHY_RSSR_FIBER_SPACE (0x1 << 1) +#define YTPHY_RSSR_UTP_SPACE (0x0 << 1) + struct ytphy_plat_priv { u32 rx_delay_ps; u32 tx_delay_ps; @@ -295,15 +385,15 @@ static int yt8531_parse_status(struct phy_device *phydev) if (val < 0) return val; - speed_mode = (val & YTPHY_SPEED_MODE_MASK) >> YTPHY_SPEED_MODE_SHIFT; + speed_mode = (val & YTPHY_SPEED_MASK); switch (speed_mode) { - case 2: + case YTPHY_SPEED_1000M: speed = SPEED_1000; break; - case 1: + case YTPHY_SPEED_100M: speed = SPEED_100; break; - default: + case YTPHY_SPEED_10M: speed = SPEED_10; break; } @@ -632,6 +722,398 @@ static int yt8531_probe(struct phy_device *phydev) return 0; } +static int ytphy_save_page(struct phy_device *phydev) +{ + int old_page; + + old_page = ytphy_read_ext(phydev, YTPHY_REG_SPACE_SELECT_REG); + if (old_page < 0) + return old_page; + + if ((old_page & YTPHY_RSSR_SPACE_MASK) == YTPHY_RSSR_FIBER_SPACE) + return YTPHY_RSSR_FIBER_SPACE; + + return YTPHY_RSSR_UTP_SPACE; +}; + +static int ytphy_restore_page(struct phy_device *phydev, int page, + int ret) +{ + int mask = YTPHY_RSSR_SPACE_MASK; + int set; + int r; + + if ((page & YTPHY_RSSR_SPACE_MASK) == YTPHY_RSSR_FIBER_SPACE) + set = YTPHY_RSSR_FIBER_SPACE; + else + set = YTPHY_RSSR_UTP_SPACE; + + r = ytphy_modify_ext(phydev, YTPHY_REG_SPACE_SELECT_REG, mask, + set); + if (ret >= 0 && r < 0) + ret = r; + + return ret; +}; + +static int ytphy_write_ext(struct phy_device *phydev, u16 regnum, + u16 val) +{ + int ret; + + ret = phy_write(phydev, MDIO_DEVAD_NONE, + YTPHY_PAGE_SELECT, regnum); + if (ret < 0) + return ret; + + return phy_write(phydev, MDIO_DEVAD_NONE, YTPHY_PAGE_DATA, val); +} + +static int yt8821_probe(struct phy_device *phydev) +{ + phydev->advertising = PHY_GBIT_FEATURES | + SUPPORTED_2500baseX_Full | + SUPPORTED_Pause | + SUPPORTED_Asym_Pause; + phydev->supported = phydev->advertising; + + return 0; +} + +static int yt8821_serdes_init(struct phy_device *phydev) +{ + int old_page; + u16 mask; + u16 set; + int ret; + + old_page = ytphy_save_page(phydev); + if (old_page < 0) + return old_page; + + ret = ytphy_modify_ext(phydev, YTPHY_REG_SPACE_SELECT_REG, + YTPHY_RSSR_SPACE_MASK, + YTPHY_RSSR_FIBER_SPACE); + if (ret < 0) + goto err_restore_page; + + ret = phy_modify(phydev, MDIO_DEVAD_NONE, MII_BMCR, + BMCR_ANENABLE, 0); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_SDS_EXT_CSR_VCO_LDO_EN | + YT8821_SDS_EXT_CSR_VCO_BIAS_LPF_EN; + set = YT8821_SDS_EXT_CSR_VCO_LDO_EN; + ret = ytphy_modify_ext(phydev, YT8821_SDS_EXT_CSR_CTRL_REG, mask, + set); + +err_restore_page: + return ytphy_restore_page(phydev, old_page, ret); +} + +static int yt8821_utp_init(struct phy_device *phydev) +{ + int old_page; + u16 mask; + u16 save; + u16 set; + int ret; + + old_page = ytphy_save_page(phydev); + if (old_page < 0) + return old_page; + + ret = ytphy_modify_ext(phydev, YTPHY_REG_SPACE_SELECT_REG, + YTPHY_RSSR_SPACE_MASK, + YTPHY_RSSR_UTP_SPACE); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_RPDN_BP_FFE_LNG_2500 | + YT8821_UTP_EXT_RPDN_BP_FFE_SHT_2500 | + YT8821_UTP_EXT_RPDN_IPR_SHT_2500; + set = YT8821_UTP_EXT_RPDN_BP_FFE_LNG_2500 | + YT8821_UTP_EXT_RPDN_BP_FFE_SHT_2500; + ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_RPDN_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_VGA_LPF1_CAP_OTHER | + YT8821_UTP_EXT_VGA_LPF1_CAP_2500; + ret = ytphy_modify_ext(phydev, + YT8821_UTP_EXT_VGA_LPF1_CAP_CTRL_REG, + mask, 0); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_VGA_LPF2_CAP_OTHER | + YT8821_UTP_EXT_VGA_LPF2_CAP_2500; + ret = ytphy_modify_ext(phydev, + YT8821_UTP_EXT_VGA_LPF2_CAP_CTRL_REG, + mask, 0); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_TRACE_LNG_GAIN_THE_2500 | + YT8821_UTP_EXT_TRACE_MED_GAIN_THE_2500; + set = FIELD_PREP(YT8821_UTP_EXT_TRACE_LNG_GAIN_THE_2500, 0x5a) | + FIELD_PREP(YT8821_UTP_EXT_TRACE_MED_GAIN_THE_2500, 0x3c); + ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_TRACE_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_IPR_LNG_2500; + set = FIELD_PREP(YT8821_UTP_EXT_IPR_LNG_2500, 0x6c); + ret = ytphy_modify_ext(phydev, + YT8821_UTP_EXT_ALPHA_IPR_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_TRACE_LNG_GAIN_THR_1000; + set = FIELD_PREP(YT8821_UTP_EXT_TRACE_LNG_GAIN_THR_1000, 0x2a); + ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_ECHO_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_TRACE_MED_GAIN_THR_1000; + set = FIELD_PREP(YT8821_UTP_EXT_TRACE_MED_GAIN_THR_1000, 0x22); + ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_GAIN_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_TH_20DB_2500; + set = FIELD_PREP(YT8821_UTP_EXT_TH_20DB_2500, 0x8000); + ret = ytphy_modify_ext(phydev, + YT8821_UTP_EXT_TH_20DB_2500_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_MU_COARSE_FR_F_FFE | + YT8821_UTP_EXT_MU_COARSE_FR_F_FBE; + set = FIELD_PREP(YT8821_UTP_EXT_MU_COARSE_FR_F_FFE, 0x7) | + FIELD_PREP(YT8821_UTP_EXT_MU_COARSE_FR_F_FBE, 0x7); + ret = ytphy_modify_ext(phydev, + YT8821_UTP_EXT_MU_COARSE_FR_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_MU_FINE_FR_F_FFE | + YT8821_UTP_EXT_MU_FINE_FR_F_FBE; + set = FIELD_PREP(YT8821_UTP_EXT_MU_FINE_FR_F_FFE, 0x2) | + FIELD_PREP(YT8821_UTP_EXT_MU_FINE_FR_F_FBE, 0x2); + ret = ytphy_modify_ext(phydev, + YT8821_UTP_EXT_MU_FINE_FR_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + /* save YT8821_UTP_EXT_PI_CTRL_REG's val for use later */ + ret = ytphy_read_ext(phydev, YT8821_UTP_EXT_PI_CTRL_REG); + if (ret < 0) + goto err_restore_page; + + save = ret; + + mask = YT8821_UTP_EXT_PI_TX_CLK_SEL_AFE | + YT8821_UTP_EXT_PI_RX_CLK_3_SEL_AFE | + YT8821_UTP_EXT_PI_RX_CLK_2_SEL_AFE | + YT8821_UTP_EXT_PI_RX_CLK_1_SEL_AFE | + YT8821_UTP_EXT_PI_RX_CLK_0_SEL_AFE; + ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_PI_CTRL_REG, + mask, 0); + if (ret < 0) + goto err_restore_page; + + /* restore YT8821_UTP_EXT_PI_CTRL_REG's val */ + ret = ytphy_write_ext(phydev, YT8821_UTP_EXT_PI_CTRL_REG, save); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_FECHO_AMP_TH_HUGE; + set = FIELD_PREP(YT8821_UTP_EXT_FECHO_AMP_TH_HUGE, 0x38); + ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_VCT_CFG6_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_NFR_TX_ABILITY; + set = YT8821_UTP_EXT_NFR_TX_ABILITY; + ret = ytphy_modify_ext(phydev, + YT8821_UTP_EXT_TXGE_NFR_FR_THP_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_PLL_SPARE_CFG; + set = FIELD_PREP(YT8821_UTP_EXT_PLL_SPARE_CFG, 0xe9); + ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_PLL_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_DAC_IMID_CH_3_10_ORG | + YT8821_UTP_EXT_DAC_IMID_CH_2_10_ORG; + set = FIELD_PREP(YT8821_UTP_EXT_DAC_IMID_CH_3_10_ORG, 0x64) | + FIELD_PREP(YT8821_UTP_EXT_DAC_IMID_CH_2_10_ORG, 0x64); + ret = ytphy_modify_ext(phydev, + YT8821_UTP_EXT_DAC_IMID_CH_2_3_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_DAC_IMID_CH_1_10_ORG | + YT8821_UTP_EXT_DAC_IMID_CH_0_10_ORG; + set = FIELD_PREP(YT8821_UTP_EXT_DAC_IMID_CH_1_10_ORG, 0x64) | + FIELD_PREP(YT8821_UTP_EXT_DAC_IMID_CH_0_10_ORG, 0x64); + ret = ytphy_modify_ext(phydev, + YT8821_UTP_EXT_DAC_IMID_CH_0_1_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_DAC_IMSB_CH_3_10_ORG | + YT8821_UTP_EXT_DAC_IMSB_CH_2_10_ORG; + set = FIELD_PREP(YT8821_UTP_EXT_DAC_IMSB_CH_3_10_ORG, 0x64) | + FIELD_PREP(YT8821_UTP_EXT_DAC_IMSB_CH_2_10_ORG, 0x64); + ret = ytphy_modify_ext(phydev, + YT8821_UTP_EXT_DAC_IMSB_CH_2_3_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_DAC_IMSB_CH_1_10_ORG | + YT8821_UTP_EXT_DAC_IMSB_CH_0_10_ORG; + set = FIELD_PREP(YT8821_UTP_EXT_DAC_IMSB_CH_1_10_ORG, 0x64) | + FIELD_PREP(YT8821_UTP_EXT_DAC_IMSB_CH_0_10_ORG, 0x64); + ret = ytphy_modify_ext(phydev, + YT8821_UTP_EXT_DAC_IMSB_CH_0_1_CTRL_REG, + mask, set); + +err_restore_page: + return ytphy_restore_page(phydev, old_page, ret); +} + +static int yt8821_auto_sleep_config(struct phy_device *phydev, + bool enable) +{ + int old_page; + int ret; + + old_page = ytphy_save_page(phydev); + if (old_page < 0) + return old_page; + + ret = ytphy_modify_ext(phydev, YTPHY_REG_SPACE_SELECT_REG, + YTPHY_RSSR_SPACE_MASK, + YTPHY_RSSR_UTP_SPACE); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_modify_ext(phydev, + YT8531_EXTREG_SLEEP_CONTROL1_REG, + YT8531_ESC1R_SLEEP_SW, + enable ? 1 : 0); + +err_restore_page: + return ytphy_restore_page(phydev, old_page, ret); +} + +static int yt8821_soft_reset(struct phy_device *phydev) +{ + return ytphy_modify_ext(phydev, YT8531_CHIP_CONFIG_REG, + YT8531_CCR_SW_RST, 0); +} + +static int yt8821_config(struct phy_device *phydev) +{ + u8 mode = YT8821_CHIP_MODE_FORCE_BX2500; + int ret; + u16 set; + + set = FIELD_PREP(YTPHY_CCR_MODE_SEL_MASK, mode); + ret = ytphy_modify_ext(phydev, + YT8531_CHIP_CONFIG_REG, + YTPHY_CCR_MODE_SEL_MASK, + set); + if (ret < 0) + return ret; + + ret = yt8821_serdes_init(phydev); + if (ret < 0) + return ret; + + ret = yt8821_utp_init(phydev); + if (ret < 0) + return ret; + + ret = yt8821_auto_sleep_config(phydev, false); + if (ret < 0) + return ret; + + return yt8821_soft_reset(phydev); +} + +static void yt8821_parse_status(struct phy_device *phydev, int val) +{ + int speed_mode; + int speed; + + speed_mode = val & YTPHY_SPEED_MASK; + switch (speed_mode) { + case YTPHY_SPEED_2500M: + speed = SPEED_2500; + break; + case YTPHY_SPEED_1000M: + speed = SPEED_1000; + break; + case YTPHY_SPEED_100M: + speed = SPEED_100; + break; + case YTPHY_SPEED_10M: + speed = SPEED_10; + break; + } + + phydev->speed = speed; + phydev->duplex = FIELD_GET(YTPHY_DUPLEX_MASK, val); +} + +static int yt8821_startup(struct phy_device *phydev) +{ + u16 val; + int ret; + + ret = ytphy_modify_ext(phydev, YTPHY_REG_SPACE_SELECT_REG, + YTPHY_RSSR_SPACE_MASK, + YTPHY_RSSR_UTP_SPACE); + if (ret) + return ret; + + ret = genphy_update_link(phydev); + if (ret) + return ret; + + ret = phy_read(phydev, MDIO_DEVAD_NONE, + YTPHY_SPECIFIC_STATUS_REG); + if (ret < 0) + return ret; + + val = ret; + + if (phydev->link) + yt8821_parse_status(phydev, val); + + return 0; +} + U_BOOT_PHY_DRIVER(motorcomm8511) = { .name = "YT8511 Gigabit Ethernet", .uid = PHY_ID_YT8511, @@ -652,3 +1134,14 @@ U_BOOT_PHY_DRIVER(motorcomm8531) = { .startup = &yt8531_startup, .shutdown = &genphy_shutdown, }; + +U_BOOT_PHY_DRIVER(motorcomm8821) = { + .name = "YT8821 2.5G Ethernet", + .uid = PHY_ID_YT8821, + .mask = PHY_ID_MASK, + .mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS | MDIO_MMD_AN), + .probe = &yt8821_probe, + .config = &yt8821_config, + .startup = &yt8821_startup, + .shutdown = &genphy_shutdown, +}; diff --git a/drivers/net/rtl8139.c b/drivers/net/rtl8139.c index 2e0afad089f..5f4b1e2d3a0 100644 --- a/drivers/net/rtl8139.c +++ b/drivers/net/rtl8139.c @@ -433,7 +433,7 @@ static int rtl8139_recv_common(struct rtl8139_priv *priv, unsigned char *rxdata, int length = 0; if (inb(priv->ioaddr + RTL_REG_CHIPCMD) & RTL_REG_CHIPCMD_RXBUFEMPTY) - return 0; + return -EAGAIN; priv->rxstatus = inw(priv->ioaddr + RTL_REG_INTRSTATUS); /* See below for the rest of the interrupt acknowledges. */ diff --git a/drivers/pci/pcie_brcmstb.c b/drivers/pci/pcie_brcmstb.c index f978c64365c..f089c48f028 100644 --- a/drivers/pci/pcie_brcmstb.c +++ b/drivers/pci/pcie_brcmstb.c @@ -12,6 +12,7 @@ * Copyright (C) 2020 Nicolas Saenz Julienne <nsaenzjulienne@suse.de> */ +#include <asm/arch/acpi/bcm2711.h> #include <errno.h> #include <dm.h> #include <dm/ofnode.h> @@ -21,88 +22,6 @@ #include <linux/log2.h> #include <linux/iopoll.h> -/* Offset of the mandatory PCIe capability config registers */ -#define BRCM_PCIE_CAP_REGS 0x00ac - -/* The PCIe controller register offsets */ -#define PCIE_RC_CFG_VENDOR_SPECIFIC_REG1 0x0188 -#define VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK 0xc -#define VENDOR_SPECIFIC_REG1_LITTLE_ENDIAN 0x0 - -#define PCIE_RC_CFG_PRIV1_ID_VAL3 0x043c -#define CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK 0xffffff - -#define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY 0x04dc -#define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK 0xc00 - -#define PCIE_RC_DL_MDIO_ADDR 0x1100 -#define PCIE_RC_DL_MDIO_WR_DATA 0x1104 -#define PCIE_RC_DL_MDIO_RD_DATA 0x1108 - -#define PCIE_MISC_MISC_CTRL 0x4008 -#define MISC_CTRL_SCB_ACCESS_EN_MASK 0x1000 -#define MISC_CTRL_CFG_READ_UR_MODE_MASK 0x2000 -#define MISC_CTRL_MAX_BURST_SIZE_MASK 0x300000 -#define MISC_CTRL_MAX_BURST_SIZE_128 0x0 -#define MISC_CTRL_SCB0_SIZE_MASK 0xf8000000 - -#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO 0x400c -#define PCIE_MEM_WIN0_LO(win) \ - PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO + ((win) * 4) - -#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI 0x4010 -#define PCIE_MEM_WIN0_HI(win) \ - PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI + ((win) * 4) - -#define PCIE_MISC_RC_BAR1_CONFIG_LO 0x402c -#define RC_BAR1_CONFIG_LO_SIZE_MASK 0x1f - -#define PCIE_MISC_RC_BAR2_CONFIG_LO 0x4034 -#define RC_BAR2_CONFIG_LO_SIZE_MASK 0x1f -#define PCIE_MISC_RC_BAR2_CONFIG_HI 0x4038 - -#define PCIE_MISC_RC_BAR3_CONFIG_LO 0x403c -#define RC_BAR3_CONFIG_LO_SIZE_MASK 0x1f - -#define PCIE_MISC_PCIE_STATUS 0x4068 -#define STATUS_PCIE_PORT_MASK 0x80 -#define STATUS_PCIE_PORT_SHIFT 7 -#define STATUS_PCIE_DL_ACTIVE_MASK 0x20 -#define STATUS_PCIE_DL_ACTIVE_SHIFT 5 -#define STATUS_PCIE_PHYLINKUP_MASK 0x10 -#define STATUS_PCIE_PHYLINKUP_SHIFT 4 - -#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT 0x4070 -#define MEM_WIN0_BASE_LIMIT_LIMIT_MASK 0xfff00000 -#define MEM_WIN0_BASE_LIMIT_BASE_MASK 0xfff0 -#define MEM_WIN0_BASE_LIMIT_BASE_HI_SHIFT 12 -#define PCIE_MEM_WIN0_BASE_LIMIT(win) \ - PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT + ((win) * 4) - -#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI 0x4080 -#define MEM_WIN0_BASE_HI_BASE_MASK 0xff -#define PCIE_MEM_WIN0_BASE_HI(win) \ - PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI + ((win) * 8) - -#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI 0x4084 -#define PCIE_MEM_WIN0_LIMIT_HI_LIMIT_MASK 0xff -#define PCIE_MEM_WIN0_LIMIT_HI(win) \ - PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI + ((win) * 8) - -#define PCIE_MISC_HARD_PCIE_HARD_DEBUG 0x4204 -#define PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x08000000 - -#define PCIE_MSI_INTR2_CLR 0x4508 -#define PCIE_MSI_INTR2_MASK_SET 0x4510 - -#define PCIE_EXT_CFG_DATA 0x8000 - -#define PCIE_EXT_CFG_INDEX 0x9000 - -#define PCIE_RGR1_SW_INIT_1 0x9210 -#define RGR1_SW_INIT_1_PERST_MASK 0x1 -#define RGR1_SW_INIT_1_INIT_MASK 0x2 - /* PCIe parameters */ #define BRCM_NUM_PCIE_OUT_WINS 4 @@ -447,7 +366,7 @@ static int brcm_pcie_probe(struct udevice *dev) * This will need to be changed when support for other SoCs is added. */ setbits_le32(base + PCIE_RGR1_SW_INIT_1, - RGR1_SW_INIT_1_INIT_MASK | RGR1_SW_INIT_1_PERST_MASK); + PCIE_RGR1_SW_INIT_1_INIT_MASK | PCIE_RGR1_SW_INIT_1_PERST_MASK); /* * The delay is a safety precaution to preclude the reset signal * from looking like a glitch. @@ -455,7 +374,7 @@ static int brcm_pcie_probe(struct udevice *dev) udelay(100); /* Take the bridge out of reset */ - clrbits_le32(base + PCIE_RGR1_SW_INIT_1, RGR1_SW_INIT_1_INIT_MASK); + clrbits_le32(base + PCIE_RGR1_SW_INIT_1, PCIE_RGR1_SW_INIT_1_INIT_MASK); clrbits_le32(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG, PCIE_HARD_DEBUG_SERDES_IDDQ_MASK); @@ -508,7 +427,7 @@ static int brcm_pcie_probe(struct udevice *dev) /* Unassert the fundamental reset */ clrbits_le32(pcie->base + PCIE_RGR1_SW_INIT_1, - RGR1_SW_INIT_1_PERST_MASK); + PCIE_RGR1_SW_INIT_1_PERST_MASK); /* * Wait for 100ms after PERST# deassertion; see PCIe CEM specification @@ -552,7 +471,7 @@ static int brcm_pcie_probe(struct udevice *dev) * a PCIe-PCIe bridge (the default setting is to be EP mode). */ clrsetbits_le32(base + PCIE_RC_CFG_PRIV1_ID_VAL3, - CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK, 0x060400); + PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK, 0x060400); if (pcie->ssc) { ret = brcm_pcie_set_ssc(pcie->base); @@ -570,8 +489,8 @@ static int brcm_pcie_probe(struct udevice *dev) nlw, ssc_good ? "(SSC)" : "(!SSC)"); /* PCIe->SCB endian mode for BAR */ - clrsetbits_le32(base + PCIE_RC_CFG_VENDOR_SPECIFIC_REG1, - VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK, + clrsetbits_le32(base + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1, + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK, VENDOR_SPECIFIC_REG1_LITTLE_ENDIAN); /* @@ -584,7 +503,7 @@ static int brcm_pcie_probe(struct udevice *dev) * let's instead just unadvertise ASPM support. */ clrbits_le32(base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY, - PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK); + LINK_CAPABILITY_ASPM_SUPPORT_MASK); return 0; } @@ -595,14 +514,14 @@ static int brcm_pcie_remove(struct udevice *dev) void __iomem *base = pcie->base; /* Assert fundamental reset */ - setbits_le32(base + PCIE_RGR1_SW_INIT_1, RGR1_SW_INIT_1_PERST_MASK); + setbits_le32(base + PCIE_RGR1_SW_INIT_1, PCIE_RGR1_SW_INIT_1_PERST_MASK); /* Turn off SerDes */ setbits_le32(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG, PCIE_HARD_DEBUG_SERDES_IDDQ_MASK); /* Shutdown bridge */ - setbits_le32(base + PCIE_RGR1_SW_INIT_1, RGR1_SW_INIT_1_INIT_MASK); + setbits_le32(base + PCIE_RGR1_SW_INIT_1, PCIE_RGR1_SW_INIT_1_INIT_MASK); return 0; } diff --git a/drivers/pinctrl/nxp/pinctrl-imx.c b/drivers/pinctrl/nxp/pinctrl-imx.c index ff466c49104..b1960c56b51 100644 --- a/drivers/pinctrl/nxp/pinctrl-imx.c +++ b/drivers/pinctrl/nxp/pinctrl-imx.c @@ -22,7 +22,7 @@ static int imx_pinctrl_set_state(struct udevice *dev, struct udevice *config) { struct imx_pinctrl_priv *priv = dev_get_priv(dev); struct imx_pinctrl_soc_info *info = priv->info; - int node = dev_of_offset(config); + ofnode node = dev_ofnode(config); const struct fdt_property *prop; u32 *pin_data; int npins, size, pin_size; @@ -40,7 +40,7 @@ static int imx_pinctrl_set_state(struct udevice *dev, struct udevice *config) else pin_size = FSL_PIN_SIZE; - prop = fdt_getprop(gd->fdt_blob, node, "fsl,pins", &size); + prop = ofnode_get_property(node, "fsl,pins", &size); if (!prop) { dev_err(dev, "No fsl,pins property in node %s\n", config->name); return -EINVAL; @@ -56,8 +56,8 @@ static int imx_pinctrl_set_state(struct udevice *dev, struct udevice *config) if (!pin_data) return -ENOMEM; - if (fdtdec_get_int_array(gd->fdt_blob, node, "fsl,pins", - pin_data, size >> 2)) { + if (ofnode_read_u32_array(node, "fsl,pins", + pin_data, size >> 2)) { dev_err(dev, "Error reading pin data.\n"); devm_kfree(dev, pin_data); return -EINVAL; @@ -202,10 +202,11 @@ int imx_pinctrl_probe(struct udevice *dev, struct imx_pinctrl_soc_info *info) { struct imx_pinctrl_priv *priv = dev_get_priv(dev); - int node = dev_of_offset(dev), ret; - struct fdtdec_phandle_args arg; + struct ofnode_phandle_args arg; + ofnode node = dev_ofnode(dev); fdt_addr_t addr; fdt_size_t size; + int ret; if (!info) { dev_err(dev, "wrong pinctrl info\n"); @@ -218,7 +219,7 @@ int imx_pinctrl_probe(struct udevice *dev, if (info->flags & IMX8_USE_SCU) return 0; - addr = devfdt_get_addr_size_index(dev, 0, &size); + addr = ofnode_get_addr_size_index(dev_ofnode(dev), 0, &size); if (addr == FDT_ADDR_T_NONE) return -EINVAL; @@ -227,22 +228,20 @@ int imx_pinctrl_probe(struct udevice *dev, return -ENOMEM; priv->info = info; - info->mux_mask = fdtdec_get_int(gd->fdt_blob, node, "fsl,mux_mask", 0); + info->mux_mask = ofnode_read_u32(node, "fsl,mux_mask", 0); /* * Refer to linux documentation for details: * Documentation/devicetree/bindings/pinctrl/fsl,imx7d-pinctrl.txt */ - if (fdtdec_get_bool(gd->fdt_blob, node, "fsl,input-sel")) { - ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, - node, "fsl,input-sel", + if (ofnode_read_bool(node, "fsl,input-sel")) { + ret = ofnode_parse_phandle_with_args(node, "fsl,input-sel", NULL, 0, 0, &arg); if (ret) { dev_err(dev, "iomuxc fsl,input-sel property not found\n"); return -EINVAL; } - addr = fdtdec_get_addr_size(gd->fdt_blob, arg.node, "reg", - &size); + addr = ofnode_get_addr_size(arg.node, "reg", &size); if (addr == FDT_ADDR_T_NONE) return -EINVAL; diff --git a/drivers/power/domain/imx8m-power-domain.c b/drivers/power/domain/imx8m-power-domain.c index 8b6870c8646..c22fbe60675 100644 --- a/drivers/power/domain/imx8m-power-domain.c +++ b/drivers/power/domain/imx8m-power-domain.c @@ -456,25 +456,22 @@ static int imx8m_power_domain_of_xlate(struct power_domain *power_domain, static int imx8m_power_domain_bind(struct udevice *dev) { - int offset; + ofnode subnode; const char *name; int ret = 0; - offset = dev_of_offset(dev); - for (offset = fdt_first_subnode(gd->fdt_blob, offset); offset > 0; - offset = fdt_next_subnode(gd->fdt_blob, offset)) { + ofnode_for_each_subnode(subnode, dev_ofnode(dev)) { /* Bind the subnode to this driver */ - name = fdt_get_name(gd->fdt_blob, offset, NULL); + name = ofnode_get_name(subnode); /* Descend into 'pgc' subnode */ if (!strstr(name, "power-domain")) { - offset = fdt_first_subnode(gd->fdt_blob, offset); - name = fdt_get_name(gd->fdt_blob, offset, NULL); + subnode = ofnode_first_subnode(subnode); + name = ofnode_get_name(subnode); } - ret = device_bind_with_driver_data(dev, dev->driver, name, dev->driver_data, - offset_to_ofnode(offset), + subnode, NULL); if (ret == -ENODEV) @@ -514,8 +511,7 @@ static int imx8m_power_domain_of_to_plat(struct udevice *dev) struct imx_pgc_domain_data *domain_data = (struct imx_pgc_domain_data *)dev_get_driver_data(dev); - pdata->resource_id = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), - "reg", -1); + pdata->resource_id = ofnode_read_u32_default(dev_ofnode(dev), "reg", -1); pdata->domain = &domain_data->domains[pdata->resource_id]; pdata->regs = domain_data->pgc_regs; pdata->base = dev_read_addr_ptr(dev->parent); diff --git a/drivers/serial/serial_pl01x.c b/drivers/serial/serial_pl01x.c index 80c35963b8f..e6bf0c2935b 100644 --- a/drivers/serial/serial_pl01x.c +++ b/drivers/serial/serial_pl01x.c @@ -19,6 +19,7 @@ #include <watchdog.h> #include <asm/io.h> #include <serial.h> +#include <spl.h> #include <dm/device_compat.h> #include <dm/platform_data/serial_pl01x.h> #include <linux/compiler.h> @@ -272,6 +273,28 @@ __weak struct serial_device *default_serial_console(void) return &pl01x_serial_drv; } #else + +static int pl01x_serial_getinfo(struct udevice *dev, + struct serial_device_info *info) +{ + struct pl01x_serial_plat *plat = dev_get_plat(dev); + + /* save code size */ + if (!not_xpl()) + return -ENOSYS; + + info->type = SERIAL_CHIP_PL01X; + info->addr_space = SERIAL_ADDRESS_SPACE_MEMORY; + info->addr = plat->base; + info->size = 0x1000; + info->reg_width = 4; + info->reg_shift = 2; + info->reg_offset = 0; + info->clock = plat->clock; + + return 0; +} + int pl01x_serial_setbrg(struct udevice *dev, int baudrate) { struct pl01x_serial_plat *plat = dev_get_plat(dev); @@ -341,6 +364,7 @@ static const struct dm_serial_ops pl01x_serial_ops = { .pending = pl01x_serial_pending, .getc = pl01x_serial_getc, .setbrg = pl01x_serial_setbrg, + .getinfo = pl01x_serial_getinfo, }; #if CONFIG_IS_ENABLED(OF_REAL) diff --git a/drivers/spi/zynq_qspi.c b/drivers/spi/zynq_qspi.c index f5b3fb5c125..4aad3248d9e 100644 --- a/drivers/spi/zynq_qspi.c +++ b/drivers/spi/zynq_qspi.c @@ -734,7 +734,7 @@ static int zynq_qspi_set_mode(struct udevice *bus, uint mode) return 0; } -bool update_stripe(const struct spi_mem_op *op) +static bool update_stripe(const struct spi_mem_op *op) { if (op->cmd.opcode == SPINOR_OP_BE_4K || op->cmd.opcode == SPINOR_OP_CHIP_ERASE || diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 6e10b629a3c..bb5893d56db 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -68,6 +68,14 @@ config USB_XHCI_MVEBU SoCs, which includes Armada8K, Armada3700 and other Armada family SoCs. +config USB_XHCI_GENERIC + bool "Generic SoC USB 3.0 support" + depends on OF_CONTROL + default n + help + Choose this option to add support for USB 3.0 driver for SoCs + that do not need platform specific code, like on emulated targets. + config USB_XHCI_OCTEON bool "Support for Marvell Octeon family on-chip xHCI USB controller" depends on ARCH_OCTEON diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 792956e647a..301bb9fdee1 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_USB_XHCI_EXYNOS) += xhci-exynos5.o obj-$(CONFIG_USB_XHCI_FSL) += xhci-fsl.o obj-$(CONFIG_USB_XHCI_MTK) += xhci-mtk.o obj-$(CONFIG_USB_XHCI_MVEBU) += xhci-mvebu.o +obj-$(CONFIG_USB_XHCI_GENERIC) += xhci-generic.o obj-$(CONFIG_USB_XHCI_OMAP) += xhci-omap.o obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o obj-$(CONFIG_USB_XHCI_RCAR) += xhci-rcar.o diff --git a/drivers/usb/host/xhci-generic.c b/drivers/usb/host/xhci-generic.c new file mode 100644 index 00000000000..355d4883176 --- /dev/null +++ b/drivers/usb/host/xhci-generic.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2024 9elements GmbH + * + * GENERIC USB HOST xHCI Controller + */ +#include <dm.h> +#include <fdtdec.h> +#include <log.h> +#include <usb.h> +#include <asm/io.h> +#include <dm/device_compat.h> +#include <usb/xhci.h> + +struct generic_xhci_plat { + fdt_addr_t hcd_base; +}; + +/** + * Contains pointers to register base addresses + * for the usb controller. + */ +struct generic_xhci { + struct xhci_ctrl ctrl; /* Needs to come first in this struct! */ + struct usb_plat usb_plat; + struct xhci_hccr *hcd; +}; + +static int xhci_usb_probe(struct udevice *dev) +{ + struct generic_xhci_plat *plat = dev_get_plat(dev); + struct generic_xhci *ctx = dev_get_priv(dev); + struct xhci_hcor *hcor; + int len; + + ctx->hcd = (struct xhci_hccr *)phys_to_virt(plat->hcd_base); + len = HC_LENGTH(xhci_readl(&ctx->hcd->cr_capbase)); + hcor = (struct xhci_hcor *)((uintptr_t)ctx->hcd + len); + + return xhci_register(dev, ctx->hcd, hcor); +} + +static int xhci_usb_of_to_plat(struct udevice *dev) +{ + struct generic_xhci_plat *plat = dev_get_plat(dev); + + /* + * Get the base address for XHCI controller from the device node + */ + plat->hcd_base = dev_read_addr(dev); + if (plat->hcd_base == FDT_ADDR_T_NONE) { + dev_dbg(dev, "Can't get the XHCI register base address\n"); + return -ENXIO; + } + + return 0; +} + +static const struct udevice_id xhci_usb_ids[] = { + { .compatible = "generic-xhci" }, + { } +}; + +U_BOOT_DRIVER(usb_xhci) = { + .name = "xhci_generic", + .id = UCLASS_USB, + .of_match = xhci_usb_ids, + .of_to_plat = xhci_usb_of_to_plat, + .probe = xhci_usb_probe, + .remove = xhci_deregister, + .ops = &xhci_usb_ops, + .plat_auto = sizeof(struct generic_xhci_plat), + .priv_auto = sizeof(struct generic_xhci), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 90bc5653ee3..0e45f0a0922 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -175,6 +175,13 @@ config WDT_DA9063 help Enable support for the watchdog timer in Dialog DA9063. +config WDT_DAVINCI + bool "DaVinci watchdog timer support" + depends on WDT + help + Select this to enable the watchdog timer for DaVinci SoCs such as the + OMAP-L138. + config WDT_GPIO bool "External gpio watchdog support" depends on WDT @@ -184,6 +191,15 @@ config WDT_GPIO doc/device-tree-bindings/watchdog/gpio-wdt.txt for information on how to describe the watchdog in device tree. +config SPL_WDT_GPIO + bool "External gpio watchdog support in SPL" + depends on SPL_WDT + depends on SPL_DM_GPIO + depends on SPL_OF_REAL + default WDT_GPIO + help + Support for external watchdog fed by toggling a gpio in SPL. + config WDT_MAX6370 bool "MAX6370 watchdog timer support" depends on WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 51be6ab9abe..0b107c008f7 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -30,8 +30,9 @@ obj-$(CONFIG_WDT_CORTINA) += cortina_wdt.o obj-$(CONFIG_WDT_ORION) += orion_wdt.o obj-$(CONFIG_WDT_CDNS) += cdns_wdt.o obj-$(CONFIG_WDT_DA9063) += da9063-wdt.o +obj-$(CONFIG_WDT_DAVINCI) += davinci_wdt.o obj-$(CONFIG_WDT_FTWDT010) += ftwdt010_wdt.o -obj-$(CONFIG_WDT_GPIO) += gpio_wdt.o +obj-$(CONFIG_$(SPL_TPL_)WDT_GPIO) += gpio_wdt.o obj-$(CONFIG_WDT_MAX6370) += max6370_wdt.o obj-$(CONFIG_WDT_MCF) += mcf_wdt.o obj-$(CONFIG_WDT_MESON_GXBB) += meson_gxbb_wdt.o diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c new file mode 100644 index 00000000000..fa8d7842e94 --- /dev/null +++ b/drivers/watchdog/davinci_wdt.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * DaVinci Watchdog driver + * + */ + +#include <asm/io.h> +#include <clk.h> +#include <dm.h> +#include <wdt.h> + +/* Control Register */ +#define DAVINCI_WDT_ID 0x00 +#define DAVINCI_WDT_TIM12 0x10 +#define DAVINCI_WDT_TIM34 0x14 +#define DAVINCI_WDT_PRD12 0x18 +#define DAVINCI_WDT_PRD34 0x1C +#define DAVINCI_WDT_TCR 0x20 +#define DAVINCI_WDT_TGCR 0x24 +#define DAVINCI_WDT_WDTCR 0x28 + +#define DAVINCI_TCR_CONT_EN BIT(7) + +#define DAVINCI_TGCR_PLUSEN BIT(4) +#define DAVINCI_TGCR_WDT_MODE BIT(3) +#define DAVINCI_TGCR_TIM34RS BIT(1) +#define DAVINCI_TGCR_TIM12RS BIT(0) + +#define DAVINCI_WDTCR_INVALID_KEY (0x5555 << 16) +#define DAVINCI_WDTCR_WDKEY0 (0xA5C6 << 16) +#define DAVINCI_WDTCR_WDKEY1 (0xDA7E << 16) +#define DAVINCI_WDTCR_WDFLAG BIT(15) +#define DAVINCI_WDTCR_WDEN BIT(14) + +#define DEFAULT_THRESHOLD 0xA03200000 + +struct davinci_wdt_priv { + void __iomem *base; + struct clk *ref_clk; +}; + +static int davinci_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags) +{ + struct davinci_wdt_priv *priv = dev_get_priv(dev); + ulong rate = clk_get_rate(priv->ref_clk); + u64 threshold; + + if (!rate) + threshold = DEFAULT_THRESHOLD; + else + threshold = rate * timeout_ms / 1000; + + /* Reset control registers */ + writel(0, priv->base + DAVINCI_WDT_TCR); + writel(0, priv->base + DAVINCI_WDT_TGCR); + + /* Enable watchdog mode and timers */ + writel(DAVINCI_TGCR_WDT_MODE | DAVINCI_TGCR_TIM12RS | DAVINCI_TGCR_TIM34RS, + priv->base + DAVINCI_WDT_TGCR); + + /* Reset counters */ + writel(0, priv->base + DAVINCI_WDT_TIM12); + writel(0, priv->base + DAVINCI_WDT_TIM34); + + /* Set timeout threshold */ + writel(threshold & 0xFFFFFFFF, priv->base + DAVINCI_WDT_PRD12); + writel(threshold >> 32, priv->base + DAVINCI_WDT_PRD34); + + /* Enable counter */ + writel(DAVINCI_TCR_CONT_EN, priv->base + DAVINCI_WDT_TCR); + + /* Go to watchdog's active state */ + writel(DAVINCI_WDTCR_WDEN | DAVINCI_WDTCR_WDKEY0, priv->base + DAVINCI_WDT_WDTCR); + writel(DAVINCI_WDTCR_WDEN | DAVINCI_WDTCR_WDKEY1, priv->base + DAVINCI_WDT_WDTCR); + + return 0; +} + +static int davinci_wdt_expire_now(struct udevice *dev, ulong flags) +{ + struct davinci_wdt_priv *priv = dev_get_priv(dev); + + writel(DAVINCI_WDTCR_INVALID_KEY, priv->base + DAVINCI_WDT_WDTCR); + + return 0; +} + +static int davinci_wdt_restart(struct udevice *dev) +{ + struct davinci_wdt_priv *priv = dev_get_priv(dev); + + writel(DAVINCI_WDTCR_WDEN | DAVINCI_WDTCR_WDKEY0, priv->base + DAVINCI_WDT_WDTCR); + writel(DAVINCI_WDTCR_WDEN | DAVINCI_WDTCR_WDKEY1, priv->base + DAVINCI_WDT_WDTCR); + + return 0; +} + +static int davinci_wdt_probe(struct udevice *dev) +{ + struct davinci_wdt_priv *priv = dev_get_priv(dev); + + priv->base = dev_remap_addr_index(dev, 0); + if (!priv->base) + return -EFAULT; + + priv->ref_clk = devm_clk_get(dev, "ref"); + if (IS_ERR(priv->ref_clk)) + return PTR_ERR(priv->ref_clk); + + return 0; +} + +static const struct wdt_ops davinci_wdt_ops = { + .start = davinci_wdt_start, + .reset = davinci_wdt_restart, + .expire_now = davinci_wdt_expire_now, +}; + +static const struct udevice_id davinci_wdt_ids[] = { + {.compatible = "ti,davinci-wdt"}, + {} +}; + +U_BOOT_DRIVER(davinci_wdt) = { + .name = "davinci_wdt", + .id = UCLASS_WDT, + .probe = davinci_wdt_probe, + .of_match = davinci_wdt_ids, + .ops = &davinci_wdt_ops, + .priv_auto = sizeof(struct davinci_wdt_priv), +}; diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c index 2920c2c751f..e889861e917 100644 --- a/drivers/watchdog/gpio_wdt.c +++ b/drivers/watchdog/gpio_wdt.c @@ -45,14 +45,32 @@ static int gpio_wdt_start(struct udevice *dev, u64 timeout, ulong flags) if (priv->always_running) return 0; - return -ENOSYS; + dm_gpio_set_dir_flags(&priv->gpio, GPIOD_IS_OUT); + gpio_wdt_reset(dev); + + return 0; +} + +static int gpio_wdt_stop(struct udevice *dev) +{ + struct gpio_wdt_priv *priv = dev_get_priv(dev); + + if (priv->always_running) + return -EOPNOTSUPP; + + if (priv->hw_algo == HW_ALGO_TOGGLE) + dm_gpio_set_dir_flags(&priv->gpio, GPIOD_IS_IN); + else + dm_gpio_set_value(&priv->gpio, 1); + + return 0; } static int dm_probe(struct udevice *dev) { struct gpio_wdt_priv *priv = dev_get_priv(dev); - int ret; const char *algo = dev_read_string(dev, "hw_algo"); + int ret, flags; if (!algo) return -EINVAL; @@ -64,7 +82,9 @@ static int dm_probe(struct udevice *dev) return -EINVAL; priv->always_running = dev_read_bool(dev, "always-running"); - ret = gpio_request_by_name(dev, "gpios", 0, &priv->gpio, GPIOD_IS_OUT); + flags = priv->always_running || priv->hw_algo == HW_ALGO_LEVEL ? + GPIOD_IS_OUT : GPIOD_IS_IN; + ret = gpio_request_by_name(dev, "gpios", 0, &priv->gpio, flags); if (ret < 0) { dev_err(dev, "Request for wdt gpio failed: %d\n", ret); return ret; @@ -78,6 +98,7 @@ static int dm_probe(struct udevice *dev) static const struct wdt_ops gpio_wdt_ops = { .start = gpio_wdt_start, + .stop = gpio_wdt_stop, .reset = gpio_wdt_reset, }; |
